/*===- TableGen'erated file -------------------------------------*- C++ -*-===*\
|*                                                                            *|
|* Rewriters                                                                  *|
|*                                                                            *|
|* Automatically generated file, do not edit!                                 *|
|*                                                                            *|
\*===----------------------------------------------------------------------===*/

  if ((funcName == "__nv_frcp_rd" ||
      funcName == "__nv_frcp_rn" ||
      funcName == "__nv_frcp_ru" ||
      funcName == "__nv_frcp_rz" ||
      funcName == "__nv_drcp_rd" ||
      funcName == "__nv_drcp_rn" ||
      funcName == "__nv_drcp_ru" ||
      funcName == "__nv_drcp_rz") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FNeg
              auto fwdarg_0 = ({
                // Computing CheckedDiv
                auto fwdarg_1_0 = dif;
                auto fwdarg_1_1 = ({
                  // Computing FMul
                  llvm::Value* fwdarg_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_1_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                  Builder2.CreateFMul(fwdarg_1_2_0, fwdarg_1_2_1);
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = checkedDiv(gutils->strongZero, Builder2, (gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx), fwdarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FNeg
              auto fwdarg_0 = ({
                // Computing CheckedDiv
                auto fwdarg_1_0 = dif;
                auto fwdarg_1_1 = ({
                  // Computing FMul
                  llvm::Value* fwdarg_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_1_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                  Builder2.CreateFMul(fwdarg_1_2_0, fwdarg_1_2_1);
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = checkedDiv(gutils->strongZero, Builder2, (gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx), fwdarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FNeg
              auto revarg_0 = ({
                // Computing CheckedDiv
                auto revarg_1_0 = dif;
                auto revarg_1_1 = ({
                  // Computing FMul
                  llvm::Value* revarg_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  llvm::Value* revarg_1_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  Builder2.CreateFMul(revarg_1_2_0, revarg_1_2_1);
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = checkedDiv(gutils->strongZero, Builder2, (gutils->getWidth() == 1) ? revarg_1_0 : gutils->extractMeta(Builder2, revarg_1_0, idx), revarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "atan" ||
      funcName == "atanf" ||
      funcName == "atanl" ||
      funcName == "__fd_atan_1") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing CheckedDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FAdd
                auto fwdarg_2_0 = ({
                  // Computing FMul
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_2_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                  Builder2.CreateFMul(fwdarg_2_1_0, fwdarg_2_1_1);
                });
                auto fwdarg_2_1 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                Builder2.CreateFAdd(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = checkedDiv(gutils->strongZero, Builder2, (gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing CheckedDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FAdd
                auto fwdarg_2_0 = ({
                  // Computing FMul
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_2_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                  Builder2.CreateFMul(fwdarg_2_1_0, fwdarg_2_1_1);
                });
                auto fwdarg_2_1 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                Builder2.CreateFAdd(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = checkedDiv(gutils->strongZero, Builder2, (gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing CheckedDiv
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FAdd
                auto revarg_2_0 = ({
                  // Computing FMul
                  llvm::Value* revarg_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  llvm::Value* revarg_2_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  Builder2.CreateFMul(revarg_2_1_0, revarg_2_1_1);
                });
                auto revarg_2_1 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                Builder2.CreateFAdd(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = checkedDiv(gutils->strongZero, Builder2, (gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "atan2" ||
      funcName == "atan2f" ||
      funcName == "atan2l" ||
      funcName == "__nv_atan2" ||
      funcName == "__nv_atan2f" ||
      funcName == "__nv_atan2l") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = ({
                // Computing FMul
                auto fwdarg_1_0 = dif;
                llvm::Value* fwdarg_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx), fwdarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto fwdarg_1 = ({
                // Computing FAdd
                auto fwdarg_2_0 = ({
                  // Computing FMul
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(1));
                  llvm::Value* fwdarg_2_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                  Builder2.CreateFMul(fwdarg_2_1_0, fwdarg_2_1_1);
                });
                auto fwdarg_2_1 = ({
                  // Computing FMul
                  llvm::Value* fwdarg_2_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_2_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                  Builder2.CreateFMul(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFAdd(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FNeg
              auto fwdarg_0 = ({
                // Computing FDiv
                auto fwdarg_1_0 = ({
                  // Computing FMul
                  auto fwdarg_1_1_0 = dif;
                  llvm::Value* fwdarg_1_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                  Value *res = nullptr;
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_0, idx), fwdarg_1_1_1);
                    if (gutils->getWidth() == 1) res = V;
                    else {
                      if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                      res = Builder2.CreateInsertValue(res, V, {idx});
                    }
                  }
                  res;
                });
                auto fwdarg_1_1 = ({
                  // Computing FAdd
                  auto fwdarg_1_2_0 = ({
                    // Computing FMul
                    llvm::Value* fwdarg_1_2_1_0 = gutils->getNewFromOriginal(call.getOperand(1));
                    llvm::Value* fwdarg_1_2_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                    Builder2.CreateFMul(fwdarg_1_2_1_0, fwdarg_1_2_1_1);
                  });
                  auto fwdarg_1_2_1 = ({
                    // Computing FMul
                    llvm::Value* fwdarg_1_2_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    llvm::Value* fwdarg_1_2_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    Builder2.CreateFMul(fwdarg_1_2_2_0, fwdarg_1_2_2_1);
                  });
                  Builder2.CreateFAdd(fwdarg_1_2_0, fwdarg_1_2_1);
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx), fwdarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = ({
                // Computing FMul
                auto fwdarg_1_0 = dif;
                llvm::Value* fwdarg_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx), fwdarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto fwdarg_1 = ({
                // Computing FAdd
                auto fwdarg_2_0 = ({
                  // Computing FMul
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(1));
                  llvm::Value* fwdarg_2_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                  Builder2.CreateFMul(fwdarg_2_1_0, fwdarg_2_1_1);
                });
                auto fwdarg_2_1 = ({
                  // Computing FMul
                  llvm::Value* fwdarg_2_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_2_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                  Builder2.CreateFMul(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFAdd(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(1))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FNeg
              auto fwdarg_0 = ({
                // Computing FDiv
                auto fwdarg_1_0 = ({
                  // Computing FMul
                  auto fwdarg_1_1_0 = dif;
                  llvm::Value* fwdarg_1_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                  Value *res = nullptr;
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_0, idx), fwdarg_1_1_1);
                    if (gutils->getWidth() == 1) res = V;
                    else {
                      if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                      res = Builder2.CreateInsertValue(res, V, {idx});
                    }
                  }
                  res;
                });
                auto fwdarg_1_1 = ({
                  // Computing FAdd
                  auto fwdarg_1_2_0 = ({
                    // Computing FMul
                    llvm::Value* fwdarg_1_2_1_0 = gutils->getNewFromOriginal(call.getOperand(1));
                    llvm::Value* fwdarg_1_2_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                    Builder2.CreateFMul(fwdarg_1_2_1_0, fwdarg_1_2_1_1);
                  });
                  auto fwdarg_1_2_1 = ({
                    // Computing FMul
                    llvm::Value* fwdarg_1_2_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    llvm::Value* fwdarg_1_2_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    Builder2.CreateFMul(fwdarg_1_2_2_0, fwdarg_1_2_2_1);
                  });
                  Builder2.CreateFAdd(fwdarg_1_2_0, fwdarg_1_2_1);
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx), fwdarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        } else if (!dif && !gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FDiv
              auto revarg_0 = ({
                // Computing FMul
                auto revarg_1_0 = dif;
                llvm::Value* revarg_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_0 : gutils->extractMeta(Builder2, revarg_1_0, idx), revarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto revarg_1 = ({
                // Computing FAdd
                auto revarg_2_0 = ({
                  // Computing FMul
                  llvm::Value* revarg_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                  llvm::Value* revarg_2_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                  Builder2.CreateFMul(revarg_2_1_0, revarg_2_1_1);
                });
                auto revarg_2_1 = ({
                  // Computing FMul
                  llvm::Value* revarg_2_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  llvm::Value* revarg_2_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  Builder2.CreateFMul(revarg_2_2_0, revarg_2_2_1);
                });
                Builder2.CreateFAdd(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        if (!gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FNeg
              auto revarg_0 = ({
                // Computing FDiv
                auto revarg_1_0 = ({
                  // Computing FMul
                  auto revarg_1_1_0 = dif;
                  llvm::Value* revarg_1_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  Value *res = nullptr;
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_0 : gutils->extractMeta(Builder2, revarg_1_1_0, idx), revarg_1_1_1);
                    if (gutils->getWidth() == 1) res = V;
                    else {
                      if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                      res = Builder2.CreateInsertValue(res, V, {idx});
                    }
                  }
                  res;
                });
                auto revarg_1_1 = ({
                  // Computing FAdd
                  auto revarg_1_2_0 = ({
                    // Computing FMul
                    llvm::Value* revarg_1_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                    llvm::Value* revarg_1_2_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                    Builder2.CreateFMul(revarg_1_2_1_0, revarg_1_2_1_1);
                  });
                  auto revarg_1_2_1 = ({
                    // Computing FMul
                    llvm::Value* revarg_1_2_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    llvm::Value* revarg_1_2_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    Builder2.CreateFMul(revarg_1_2_2_0, revarg_1_2_2_1);
                  });
                  Builder2.CreateFAdd(revarg_1_2_0, revarg_1_2_1);
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_1_0 : gutils->extractMeta(Builder2, revarg_1_0, idx), revarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(1)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(1), toadd, Builder2, call.getOperand(1)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "cbrt" ||
      funcName == "cbrtf" ||
      funcName == "cbrtl") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = ({
                // Computing FMul
                auto fwdarg_1_0 = dif;
                auto fwdarg_1_1 = ({
                  // Computing Call
                  llvm::Value* fwdarg_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_1_2_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx), fwdarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "3.0");
                llvm::Value* fwdarg_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = ({
                // Computing FMul
                auto fwdarg_1_0 = dif;
                auto fwdarg_1_1 = ({
                  // Computing Call
                  llvm::Value* fwdarg_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_1_2_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx), fwdarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "3.0");
                llvm::Value* fwdarg_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FDiv
              auto revarg_0 = ({
                // Computing FMul
                auto revarg_1_0 = dif;
                auto revarg_1_1 = ({
                  // Computing Call
                  llvm::Value* revarg_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_1_2_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_0 : gutils->extractMeta(Builder2, revarg_1_0, idx), revarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto revarg_1 = ({
                // Computing FMul
                auto revarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "3.0");
                llvm::Value* revarg_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                Builder2.CreateFMul(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "hypot" ||
      funcName == "hypotf" ||
      funcName == "hypotl" ||
      funcName == "__hypot_finite") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = ({
                // Computing FMul
                auto fwdarg_1_0 = dif;
                llvm::Value* fwdarg_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx), fwdarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                llvm::Value* fwdarg_2_1 = gutils->getNewFromOriginal(call.getOperand(1));
                auto callval = cast<CallInst>(&call)->getCalledOperand();
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0, fwdarg_2_1})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = ({
                // Computing FMul
                auto fwdarg_1_0 = dif;
                llvm::Value* fwdarg_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx), fwdarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                llvm::Value* fwdarg_2_1 = gutils->getNewFromOriginal(call.getOperand(1));
                auto callval = cast<CallInst>(&call)->getCalledOperand();
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0, fwdarg_2_1})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = ({
                // Computing FMul
                auto fwdarg_1_0 = dif;
                llvm::Value* fwdarg_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx), fwdarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                llvm::Value* fwdarg_2_1 = gutils->getNewFromOriginal(call.getOperand(1));
                auto callval = cast<CallInst>(&call)->getCalledOperand();
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0, fwdarg_2_1})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(1))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = ({
                // Computing FMul
                auto fwdarg_1_0 = dif;
                llvm::Value* fwdarg_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx), fwdarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                llvm::Value* fwdarg_2_1 = gutils->getNewFromOriginal(call.getOperand(1));
                auto callval = cast<CallInst>(&call)->getCalledOperand();
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0, fwdarg_2_1})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        } else if (!dif && !gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FDiv
              auto revarg_0 = ({
                // Computing FMul
                auto revarg_1_0 = dif;
                llvm::Value* revarg_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_0 : gutils->extractMeta(Builder2, revarg_1_0, idx), revarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto revarg_1 = ({
                // Computing Call
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                llvm::Value* revarg_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                auto callval = cast<CallInst>(&call)->getCalledOperand();
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_0, revarg_2_1})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        if (!gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FDiv
              auto revarg_0 = ({
                // Computing FMul
                auto revarg_1_0 = dif;
                llvm::Value* revarg_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_0 : gutils->extractMeta(Builder2, revarg_1_0, idx), revarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto revarg_1 = ({
                // Computing Call
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                llvm::Value* revarg_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                auto callval = cast<CallInst>(&call)->getCalledOperand();
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_0, revarg_2_1})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(1)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(1), toadd, Builder2, call.getOperand(1)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "tanh") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_c = nullptr;
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FSub
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                auto fwdarg_2_1 = ({
                  // Computing FMul
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_c = fwdarg_2_2_0;
                  auto fwdarg_2_2_1 = local_c;
                  Builder2.CreateFMul(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFSub(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_c = nullptr;
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FSub
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                auto fwdarg_2_1 = ({
                  // Computing FMul
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_c = fwdarg_2_2_0;
                  auto fwdarg_2_2_1 = local_c;
                  Builder2.CreateFMul(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFSub(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          llvm::Value *local_c = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FSub
                auto revarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                auto revarg_2_1 = ({
                  // Computing FMul
                  auto revarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* revarg_2_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_c = revarg_2_2_0;
                  auto revarg_2_2_1 = local_c;
                  Builder2.CreateFMul(revarg_2_2_0, revarg_2_2_1);
                });
                Builder2.CreateFSub(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "tanhf") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_c = nullptr;
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FSub
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                auto fwdarg_2_1 = ({
                  // Computing FMul
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_c = fwdarg_2_2_0;
                  auto fwdarg_2_2_1 = local_c;
                  Builder2.CreateFMul(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFSub(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_c = nullptr;
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FSub
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                auto fwdarg_2_1 = ({
                  // Computing FMul
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_c = fwdarg_2_2_0;
                  auto fwdarg_2_2_1 = local_c;
                  Builder2.CreateFMul(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFSub(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          llvm::Value *local_c = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FSub
                auto revarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                auto revarg_2_1 = ({
                  // Computing FMul
                  auto revarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* revarg_2_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_c = revarg_2_2_0;
                  auto revarg_2_2_1 = local_c;
                  Builder2.CreateFMul(revarg_2_2_0, revarg_2_2_1);
                });
                Builder2.CreateFSub(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "tanhl") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_c = nullptr;
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FSub
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                auto fwdarg_2_1 = ({
                  // Computing FMul
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_c = fwdarg_2_2_0;
                  auto fwdarg_2_2_1 = local_c;
                  Builder2.CreateFMul(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFSub(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_c = nullptr;
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FSub
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                auto fwdarg_2_1 = ({
                  // Computing FMul
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_c = fwdarg_2_2_0;
                  auto fwdarg_2_2_1 = local_c;
                  Builder2.CreateFMul(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFSub(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          llvm::Value *local_c = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FSub
                auto revarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                auto revarg_2_1 = ({
                  // Computing FMul
                  auto revarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* revarg_2_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_c = revarg_2_2_0;
                  auto revarg_2_2_1 = local_c;
                  Builder2.CreateFMul(revarg_2_2_0, revarg_2_2_1);
                });
                Builder2.CreateFSub(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "cosh") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("sinh", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("sinh", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing Call
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("sinh", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "coshf") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("sinhf", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("sinhf", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing Call
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("sinhf", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "coshl") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("sinhl", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("sinhl", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing Call
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("sinhl", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "sinh") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("cosh", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("cosh", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing Call
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("cosh", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "sinhf") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("coshf", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("coshf", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing Call
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("coshf", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "sinhl") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("coshl", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("coshl", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing Call
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("coshl", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "air.tanh.f32") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_c = nullptr;
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.cosh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                local_c = fwdarg_2_0;
                auto fwdarg_2_1 = local_c;
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_c = nullptr;
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.cosh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                local_c = fwdarg_2_0;
                auto fwdarg_2_1 = local_c;
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          llvm::Value *local_c = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FDiv
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FMul
                auto revarg_2_0 = ({
                  // Computing Call
                  llvm::Value* revarg_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.cosh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                local_c = revarg_2_0;
                auto revarg_2_1 = local_c;
                Builder2.CreateFMul(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "air.tanh.f64") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_c = nullptr;
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.cosh.f64", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                local_c = fwdarg_2_0;
                auto fwdarg_2_1 = local_c;
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_c = nullptr;
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.cosh.f64", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                local_c = fwdarg_2_0;
                auto fwdarg_2_1 = local_c;
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          llvm::Value *local_c = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FDiv
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FMul
                auto revarg_2_0 = ({
                  // Computing Call
                  llvm::Value* revarg_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.cosh.f64", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                local_c = revarg_2_0;
                auto revarg_2_1 = local_c;
                Builder2.CreateFMul(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "air.cosh.f32") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.sinh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.sinh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing Call
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.sinh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "air.cosh.f64") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.sinh.f64", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.sinh.f64", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing Call
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.sinh.f64", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "air.sinh.f32") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.cosh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.cosh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing Call
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.cosh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "air.sinh.f64") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.cosh.f64", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.cosh.f64", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing Call
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.cosh.f64", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "air.fast_tanh.f32") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_c = nullptr;
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.fast_cosh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                local_c = fwdarg_2_0;
                auto fwdarg_2_1 = local_c;
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_c = nullptr;
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.fast_cosh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                local_c = fwdarg_2_0;
                auto fwdarg_2_1 = local_c;
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          llvm::Value *local_c = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FDiv
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FMul
                auto revarg_2_0 = ({
                  // Computing Call
                  llvm::Value* revarg_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.fast_cosh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                local_c = revarg_2_0;
                auto revarg_2_1 = local_c;
                Builder2.CreateFMul(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "air.fast_cosh.f32") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.fast_sinh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.fast_sinh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing Call
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.fast_sinh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "air.fast_sinh.f32") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.fast_cosh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.fast_cosh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing Call
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("air.fast_cosh.f32", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "asinh" ||
      funcName == "asinhf" ||
      funcName == "asinhl" ||
      funcName == "__nv_asinh" ||
      funcName == "__nv_asinhf") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing sqrt
                auto fwdarg_2_0 = ({
                  // Computing FAdd
                  auto fwdarg_2_1_0 = ({
                    // Computing FMul
                    llvm::Value* fwdarg_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    llvm::Value* fwdarg_2_1_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    Builder2.CreateFMul(fwdarg_2_1_1_0, fwdarg_2_1_1_1);
                  });
                  auto fwdarg_2_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                  Builder2.CreateFAdd(fwdarg_2_1_0, fwdarg_2_1_1);
                });
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sqrt, std::vector<Type*>({fwdarg_2_0->getType()})), ArrayRef<Value*>({fwdarg_2_0}));
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing sqrt
                auto fwdarg_2_0 = ({
                  // Computing FAdd
                  auto fwdarg_2_1_0 = ({
                    // Computing FMul
                    llvm::Value* fwdarg_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    llvm::Value* fwdarg_2_1_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    Builder2.CreateFMul(fwdarg_2_1_1_0, fwdarg_2_1_1_1);
                  });
                  auto fwdarg_2_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                  Builder2.CreateFAdd(fwdarg_2_1_0, fwdarg_2_1_1);
                });
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sqrt, std::vector<Type*>({fwdarg_2_0->getType()})), ArrayRef<Value*>({fwdarg_2_0}));
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FDiv
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing sqrt
                auto revarg_2_0 = ({
                  // Computing FAdd
                  auto revarg_2_1_0 = ({
                    // Computing FMul
                    llvm::Value* revarg_2_1_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    llvm::Value* revarg_2_1_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    Builder2.CreateFMul(revarg_2_1_1_0, revarg_2_1_1_1);
                  });
                  auto revarg_2_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                  Builder2.CreateFAdd(revarg_2_1_0, revarg_2_1_1);
                });
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sqrt, std::vector<Type*>({revarg_2_0->getType()})), ArrayRef<Value*>({revarg_2_0}));
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "acosh" ||
      funcName == "acoshf" ||
      funcName == "acoshl" ||
      funcName == "__nv_acosh" ||
      funcName == "__nv_acoshf") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing sqrt
                auto fwdarg_2_0 = ({
                  // Computing FSub
                  auto fwdarg_2_1_0 = ({
                    // Computing FMul
                    llvm::Value* fwdarg_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    llvm::Value* fwdarg_2_1_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    Builder2.CreateFMul(fwdarg_2_1_1_0, fwdarg_2_1_1_1);
                  });
                  auto fwdarg_2_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                  Builder2.CreateFSub(fwdarg_2_1_0, fwdarg_2_1_1);
                });
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sqrt, std::vector<Type*>({fwdarg_2_0->getType()})), ArrayRef<Value*>({fwdarg_2_0}));
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing sqrt
                auto fwdarg_2_0 = ({
                  // Computing FSub
                  auto fwdarg_2_1_0 = ({
                    // Computing FMul
                    llvm::Value* fwdarg_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    llvm::Value* fwdarg_2_1_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    Builder2.CreateFMul(fwdarg_2_1_1_0, fwdarg_2_1_1_1);
                  });
                  auto fwdarg_2_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                  Builder2.CreateFSub(fwdarg_2_1_0, fwdarg_2_1_1);
                });
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sqrt, std::vector<Type*>({fwdarg_2_0->getType()})), ArrayRef<Value*>({fwdarg_2_0}));
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FDiv
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing sqrt
                auto revarg_2_0 = ({
                  // Computing FSub
                  auto revarg_2_1_0 = ({
                    // Computing FMul
                    llvm::Value* revarg_2_1_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    llvm::Value* revarg_2_1_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    Builder2.CreateFMul(revarg_2_1_1_0, revarg_2_1_1_1);
                  });
                  auto revarg_2_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                  Builder2.CreateFSub(revarg_2_1_0, revarg_2_1_1);
                });
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sqrt, std::vector<Type*>({revarg_2_0->getType()})), ArrayRef<Value*>({revarg_2_0}));
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "atanh" ||
      funcName == "atanhf" ||
      funcName == "atanhl" ||
      funcName == "__nv_atanh" ||
      funcName == "__nv_atanhf") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FSub
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                auto fwdarg_2_1 = ({
                  // Computing FMul
                  llvm::Value* fwdarg_2_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_2_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                  Builder2.CreateFMul(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFSub(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FSub
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                auto fwdarg_2_1 = ({
                  // Computing FMul
                  llvm::Value* fwdarg_2_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_2_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                  Builder2.CreateFMul(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFSub(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FDiv
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FSub
                auto revarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                auto revarg_2_1 = ({
                  // Computing FMul
                  llvm::Value* revarg_2_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  llvm::Value* revarg_2_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  Builder2.CreateFMul(revarg_2_2_0, revarg_2_2_1);
                });
                Builder2.CreateFSub(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "__nv_fast_expf") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto callval = cast<CallInst>(&call)->getCalledOperand();
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing Call
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto callval = cast<CallInst>(&call)->getCalledOperand();
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing Call
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto callval = cast<CallInst>(&call)->getCalledOperand();
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "exp10") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing FMul
                auto fwdarg_1_0 = dif;
                auto fwdarg_1_1 = ({
                  // Computing Call
                  llvm::Value* fwdarg_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_1_2_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx), fwdarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto fwdarg_1 = ConstantFP::get(call.getOperand(0)->getType(), "2.30258509299404568401799145468");
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing FMul
                auto fwdarg_1_0 = dif;
                auto fwdarg_1_1 = ({
                  // Computing Call
                  llvm::Value* fwdarg_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_1_2_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx), fwdarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto fwdarg_1 = ConstantFP::get(call.getOperand(0)->getType(), "2.30258509299404568401799145468");
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = ({
                // Computing FMul
                auto revarg_1_0 = dif;
                auto revarg_1_1 = ({
                  // Computing Call
                  llvm::Value* revarg_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_1_2_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_0 : gutils->extractMeta(Builder2, revarg_1_0, idx), revarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto revarg_1 = ConstantFP::get(call.getOperand(0)->getType(), "2.30258509299404568401799145468");
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "tan" ||
      funcName == "tanf" ||
      funcName == "tanl") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_c = nullptr;
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FAdd
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                auto fwdarg_2_1 = ({
                  // Computing FMul
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_c = fwdarg_2_2_0;
                  auto fwdarg_2_2_1 = local_c;
                  Builder2.CreateFMul(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFAdd(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_c = nullptr;
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FAdd
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                auto fwdarg_2_1 = ({
                  // Computing FMul
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_c = fwdarg_2_2_0;
                  auto fwdarg_2_2_1 = local_c;
                  Builder2.CreateFMul(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFAdd(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          llvm::Value *local_c = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FAdd
                auto revarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                auto revarg_2_1 = ({
                  // Computing FMul
                  auto revarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* revarg_2_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_c = revarg_2_2_0;
                  auto revarg_2_2_1 = local_c;
                  Builder2.CreateFMul(revarg_2_2_0, revarg_2_2_1);
                });
                Builder2.CreateFAdd(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "remainder") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = dif;
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing FNeg
                auto fwdarg_1_0 = dif;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto fwdarg_1 = ({
                // Computing round
                auto fwdarg_2_0 = ({
                  // Computing FDiv
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_2_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                  Builder2.CreateFDiv(fwdarg_2_1_0, fwdarg_2_1_1);
                });
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::round, std::vector<Type*>({fwdarg_2_0->getType()})), ArrayRef<Value*>({fwdarg_2_0}));
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = dif;
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(1))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing FNeg
                auto fwdarg_1_0 = dif;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto fwdarg_1 = ({
                // Computing round
                auto fwdarg_2_0 = ({
                  // Computing FDiv
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_2_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                  Builder2.CreateFDiv(fwdarg_2_1_0, fwdarg_2_1_1);
                });
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::round, std::vector<Type*>({fwdarg_2_0->getType()})), ArrayRef<Value*>({fwdarg_2_0}));
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        } else if (!dif && !gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = dif;
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        if (!gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = ({
                // Computing FNeg
                auto revarg_1_0 = dif;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_1_0 : gutils->extractMeta(Builder2, revarg_1_0, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto revarg_1 = ({
                // Computing round
                auto revarg_2_0 = ({
                  // Computing FDiv
                  llvm::Value* revarg_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  llvm::Value* revarg_2_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                  Builder2.CreateFDiv(revarg_2_1_0, revarg_2_1_1);
                });
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::round, std::vector<Type*>({revarg_2_0->getType()})), ArrayRef<Value*>({revarg_2_0}));
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(1)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(1), toadd, Builder2, call.getOperand(1)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "asin" ||
      funcName == "asinf" ||
      funcName == "asinl" ||
      funcName == "__nv_asin" ||
      funcName == "__nv_asinf" ||
      funcName == "__nv_asinl") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing sqrt
                auto fwdarg_2_0 = ({
                  // Computing FSub
                  auto fwdarg_2_1_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                  auto fwdarg_2_1_1 = ({
                    // Computing FMul
                    llvm::Value* fwdarg_2_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    llvm::Value* fwdarg_2_1_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    Builder2.CreateFMul(fwdarg_2_1_2_0, fwdarg_2_1_2_1);
                  });
                  Builder2.CreateFSub(fwdarg_2_1_0, fwdarg_2_1_1);
                });
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sqrt, std::vector<Type*>({fwdarg_2_0->getType()})), ArrayRef<Value*>({fwdarg_2_0}));
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing sqrt
                auto fwdarg_2_0 = ({
                  // Computing FSub
                  auto fwdarg_2_1_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                  auto fwdarg_2_1_1 = ({
                    // Computing FMul
                    llvm::Value* fwdarg_2_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    llvm::Value* fwdarg_2_1_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    Builder2.CreateFMul(fwdarg_2_1_2_0, fwdarg_2_1_2_1);
                  });
                  Builder2.CreateFSub(fwdarg_2_1_0, fwdarg_2_1_1);
                });
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sqrt, std::vector<Type*>({fwdarg_2_0->getType()})), ArrayRef<Value*>({fwdarg_2_0}));
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FDiv
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing sqrt
                auto revarg_2_0 = ({
                  // Computing FSub
                  auto revarg_2_1_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                  auto revarg_2_1_1 = ({
                    // Computing FMul
                    llvm::Value* revarg_2_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    llvm::Value* revarg_2_1_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    Builder2.CreateFMul(revarg_2_1_2_0, revarg_2_1_2_1);
                  });
                  Builder2.CreateFSub(revarg_2_1_0, revarg_2_1_1);
                });
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sqrt, std::vector<Type*>({revarg_2_0->getType()})), ArrayRef<Value*>({revarg_2_0}));
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "acos" ||
      funcName == "acosf" ||
      funcName == "acosl" ||
      funcName == "__nv_acos" ||
      funcName == "__nv_acosf" ||
      funcName == "__nv_acosl") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FNeg
              auto fwdarg_0 = ({
                // Computing FDiv
                auto fwdarg_1_0 = dif;
                auto fwdarg_1_1 = ({
                  // Computing sqrt
                  auto fwdarg_1_2_0 = ({
                    // Computing FSub
                    auto fwdarg_1_2_1_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                    auto fwdarg_1_2_1_1 = ({
                      // Computing FMul
                      llvm::Value* fwdarg_1_2_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      llvm::Value* fwdarg_1_2_1_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                      Builder2.CreateFMul(fwdarg_1_2_1_2_0, fwdarg_1_2_1_2_1);
                    });
                    Builder2.CreateFSub(fwdarg_1_2_1_0, fwdarg_1_2_1_1);
                  });
                  Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sqrt, std::vector<Type*>({fwdarg_1_2_0->getType()})), ArrayRef<Value*>({fwdarg_1_2_0}));
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx), fwdarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FNeg
              auto fwdarg_0 = ({
                // Computing FDiv
                auto fwdarg_1_0 = dif;
                auto fwdarg_1_1 = ({
                  // Computing sqrt
                  auto fwdarg_1_2_0 = ({
                    // Computing FSub
                    auto fwdarg_1_2_1_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                    auto fwdarg_1_2_1_1 = ({
                      // Computing FMul
                      llvm::Value* fwdarg_1_2_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      llvm::Value* fwdarg_1_2_1_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                      Builder2.CreateFMul(fwdarg_1_2_1_2_0, fwdarg_1_2_1_2_1);
                    });
                    Builder2.CreateFSub(fwdarg_1_2_1_0, fwdarg_1_2_1_1);
                  });
                  Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sqrt, std::vector<Type*>({fwdarg_1_2_0->getType()})), ArrayRef<Value*>({fwdarg_1_2_0}));
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_1_0 : gutils->extractMeta(Builder2, fwdarg_1_0, idx), fwdarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FNeg
              auto revarg_0 = ({
                // Computing FDiv
                auto revarg_1_0 = dif;
                auto revarg_1_1 = ({
                  // Computing sqrt
                  auto revarg_1_2_0 = ({
                    // Computing FSub
                    auto revarg_1_2_1_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                    auto revarg_1_2_1_1 = ({
                      // Computing FMul
                      llvm::Value* revarg_1_2_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                      llvm::Value* revarg_1_2_1_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                      Builder2.CreateFMul(revarg_1_2_1_2_0, revarg_1_2_1_2_1);
                    });
                    Builder2.CreateFSub(revarg_1_2_1_0, revarg_1_2_1_1);
                  });
                  Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sqrt, std::vector<Type*>({revarg_1_2_0->getType()})), ArrayRef<Value*>({revarg_1_2_0}));
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_1_0 : gutils->extractMeta(Builder2, revarg_1_0, idx), revarg_1_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "log1p" ||
      funcName == "log1pf" ||
      funcName == "log1pl") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FAdd
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto fwdarg_2_1 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                Builder2.CreateFAdd(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FAdd
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto fwdarg_2_1 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                Builder2.CreateFAdd(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FDiv
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FAdd
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto revarg_2_1 = ConstantFP::get(call.getOperand(0)->getType(), "1.0");
                Builder2.CreateFAdd(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "expm1" ||
      funcName == "expm1f" ||
      funcName == "expm1l") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing exp
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({fwdarg_1_0->getType()})), ArrayRef<Value*>({fwdarg_1_0}));
              });
              auto fwdarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing exp
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({fwdarg_1_0->getType()})), ArrayRef<Value*>({fwdarg_1_0}));
              });
              auto fwdarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = ({
                // Computing exp
                llvm::Value* revarg_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({revarg_1_0->getType()})), ArrayRef<Value*>({revarg_1_0}));
              });
              auto revarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(revarg_0, (gutils->getWidth() == 1) ? revarg_1 : gutils->extractMeta(Builder2, revarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "jl_rem2pi" ||
      funcName == "jl_rem2pif" ||
      funcName == "jl_rem2pil") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = dif;
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = dif;
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = dif;
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "sinc" ||
      funcName == "sincf" ||
      funcName == "sincl") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing Select
              auto fwdarg_0 = ({
                // Computing FCmpOEQ
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto fwdarg_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
                Builder2.CreateFCmpOEQ(fwdarg_1_0, fwdarg_1_1);
              });
              auto fwdarg_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
              auto fwdarg_2 = ({
                // Computing FMul
                auto fwdarg_3_0 = dif;
                auto fwdarg_3_1 = ({
                  // Computing FDiv
                  auto fwdarg_3_2_0 = ({
                    // Computing FSub
                    auto fwdarg_3_2_1_0 = ({
                      // Computing cos
                      llvm::Value* fwdarg_3_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::cos, std::vector<Type*>({fwdarg_3_2_1_1_0->getType()})), ArrayRef<Value*>({fwdarg_3_2_1_1_0}));
                    });
                    auto fwdarg_3_2_1_1 = ({
                      // Computing Call
                      llvm::Value* fwdarg_3_2_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_3_2_1_2_0})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    Builder2.CreateFSub(fwdarg_3_2_1_0, fwdarg_3_2_1_1);
                  });
                  llvm::Value* fwdarg_3_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                  Builder2.CreateFDiv(fwdarg_3_2_0, fwdarg_3_2_1);
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_3_0 : gutils->extractMeta(Builder2, fwdarg_3_0, idx), fwdarg_3_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(fwdarg_0, fwdarg_1, (gutils->getWidth() == 1) ? fwdarg_2 : gutils->extractMeta(Builder2, fwdarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing Select
              auto fwdarg_0 = ({
                // Computing FCmpOEQ
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto fwdarg_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
                Builder2.CreateFCmpOEQ(fwdarg_1_0, fwdarg_1_1);
              });
              auto fwdarg_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
              auto fwdarg_2 = ({
                // Computing FMul
                auto fwdarg_3_0 = dif;
                auto fwdarg_3_1 = ({
                  // Computing FDiv
                  auto fwdarg_3_2_0 = ({
                    // Computing FSub
                    auto fwdarg_3_2_1_0 = ({
                      // Computing cos
                      llvm::Value* fwdarg_3_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::cos, std::vector<Type*>({fwdarg_3_2_1_1_0->getType()})), ArrayRef<Value*>({fwdarg_3_2_1_1_0}));
                    });
                    auto fwdarg_3_2_1_1 = ({
                      // Computing Call
                      llvm::Value* fwdarg_3_2_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_3_2_1_2_0})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    Builder2.CreateFSub(fwdarg_3_2_1_0, fwdarg_3_2_1_1);
                  });
                  llvm::Value* fwdarg_3_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                  Builder2.CreateFDiv(fwdarg_3_2_0, fwdarg_3_2_1);
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_3_0 : gutils->extractMeta(Builder2, fwdarg_3_0, idx), fwdarg_3_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(fwdarg_0, fwdarg_1, (gutils->getWidth() == 1) ? fwdarg_2 : gutils->extractMeta(Builder2, fwdarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing Select
              auto revarg_0 = ({
                // Computing FCmpOEQ
                llvm::Value* revarg_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto revarg_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
                Builder2.CreateFCmpOEQ(revarg_1_0, revarg_1_1);
              });
              auto revarg_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
              auto revarg_2 = ({
                // Computing FMul
                auto revarg_3_0 = dif;
                auto revarg_3_1 = ({
                  // Computing FDiv
                  auto revarg_3_2_0 = ({
                    // Computing FSub
                    auto revarg_3_2_1_0 = ({
                      // Computing cos
                      llvm::Value* revarg_3_2_1_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                      Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::cos, std::vector<Type*>({revarg_3_2_1_1_0->getType()})), ArrayRef<Value*>({revarg_3_2_1_1_0}));
                    });
                    auto revarg_3_2_1_1 = ({
                      // Computing Call
                      llvm::Value* revarg_3_2_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_3_2_1_2_0})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    Builder2.CreateFSub(revarg_3_2_1_0, revarg_3_2_1_1);
                  });
                  llvm::Value* revarg_3_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  Builder2.CreateFDiv(revarg_3_2_0, revarg_3_2_1);
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_3_0 : gutils->extractMeta(Builder2, revarg_3_0, idx), revarg_3_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(revarg_0, revarg_1, (gutils->getWidth() == 1) ? revarg_2 : gutils->extractMeta(Builder2, revarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "sincn" ||
      funcName == "sincnf" ||
      funcName == "sincnl") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing Select
              auto fwdarg_0 = ({
                // Computing FCmpOEQ
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto fwdarg_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
                Builder2.CreateFCmpOEQ(fwdarg_1_0, fwdarg_1_1);
              });
              auto fwdarg_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
              auto fwdarg_2 = ({
                // Computing FMul
                auto fwdarg_3_0 = dif;
                auto fwdarg_3_1 = ({
                  // Computing FDiv
                  auto fwdarg_3_2_0 = ({
                    // Computing FSub
                    auto fwdarg_3_2_1_0 = ({
                      // Computing cos
                      auto fwdarg_3_2_1_1_0 = ({
                        // Computing FMul
                        auto fwdarg_3_2_1_1_1_0 = ConstantFP::get(call.getOperand(0)->getType(), "3.1415926535897962684626433");
                        llvm::Value* fwdarg_3_2_1_1_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                        Builder2.CreateFMul(fwdarg_3_2_1_1_1_0, fwdarg_3_2_1_1_1_1);
                      });
                      Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::cos, std::vector<Type*>({fwdarg_3_2_1_1_0->getType()})), ArrayRef<Value*>({fwdarg_3_2_1_1_0}));
                    });
                    auto fwdarg_3_2_1_1 = ({
                      // Computing Call
                      llvm::Value* fwdarg_3_2_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_3_2_1_2_0})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    Builder2.CreateFSub(fwdarg_3_2_1_0, fwdarg_3_2_1_1);
                  });
                  llvm::Value* fwdarg_3_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                  Builder2.CreateFDiv(fwdarg_3_2_0, fwdarg_3_2_1);
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_3_0 : gutils->extractMeta(Builder2, fwdarg_3_0, idx), fwdarg_3_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(fwdarg_0, fwdarg_1, (gutils->getWidth() == 1) ? fwdarg_2 : gutils->extractMeta(Builder2, fwdarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing Select
              auto fwdarg_0 = ({
                // Computing FCmpOEQ
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto fwdarg_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
                Builder2.CreateFCmpOEQ(fwdarg_1_0, fwdarg_1_1);
              });
              auto fwdarg_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
              auto fwdarg_2 = ({
                // Computing FMul
                auto fwdarg_3_0 = dif;
                auto fwdarg_3_1 = ({
                  // Computing FDiv
                  auto fwdarg_3_2_0 = ({
                    // Computing FSub
                    auto fwdarg_3_2_1_0 = ({
                      // Computing cos
                      auto fwdarg_3_2_1_1_0 = ({
                        // Computing FMul
                        auto fwdarg_3_2_1_1_1_0 = ConstantFP::get(call.getOperand(0)->getType(), "3.1415926535897962684626433");
                        llvm::Value* fwdarg_3_2_1_1_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                        Builder2.CreateFMul(fwdarg_3_2_1_1_1_0, fwdarg_3_2_1_1_1_1);
                      });
                      Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::cos, std::vector<Type*>({fwdarg_3_2_1_1_0->getType()})), ArrayRef<Value*>({fwdarg_3_2_1_1_0}));
                    });
                    auto fwdarg_3_2_1_1 = ({
                      // Computing Call
                      llvm::Value* fwdarg_3_2_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_3_2_1_2_0})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    Builder2.CreateFSub(fwdarg_3_2_1_0, fwdarg_3_2_1_1);
                  });
                  llvm::Value* fwdarg_3_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                  Builder2.CreateFDiv(fwdarg_3_2_0, fwdarg_3_2_1);
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_3_0 : gutils->extractMeta(Builder2, fwdarg_3_0, idx), fwdarg_3_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(fwdarg_0, fwdarg_1, (gutils->getWidth() == 1) ? fwdarg_2 : gutils->extractMeta(Builder2, fwdarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing Select
              auto revarg_0 = ({
                // Computing FCmpOEQ
                llvm::Value* revarg_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto revarg_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
                Builder2.CreateFCmpOEQ(revarg_1_0, revarg_1_1);
              });
              auto revarg_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
              auto revarg_2 = ({
                // Computing FMul
                auto revarg_3_0 = dif;
                auto revarg_3_1 = ({
                  // Computing FDiv
                  auto revarg_3_2_0 = ({
                    // Computing FSub
                    auto revarg_3_2_1_0 = ({
                      // Computing cos
                      auto revarg_3_2_1_1_0 = ({
                        // Computing FMul
                        auto revarg_3_2_1_1_1_0 = ConstantFP::get(call.getOperand(0)->getType(), "3.1415926535897962684626433");
                        llvm::Value* revarg_3_2_1_1_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                        Builder2.CreateFMul(revarg_3_2_1_1_1_0, revarg_3_2_1_1_1_1);
                      });
                      Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::cos, std::vector<Type*>({revarg_3_2_1_1_0->getType()})), ArrayRef<Value*>({revarg_3_2_1_1_0}));
                    });
                    auto revarg_3_2_1_1 = ({
                      // Computing Call
                      llvm::Value* revarg_3_2_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_3_2_1_2_0})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    Builder2.CreateFSub(revarg_3_2_1_0, revarg_3_2_1_1);
                  });
                  llvm::Value* revarg_3_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  Builder2.CreateFDiv(revarg_3_2_0, revarg_3_2_1);
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_3_0 : gutils->extractMeta(Builder2, revarg_3_0, idx), revarg_3_1);
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(revarg_0, revarg_1, (gutils->getWidth() == 1) ? revarg_2 : gutils->extractMeta(Builder2, revarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "Faddeeva_erfcx_re") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "2.0");
                auto fwdarg_2_1 = ({
                  // Computing FSub
                  auto fwdarg_2_2_0 = ({
                    // Computing FMul
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto fwdarg_2_2_1_1 = ({
                      // Computing Call
                      llvm::Value* fwdarg_2_2_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_2_0})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    Builder2.CreateFMul(fwdarg_2_2_1_0, fwdarg_2_2_1_1);
                  });
                  auto fwdarg_2_2_1 = ConstantFP::get(call.getOperand(0)->getType(), "0.56418958354775628694807945156077258584405062932900");
                  Builder2.CreateFSub(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "2.0");
                auto fwdarg_2_1 = ({
                  // Computing FSub
                  auto fwdarg_2_2_0 = ({
                    // Computing FMul
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto fwdarg_2_2_1_1 = ({
                      // Computing Call
                      llvm::Value* fwdarg_2_2_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_2_0})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    Builder2.CreateFMul(fwdarg_2_2_1_0, fwdarg_2_2_1_1);
                  });
                  auto fwdarg_2_2_1 = ConstantFP::get(call.getOperand(0)->getType(), "0.56418958354775628694807945156077258584405062932900");
                  Builder2.CreateFSub(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FMul
                auto revarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "2.0");
                auto revarg_2_1 = ({
                  // Computing FSub
                  auto revarg_2_2_0 = ({
                    // Computing FMul
                    llvm::Value* revarg_2_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    auto revarg_2_2_1_1 = ({
                      // Computing Call
                      llvm::Value* revarg_2_2_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_1_2_0})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    Builder2.CreateFMul(revarg_2_2_1_0, revarg_2_2_1_1);
                  });
                  auto revarg_2_2_1 = ConstantFP::get(call.getOperand(0)->getType(), "0.56418958354775628694807945156077258584405062932900");
                  Builder2.CreateFSub(revarg_2_2_0, revarg_2_2_1);
                });
                Builder2.CreateFMul(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "fmod" ||
      funcName == "fmodf" ||
      funcName == "fmodl" ||
      funcName == "__nv_fmod" ||
      funcName == "__nv_fmodf" ||
      funcName == "__nv_fmodl") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = dif;
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_div = nullptr;
          {
            Value *itmp = ({
              // Computing CheckedMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FNeg
                auto fwdarg_2_0 = ({
                  // Computing copysign
                  auto fwdarg_2_1_0 = ({
                    // Computing floor
                    auto fwdarg_2_1_1_0 = ({
                      // Computing fabs
                      auto fwdarg_2_1_1_1_0 = ({
                        // Computing FDiv
                        llvm::Value* fwdarg_2_1_1_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                        llvm::Value* fwdarg_2_1_1_1_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                        Builder2.CreateFDiv(fwdarg_2_1_1_1_1_0, fwdarg_2_1_1_1_1_1);
                      });
                      local_div = fwdarg_2_1_1_1_0;
                      Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::fabs, std::vector<Type*>({fwdarg_2_1_1_1_0->getType()})), ArrayRef<Value*>({fwdarg_2_1_1_1_0}));
                    });
                    Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::floor, std::vector<Type*>({fwdarg_2_1_1_0->getType()})), ArrayRef<Value*>({fwdarg_2_1_1_0}));
                  });
                  auto fwdarg_2_1_1 = local_div;
                  Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::copysign, std::vector<Type*>({fwdarg_2_1_0->getType()})), ArrayRef<Value*>({fwdarg_2_1_0, fwdarg_2_1_1}));
                });
                Builder2.CreateFNeg(fwdarg_2_0);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = checkedMul(gutils->strongZero, Builder2, (gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = dif;
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(1))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_div = nullptr;
          {
            Value *itmp = ({
              // Computing CheckedMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FNeg
                auto fwdarg_2_0 = ({
                  // Computing copysign
                  auto fwdarg_2_1_0 = ({
                    // Computing floor
                    auto fwdarg_2_1_1_0 = ({
                      // Computing fabs
                      auto fwdarg_2_1_1_1_0 = ({
                        // Computing FDiv
                        llvm::Value* fwdarg_2_1_1_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                        llvm::Value* fwdarg_2_1_1_1_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                        Builder2.CreateFDiv(fwdarg_2_1_1_1_1_0, fwdarg_2_1_1_1_1_1);
                      });
                      local_div = fwdarg_2_1_1_1_0;
                      Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::fabs, std::vector<Type*>({fwdarg_2_1_1_1_0->getType()})), ArrayRef<Value*>({fwdarg_2_1_1_1_0}));
                    });
                    Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::floor, std::vector<Type*>({fwdarg_2_1_1_0->getType()})), ArrayRef<Value*>({fwdarg_2_1_1_0}));
                  });
                  auto fwdarg_2_1_1 = local_div;
                  Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::copysign, std::vector<Type*>({fwdarg_2_1_0->getType()})), ArrayRef<Value*>({fwdarg_2_1_0, fwdarg_2_1_1}));
                });
                Builder2.CreateFNeg(fwdarg_2_0);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = checkedMul(gutils->strongZero, Builder2, (gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        } else if (!dif && !gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = dif;
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        if (!gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          llvm::Value *local_div = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing CheckedMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FNeg
                auto revarg_2_0 = ({
                  // Computing copysign
                  auto revarg_2_1_0 = ({
                    // Computing floor
                    auto revarg_2_1_1_0 = ({
                      // Computing fabs
                      auto revarg_2_1_1_1_0 = ({
                        // Computing FDiv
                        llvm::Value* revarg_2_1_1_1_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                        llvm::Value* revarg_2_1_1_1_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                        Builder2.CreateFDiv(revarg_2_1_1_1_1_0, revarg_2_1_1_1_1_1);
                      });
                      local_div = revarg_2_1_1_1_0;
                      Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::fabs, std::vector<Type*>({revarg_2_1_1_1_0->getType()})), ArrayRef<Value*>({revarg_2_1_1_1_0}));
                    });
                    Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::floor, std::vector<Type*>({revarg_2_1_1_0->getType()})), ArrayRef<Value*>({revarg_2_1_1_0}));
                  });
                  auto revarg_2_1_1 = local_div;
                  Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::copysign, std::vector<Type*>({revarg_2_1_0->getType()})), ArrayRef<Value*>({revarg_2_1_0, revarg_2_1_1}));
                });
                Builder2.CreateFNeg(revarg_2_0);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = checkedMul(gutils->strongZero, Builder2, (gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(1)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(1), toadd, Builder2, call.getOperand(1)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "modf" ||
      funcName == "modff" ||
      funcName == "modfl") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = dif;
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = dif;
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = dif;
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "__fd_sincos_1" ||
      funcName == "__fd_sincos_1f" ||
      funcName == "__fd_sincos_1l") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_callset = nullptr;
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing ExtractValue
                auto fwdarg_1_0 = ({
                  // Computing Call
                  llvm::Value* fwdarg_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_1_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                local_callset = fwdarg_1_0;
                Builder2.CreateExtractValue(fwdarg_1_0, ArrayRef<unsigned>({(unsigned)(1)}));
              });
              auto fwdarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {0}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          {
            Value *itmp = ({
              // Computing FNeg
              auto fwdarg_0 = ({
                // Computing FMul
                auto fwdarg_1_0 = ({
                  // Computing ExtractValue
                  auto fwdarg_1_1_0 = local_callset;
                  Builder2.CreateExtractValue(fwdarg_1_1_0, ArrayRef<unsigned>({(unsigned)(0)}));
                });
                auto fwdarg_1_1 = dif;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul(fwdarg_1_0, (gutils->getWidth() == 1) ? fwdarg_1_1 : gutils->extractMeta(Builder2, fwdarg_1_1, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {1}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_callset = nullptr;
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing ExtractValue
                auto fwdarg_1_0 = ({
                  // Computing Call
                  llvm::Value* fwdarg_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_1_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                local_callset = fwdarg_1_0;
                Builder2.CreateExtractValue(fwdarg_1_0, ArrayRef<unsigned>({(unsigned)(1)}));
              });
              auto fwdarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {0}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          {
            Value *itmp = ({
              // Computing FNeg
              auto fwdarg_0 = ({
                // Computing FMul
                auto fwdarg_1_0 = ({
                  // Computing ExtractValue
                  auto fwdarg_1_1_0 = local_callset;
                  Builder2.CreateExtractValue(fwdarg_1_1_0, ArrayRef<unsigned>({(unsigned)(0)}));
                });
                auto fwdarg_1_1 = dif;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul(fwdarg_1_0, (gutils->getWidth() == 1) ? fwdarg_1_1 : gutils->extractMeta(Builder2, fwdarg_1_1, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {1}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          llvm::Value *local_callset = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = ({
                // Computing ExtractValue
                auto revarg_1_0 = ({
                  // Computing Call
                  llvm::Value* revarg_1_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_1_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                local_callset = revarg_1_0;
                Builder2.CreateExtractValue(revarg_1_0, ArrayRef<unsigned>({(unsigned)(1)}));
              });
              auto revarg_1 = ({
                Value *out = UndefValue::get(gutils->getShadowType(getSubType(call.getType(), 0)));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *prev = (gutils->getWidth() == 1) ? gutils->extractMeta(Builder2, dif, ArrayRef<unsigned>({0})) : gutils->extractMeta(Builder2, dif, ArrayRef<unsigned>({idx, 0}));
                  out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, prev, idx) : prev;
                  }
                out; })
;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(revarg_0, (gutils->getWidth() == 1) ? revarg_1 : gutils->extractMeta(Builder2, revarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          {
            Value *tmp = ({
              // Computing FNeg
              auto revarg_0 = ({
                // Computing FMul
                auto revarg_1_0 = ({
                  // Computing ExtractValue
                  auto revarg_1_1_0 = local_callset;
                  Builder2.CreateExtractValue(revarg_1_1_0, ArrayRef<unsigned>({(unsigned)(0)}));
                });
                auto revarg_1_1 = ({
                  Value *out = UndefValue::get(gutils->getShadowType(getSubType(call.getType(), 1)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    Value *prev = (gutils->getWidth() == 1) ? gutils->extractMeta(Builder2, dif, ArrayRef<unsigned>({1})) : gutils->extractMeta(Builder2, dif, ArrayRef<unsigned>({idx, 1}));
                    out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, prev, idx) : prev;
                    }
                  out; })
;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul(revarg_1_0, (gutils->getWidth() == 1) ? revarg_1_1 : gutils->extractMeta(Builder2, revarg_1_1, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "sincospi" ||
      funcName == "sincospif" ||
      funcName == "sincospil") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_callset = nullptr;
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ConstantFP::get(call.getOperand(0)->getType(), "3.1415926535897962684626433");
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ({
                  // Computing ExtractValue
                  auto fwdarg_2_1_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_callset = fwdarg_2_1_0;
                  Builder2.CreateExtractValue(fwdarg_2_1_0, ArrayRef<unsigned>({(unsigned)(1)}));
                });
                auto fwdarg_2_1 = dif;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul(fwdarg_2_0, (gutils->getWidth() == 1) ? fwdarg_2_1 : gutils->extractMeta(Builder2, fwdarg_2_1, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {0}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ConstantFP::get(call.getOperand(0)->getType(), "3.1415926535897962684626433");
              auto fwdarg_1 = ({
                // Computing FNeg
                auto fwdarg_2_0 = ({
                  // Computing FMul
                  auto fwdarg_2_1_0 = ({
                    // Computing ExtractValue
                    auto fwdarg_2_1_1_0 = local_callset;
                    Builder2.CreateExtractValue(fwdarg_2_1_1_0, ArrayRef<unsigned>({(unsigned)(0)}));
                  });
                  auto fwdarg_2_1_1 = dif;
                  Value *res = nullptr;
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    Value *V = Builder2.CreateFMul(fwdarg_2_1_0, (gutils->getWidth() == 1) ? fwdarg_2_1_1 : gutils->extractMeta(Builder2, fwdarg_2_1_1, idx));
                    if (gutils->getWidth() == 1) res = V;
                    else {
                      if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                      res = Builder2.CreateInsertValue(res, V, {idx});
                    }
                  }
                  res;
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_2_0 : gutils->extractMeta(Builder2, fwdarg_2_0, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {1}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_callset = nullptr;
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ConstantFP::get(call.getOperand(0)->getType(), "3.1415926535897962684626433");
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ({
                  // Computing ExtractValue
                  auto fwdarg_2_1_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_callset = fwdarg_2_1_0;
                  Builder2.CreateExtractValue(fwdarg_2_1_0, ArrayRef<unsigned>({(unsigned)(1)}));
                });
                auto fwdarg_2_1 = dif;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul(fwdarg_2_0, (gutils->getWidth() == 1) ? fwdarg_2_1 : gutils->extractMeta(Builder2, fwdarg_2_1, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {0}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ConstantFP::get(call.getOperand(0)->getType(), "3.1415926535897962684626433");
              auto fwdarg_1 = ({
                // Computing FNeg
                auto fwdarg_2_0 = ({
                  // Computing FMul
                  auto fwdarg_2_1_0 = ({
                    // Computing ExtractValue
                    auto fwdarg_2_1_1_0 = local_callset;
                    Builder2.CreateExtractValue(fwdarg_2_1_1_0, ArrayRef<unsigned>({(unsigned)(0)}));
                  });
                  auto fwdarg_2_1_1 = dif;
                  Value *res = nullptr;
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    Value *V = Builder2.CreateFMul(fwdarg_2_1_0, (gutils->getWidth() == 1) ? fwdarg_2_1_1 : gutils->extractMeta(Builder2, fwdarg_2_1_1, idx));
                    if (gutils->getWidth() == 1) res = V;
                    else {
                      if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                      res = Builder2.CreateInsertValue(res, V, {idx});
                    }
                  }
                  res;
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_2_0 : gutils->extractMeta(Builder2, fwdarg_2_0, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {1}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          llvm::Value *local_callset = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = ConstantFP::get(call.getOperand(0)->getType(), "3.1415926535897962684626433");
              auto revarg_1 = ({
                // Computing FMul
                auto revarg_2_0 = ({
                  // Computing ExtractValue
                  auto revarg_2_1_0 = ({
                    // Computing Call
                    llvm::Value* revarg_2_1_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_1_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_callset = revarg_2_1_0;
                  Builder2.CreateExtractValue(revarg_2_1_0, ArrayRef<unsigned>({(unsigned)(1)}));
                });
                auto revarg_2_1 = ({
                  Value *out = UndefValue::get(gutils->getShadowType(getSubType(call.getType(), 0)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    Value *prev = (gutils->getWidth() == 1) ? gutils->extractMeta(Builder2, dif, ArrayRef<unsigned>({0})) : gutils->extractMeta(Builder2, dif, ArrayRef<unsigned>({idx, 0}));
                    out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, prev, idx) : prev;
                    }
                  out; })
;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul(revarg_2_0, (gutils->getWidth() == 1) ? revarg_2_1 : gutils->extractMeta(Builder2, revarg_2_1, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(revarg_0, (gutils->getWidth() == 1) ? revarg_1 : gutils->extractMeta(Builder2, revarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = ConstantFP::get(call.getOperand(0)->getType(), "3.1415926535897962684626433");
              auto revarg_1 = ({
                // Computing FNeg
                auto revarg_2_0 = ({
                  // Computing FMul
                  auto revarg_2_1_0 = ({
                    // Computing ExtractValue
                    auto revarg_2_1_1_0 = local_callset;
                    Builder2.CreateExtractValue(revarg_2_1_1_0, ArrayRef<unsigned>({(unsigned)(0)}));
                  });
                  auto revarg_2_1_1 = ({
                    Value *out = UndefValue::get(gutils->getShadowType(getSubType(call.getType(), 1)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *prev = (gutils->getWidth() == 1) ? gutils->extractMeta(Builder2, dif, ArrayRef<unsigned>({1})) : gutils->extractMeta(Builder2, dif, ArrayRef<unsigned>({idx, 1}));
                      out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, prev, idx) : prev;
                      }
                    out; })
;
                  Value *res = nullptr;
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    Value *V = Builder2.CreateFMul(revarg_2_1_0, (gutils->getWidth() == 1) ? revarg_2_1_1 : gutils->extractMeta(Builder2, revarg_2_1_1, idx));
                    if (gutils->getWidth() == 1) res = V;
                    else {
                      if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                      res = Builder2.CreateInsertValue(res, V, {idx});
                    }
                  }
                  res;
                });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_2_0 : gutils->extractMeta(Builder2, revarg_2_0, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(revarg_0, (gutils->getWidth() == 1) ? revarg_1 : gutils->extractMeta(Builder2, revarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "xlogy_jl") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing log
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(1));
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::log, std::vector<Type*>({fwdarg_1_0->getType()})), ArrayRef<Value*>({fwdarg_1_0}));
              });
              auto fwdarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing Select
              auto fwdarg_0 = ({
                // Computing FCmpOEQ
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto fwdarg_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
                Builder2.CreateFCmpOEQ(fwdarg_1_0, fwdarg_1_1);
              });
              auto fwdarg_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
              auto fwdarg_2 = ({
                // Computing FMul
                auto fwdarg_3_0 = ({
                  // Computing FDiv
                  llvm::Value* fwdarg_3_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_3_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                  Builder2.CreateFDiv(fwdarg_3_1_0, fwdarg_3_1_1);
                });
                auto fwdarg_3_1 = dif;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul(fwdarg_3_0, (gutils->getWidth() == 1) ? fwdarg_3_1 : gutils->extractMeta(Builder2, fwdarg_3_1, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(fwdarg_0, fwdarg_1, (gutils->getWidth() == 1) ? fwdarg_2 : gutils->extractMeta(Builder2, fwdarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing log
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(1));
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::log, std::vector<Type*>({fwdarg_1_0->getType()})), ArrayRef<Value*>({fwdarg_1_0}));
              });
              auto fwdarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(1))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing Select
              auto fwdarg_0 = ({
                // Computing FCmpOEQ
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto fwdarg_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
                Builder2.CreateFCmpOEQ(fwdarg_1_0, fwdarg_1_1);
              });
              auto fwdarg_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
              auto fwdarg_2 = ({
                // Computing FMul
                auto fwdarg_3_0 = ({
                  // Computing FDiv
                  llvm::Value* fwdarg_3_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_3_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                  Builder2.CreateFDiv(fwdarg_3_1_0, fwdarg_3_1_1);
                });
                auto fwdarg_3_1 = dif;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul(fwdarg_3_0, (gutils->getWidth() == 1) ? fwdarg_3_1 : gutils->extractMeta(Builder2, fwdarg_3_1, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(fwdarg_0, fwdarg_1, (gutils->getWidth() == 1) ? fwdarg_2 : gutils->extractMeta(Builder2, fwdarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        } else if (!dif && !gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = ({
                // Computing log
                llvm::Value* revarg_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::log, std::vector<Type*>({revarg_1_0->getType()})), ArrayRef<Value*>({revarg_1_0}));
              });
              auto revarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(revarg_0, (gutils->getWidth() == 1) ? revarg_1 : gutils->extractMeta(Builder2, revarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        if (!gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing Select
              auto revarg_0 = ({
                // Computing FCmpOEQ
                llvm::Value* revarg_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto revarg_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
                Builder2.CreateFCmpOEQ(revarg_1_0, revarg_1_1);
              });
              auto revarg_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
              auto revarg_2 = ({
                // Computing FMul
                auto revarg_3_0 = ({
                  // Computing FDiv
                  llvm::Value* revarg_3_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  llvm::Value* revarg_3_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                  Builder2.CreateFDiv(revarg_3_1_0, revarg_3_1_1);
                });
                auto revarg_3_1 = dif;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul(revarg_3_0, (gutils->getWidth() == 1) ? revarg_3_1 : gutils->extractMeta(Builder2, revarg_3_1, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(revarg_0, revarg_1, (gutils->getWidth() == 1) ? revarg_2 : gutils->extractMeta(Builder2, revarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(1)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(1), toadd, Builder2, call.getOperand(1)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "tgamma") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing Call
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT_old = cast<CallInst>(&call)->getFunctionType();
                auto FT = FunctionType::get(FT_old->params()[0], FT_old->params(), FT_old->isVarArg());
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("digamma", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_1_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              auto fwdarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing Call
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT_old = cast<CallInst>(&call)->getFunctionType();
                auto FT = FunctionType::get(FT_old->params()[0], FT_old->params(), FT_old->isVarArg());
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("digamma", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_1_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              auto fwdarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = ({
                // Computing Call
                llvm::Value* revarg_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto FT_old = cast<CallInst>(&call)->getFunctionType();
                auto FT = FunctionType::get(FT_old->params()[0], FT_old->params(), FT_old->isVarArg());
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("digamma", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_1_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              auto revarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(revarg_0, (gutils->getWidth() == 1) ? revarg_1 : gutils->extractMeta(Builder2, revarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "logabsgamma" ||
      funcName == "lgamma") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing Call
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT_old = cast<CallInst>(&call)->getFunctionType();
                auto FT = FunctionType::get(FT_old->params()[0], FT_old->params(), FT_old->isVarArg());
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("digamma", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_1_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              auto fwdarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {0}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing Call
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT_old = cast<CallInst>(&call)->getFunctionType();
                auto FT = FunctionType::get(FT_old->params()[0], FT_old->params(), FT_old->isVarArg());
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("digamma", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_1_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              auto fwdarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {0}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = ({
                // Computing Call
                llvm::Value* revarg_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto FT_old = cast<CallInst>(&call)->getFunctionType();
                auto FT = FunctionType::get(FT_old->params()[0], FT_old->params(), FT_old->isVarArg());
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("digamma", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_1_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              auto revarg_1 = ({
                Value *out = UndefValue::get(gutils->getShadowType(getSubType(call.getType(), 0)));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *prev = (gutils->getWidth() == 1) ? gutils->extractMeta(Builder2, dif, ArrayRef<unsigned>({0})) : gutils->extractMeta(Builder2, dif, ArrayRef<unsigned>({idx, 0}));
                  out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, prev, idx) : prev;
                  }
                out; })
;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(revarg_0, (gutils->getWidth() == 1) ? revarg_1 : gutils->extractMeta(Builder2, revarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "logabsgammaf" ||
      funcName == "lgammaf") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing Call
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT_old = cast<CallInst>(&call)->getFunctionType();
                auto FT = FunctionType::get(FT_old->params()[0], FT_old->params(), FT_old->isVarArg());
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("digammaf", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_1_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              auto fwdarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {0}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing Call
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto FT_old = cast<CallInst>(&call)->getFunctionType();
                auto FT = FunctionType::get(FT_old->params()[0], FT_old->params(), FT_old->isVarArg());
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("digammaf", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_1_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              auto fwdarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {0}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = ({
                // Computing Call
                llvm::Value* revarg_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto FT_old = cast<CallInst>(&call)->getFunctionType();
                auto FT = FunctionType::get(FT_old->params()[0], FT_old->params(), FT_old->isVarArg());
                auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("digammaf", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_1_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              auto revarg_1 = ({
                Value *out = UndefValue::get(gutils->getShadowType(getSubType(call.getType(), 0)));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *prev = (gutils->getWidth() == 1) ? gutils->extractMeta(Builder2, dif, ArrayRef<unsigned>({0})) : gutils->extractMeta(Builder2, dif, ArrayRef<unsigned>({idx, 0}));
                  out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, prev, idx) : prev;
                  }
                out; })
;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(revarg_0, (gutils->getWidth() == 1) ? revarg_1 : gutils->extractMeta(Builder2, revarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "sinpi" ||
      funcName == "sinpif" ||
      funcName == "sinpil" ||
      funcName == "cospi" ||
      funcName == "cospif" ||
      funcName == "cospil") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ConstantFP::get(call.getOperand(0)->getType(), "3.1415926535897962684626433");
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ({
                  // Computing Call
                  auto fwdarg_2_1_0 = ({
                    // Computing FAdd
                    llvm::Value* fwdarg_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto fwdarg_2_1_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "0.5");
                    Builder2.CreateFAdd(fwdarg_2_1_1_0, fwdarg_2_1_1_1);
                  });
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                auto fwdarg_2_1 = dif;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul(fwdarg_2_0, (gutils->getWidth() == 1) ? fwdarg_2_1 : gutils->extractMeta(Builder2, fwdarg_2_1, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ConstantFP::get(call.getOperand(0)->getType(), "3.1415926535897962684626433");
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ({
                  // Computing Call
                  auto fwdarg_2_1_0 = ({
                    // Computing FAdd
                    llvm::Value* fwdarg_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto fwdarg_2_1_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "0.5");
                    Builder2.CreateFAdd(fwdarg_2_1_1_0, fwdarg_2_1_1_1);
                  });
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                auto fwdarg_2_1 = dif;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul(fwdarg_2_0, (gutils->getWidth() == 1) ? fwdarg_2_1 : gutils->extractMeta(Builder2, fwdarg_2_1, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = ConstantFP::get(call.getOperand(0)->getType(), "3.1415926535897962684626433");
              auto revarg_1 = ({
                // Computing FMul
                auto revarg_2_0 = ({
                  // Computing Call
                  auto revarg_2_1_0 = ({
                    // Computing FAdd
                    llvm::Value* revarg_2_1_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    auto revarg_2_1_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "0.5");
                    Builder2.CreateFAdd(revarg_2_1_1_0, revarg_2_1_1_1);
                  });
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                auto revarg_2_1 = dif;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFMul(revarg_2_0, (gutils->getWidth() == 1) ? revarg_2_1 : gutils->extractMeta(Builder2, revarg_2_1, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(revarg_0, (gutils->getWidth() == 1) ? revarg_1 : gutils->extractMeta(Builder2, revarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "j0") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FNeg
                auto fwdarg_2_0 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("j1", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFNeg(fwdarg_2_0);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FNeg
                auto fwdarg_2_0 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("j1", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFNeg(fwdarg_2_0);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FNeg
                auto revarg_2_0 = ({
                  // Computing Call
                  llvm::Value* revarg_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("j1", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFNeg(revarg_2_0);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "j0f") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FNeg
                auto fwdarg_2_0 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("j1f", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFNeg(fwdarg_2_0);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FNeg
                auto fwdarg_2_0 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("j1f", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFNeg(fwdarg_2_0);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FNeg
                auto revarg_2_0 = ({
                  // Computing Call
                  llvm::Value* revarg_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("j1f", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFNeg(revarg_2_0);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "y0") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FNeg
                auto fwdarg_2_0 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("y1", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFNeg(fwdarg_2_0);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FNeg
                auto fwdarg_2_0 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("y1", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFNeg(fwdarg_2_0);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FNeg
                auto revarg_2_0 = ({
                  // Computing Call
                  llvm::Value* revarg_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("y1", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFNeg(revarg_2_0);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "y0f") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FNeg
                auto fwdarg_2_0 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("y1f", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFNeg(fwdarg_2_0);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FNeg
                auto fwdarg_2_0 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("y1f", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFNeg(fwdarg_2_0);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FNeg
                auto revarg_2_0 = ({
                  // Computing Call
                  llvm::Value* revarg_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("y1f", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_1_0})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFNeg(revarg_2_0);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "j1") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "0.5");
                auto fwdarg_2_1 = ({
                  // Computing FSub
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("j0", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto fwdarg_2_2_1 = ({
                    // Computing Call
                    auto fwdarg_2_2_2_0 = ConstantInt::getSigned(Type::getIntNTy(gutils->oldFunc->getContext(), 32), 2);
                    llvm::Value* fwdarg_2_2_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto FT_old = cast<CallInst>(&call)->getFunctionType();
                    SmallVector<llvm::Type*, 1> FT_args = {Type::getInt32Ty(gutils->oldFunc->getContext())};
                    FT_args.append(FT_old->params().begin(), FT_old->params().end());
                    auto FT = FunctionType::get(FT_old->getReturnType(), FT_args, FT_old->isVarArg());
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("jn", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_2_0, fwdarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Builder2.CreateFSub(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "0.5");
                auto fwdarg_2_1 = ({
                  // Computing FSub
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("j0", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto fwdarg_2_2_1 = ({
                    // Computing Call
                    auto fwdarg_2_2_2_0 = ConstantInt::getSigned(Type::getIntNTy(gutils->oldFunc->getContext(), 32), 2);
                    llvm::Value* fwdarg_2_2_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto FT_old = cast<CallInst>(&call)->getFunctionType();
                    SmallVector<llvm::Type*, 1> FT_args = {Type::getInt32Ty(gutils->oldFunc->getContext())};
                    FT_args.append(FT_old->params().begin(), FT_old->params().end());
                    auto FT = FunctionType::get(FT_old->getReturnType(), FT_args, FT_old->isVarArg());
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("jn", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_2_0, fwdarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Builder2.CreateFSub(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FMul
                auto revarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "0.5");
                auto revarg_2_1 = ({
                  // Computing FSub
                  auto revarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* revarg_2_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("j0", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto revarg_2_2_1 = ({
                    // Computing Call
                    auto revarg_2_2_2_0 = ConstantInt::getSigned(Type::getIntNTy(gutils->oldFunc->getContext(), 32), 2);
                    llvm::Value* revarg_2_2_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    auto FT_old = cast<CallInst>(&call)->getFunctionType();
                    SmallVector<llvm::Type*, 1> FT_args = {Type::getInt32Ty(gutils->oldFunc->getContext())};
                    FT_args.append(FT_old->params().begin(), FT_old->params().end());
                    auto FT = FunctionType::get(FT_old->getReturnType(), FT_args, FT_old->isVarArg());
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("jn", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_2_0, revarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Builder2.CreateFSub(revarg_2_2_0, revarg_2_2_1);
                });
                Builder2.CreateFMul(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "j1f") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "0.5");
                auto fwdarg_2_1 = ({
                  // Computing FSub
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("j0f", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto fwdarg_2_2_1 = ({
                    // Computing Call
                    auto fwdarg_2_2_2_0 = ConstantInt::getSigned(Type::getIntNTy(gutils->oldFunc->getContext(), 32), 2);
                    llvm::Value* fwdarg_2_2_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto FT_old = cast<CallInst>(&call)->getFunctionType();
                    SmallVector<llvm::Type*, 1> FT_args = {Type::getInt32Ty(gutils->oldFunc->getContext())};
                    FT_args.append(FT_old->params().begin(), FT_old->params().end());
                    auto FT = FunctionType::get(FT_old->getReturnType(), FT_args, FT_old->isVarArg());
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("jnf", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_2_0, fwdarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Builder2.CreateFSub(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "0.5");
                auto fwdarg_2_1 = ({
                  // Computing FSub
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("j0f", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto fwdarg_2_2_1 = ({
                    // Computing Call
                    auto fwdarg_2_2_2_0 = ConstantInt::getSigned(Type::getIntNTy(gutils->oldFunc->getContext(), 32), 2);
                    llvm::Value* fwdarg_2_2_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto FT_old = cast<CallInst>(&call)->getFunctionType();
                    SmallVector<llvm::Type*, 1> FT_args = {Type::getInt32Ty(gutils->oldFunc->getContext())};
                    FT_args.append(FT_old->params().begin(), FT_old->params().end());
                    auto FT = FunctionType::get(FT_old->getReturnType(), FT_args, FT_old->isVarArg());
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("jnf", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_2_0, fwdarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Builder2.CreateFSub(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FMul
                auto revarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "0.5");
                auto revarg_2_1 = ({
                  // Computing FSub
                  auto revarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* revarg_2_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("j0f", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto revarg_2_2_1 = ({
                    // Computing Call
                    auto revarg_2_2_2_0 = ConstantInt::getSigned(Type::getIntNTy(gutils->oldFunc->getContext(), 32), 2);
                    llvm::Value* revarg_2_2_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    auto FT_old = cast<CallInst>(&call)->getFunctionType();
                    SmallVector<llvm::Type*, 1> FT_args = {Type::getInt32Ty(gutils->oldFunc->getContext())};
                    FT_args.append(FT_old->params().begin(), FT_old->params().end());
                    auto FT = FunctionType::get(FT_old->getReturnType(), FT_args, FT_old->isVarArg());
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("jnf", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_2_0, revarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Builder2.CreateFSub(revarg_2_2_0, revarg_2_2_1);
                });
                Builder2.CreateFMul(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "y1") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "0.5");
                auto fwdarg_2_1 = ({
                  // Computing FSub
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("y0", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto fwdarg_2_2_1 = ({
                    // Computing Call
                    auto fwdarg_2_2_2_0 = ConstantInt::getSigned(Type::getIntNTy(gutils->oldFunc->getContext(), 32), 2);
                    llvm::Value* fwdarg_2_2_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto FT_old = cast<CallInst>(&call)->getFunctionType();
                    SmallVector<llvm::Type*, 1> FT_args = {Type::getInt32Ty(gutils->oldFunc->getContext())};
                    FT_args.append(FT_old->params().begin(), FT_old->params().end());
                    auto FT = FunctionType::get(FT_old->getReturnType(), FT_args, FT_old->isVarArg());
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("yn", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_2_0, fwdarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Builder2.CreateFSub(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "0.5");
                auto fwdarg_2_1 = ({
                  // Computing FSub
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("y0", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto fwdarg_2_2_1 = ({
                    // Computing Call
                    auto fwdarg_2_2_2_0 = ConstantInt::getSigned(Type::getIntNTy(gutils->oldFunc->getContext(), 32), 2);
                    llvm::Value* fwdarg_2_2_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto FT_old = cast<CallInst>(&call)->getFunctionType();
                    SmallVector<llvm::Type*, 1> FT_args = {Type::getInt32Ty(gutils->oldFunc->getContext())};
                    FT_args.append(FT_old->params().begin(), FT_old->params().end());
                    auto FT = FunctionType::get(FT_old->getReturnType(), FT_args, FT_old->isVarArg());
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("yn", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_2_0, fwdarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Builder2.CreateFSub(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FMul
                auto revarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "0.5");
                auto revarg_2_1 = ({
                  // Computing FSub
                  auto revarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* revarg_2_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("y0", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto revarg_2_2_1 = ({
                    // Computing Call
                    auto revarg_2_2_2_0 = ConstantInt::getSigned(Type::getIntNTy(gutils->oldFunc->getContext(), 32), 2);
                    llvm::Value* revarg_2_2_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    auto FT_old = cast<CallInst>(&call)->getFunctionType();
                    SmallVector<llvm::Type*, 1> FT_args = {Type::getInt32Ty(gutils->oldFunc->getContext())};
                    FT_args.append(FT_old->params().begin(), FT_old->params().end());
                    auto FT = FunctionType::get(FT_old->getReturnType(), FT_args, FT_old->isVarArg());
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("yn", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_2_0, revarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Builder2.CreateFSub(revarg_2_2_0, revarg_2_2_1);
                });
                Builder2.CreateFMul(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "y1f") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "0.5");
                auto fwdarg_2_1 = ({
                  // Computing FSub
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("y0f", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto fwdarg_2_2_1 = ({
                    // Computing Call
                    auto fwdarg_2_2_2_0 = ConstantInt::getSigned(Type::getIntNTy(gutils->oldFunc->getContext(), 32), 2);
                    llvm::Value* fwdarg_2_2_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto FT_old = cast<CallInst>(&call)->getFunctionType();
                    SmallVector<llvm::Type*, 1> FT_args = {Type::getInt32Ty(gutils->oldFunc->getContext())};
                    FT_args.append(FT_old->params().begin(), FT_old->params().end());
                    auto FT = FunctionType::get(FT_old->getReturnType(), FT_args, FT_old->isVarArg());
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("ynf", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_2_0, fwdarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Builder2.CreateFSub(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "0.5");
                auto fwdarg_2_1 = ({
                  // Computing FSub
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("y0f", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto fwdarg_2_2_1 = ({
                    // Computing Call
                    auto fwdarg_2_2_2_0 = ConstantInt::getSigned(Type::getIntNTy(gutils->oldFunc->getContext(), 32), 2);
                    llvm::Value* fwdarg_2_2_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto FT_old = cast<CallInst>(&call)->getFunctionType();
                    SmallVector<llvm::Type*, 1> FT_args = {Type::getInt32Ty(gutils->oldFunc->getContext())};
                    FT_args.append(FT_old->params().begin(), FT_old->params().end());
                    auto FT = FunctionType::get(FT_old->getReturnType(), FT_args, FT_old->isVarArg());
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("ynf", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_2_0, fwdarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Builder2.CreateFSub(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FMul
                auto revarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "0.5");
                auto revarg_2_1 = ({
                  // Computing FSub
                  auto revarg_2_2_0 = ({
                    // Computing Call
                    llvm::Value* revarg_2_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("y0f", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto revarg_2_2_1 = ({
                    // Computing Call
                    auto revarg_2_2_2_0 = ConstantInt::getSigned(Type::getIntNTy(gutils->oldFunc->getContext(), 32), 2);
                    llvm::Value* revarg_2_2_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    auto FT_old = cast<CallInst>(&call)->getFunctionType();
                    SmallVector<llvm::Type*, 1> FT_args = {Type::getInt32Ty(gutils->oldFunc->getContext())};
                    FT_args.append(FT_old->params().begin(), FT_old->params().end());
                    auto FT = FunctionType::get(FT_old->getReturnType(), FT_args, FT_old->isVarArg());
                    auto callval = gutils->oldFunc->getParent()->getOrInsertFunction("ynf", FT, called->getAttributes().removeFnAttribute(called->getContext(), "enzymejl_needs_restoration")).getCallee();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_2_0, revarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Builder2.CreateFSub(revarg_2_2_0, revarg_2_2_1);
                });
                Builder2.CreateFMul(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "jn" ||
      funcName == "jnf" ||
      funcName == "yn" ||
      funcName == "ynf") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(1)->getType(), "0.5");
                auto fwdarg_2_1 = ({
                  // Computing FSub
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    auto fwdarg_2_2_1_0 = ({
                      // Computing Sub
                      llvm::Value* fwdarg_2_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto fwdarg_2_2_1_1_1 = ConstantInt::getSigned(call.getOperand(0)->getType(), 1);
                      Builder2.CreateSub(fwdarg_2_2_1_1_0, fwdarg_2_2_1_1_1);
                    });
                    llvm::Value* fwdarg_2_2_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0, fwdarg_2_2_1_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto fwdarg_2_2_1 = ({
                    // Computing Call
                    auto fwdarg_2_2_2_0 = ({
                      // Computing Add
                      llvm::Value* fwdarg_2_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto fwdarg_2_2_2_1_1 = ConstantInt::getSigned(call.getOperand(0)->getType(), 1);
                      Builder2.CreateAdd(fwdarg_2_2_2_1_0, fwdarg_2_2_2_1_1);
                    });
                    llvm::Value* fwdarg_2_2_2_1 = gutils->getNewFromOriginal(call.getOperand(1));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_2_0, fwdarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Builder2.CreateFSub(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(1))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(1)->getType(), "0.5");
                auto fwdarg_2_1 = ({
                  // Computing FSub
                  auto fwdarg_2_2_0 = ({
                    // Computing Call
                    auto fwdarg_2_2_1_0 = ({
                      // Computing Sub
                      llvm::Value* fwdarg_2_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto fwdarg_2_2_1_1_1 = ConstantInt::getSigned(call.getOperand(0)->getType(), 1);
                      Builder2.CreateSub(fwdarg_2_2_1_1_0, fwdarg_2_2_1_1_1);
                    });
                    llvm::Value* fwdarg_2_2_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_1_0, fwdarg_2_2_1_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto fwdarg_2_2_1 = ({
                    // Computing Call
                    auto fwdarg_2_2_2_0 = ({
                      // Computing Add
                      llvm::Value* fwdarg_2_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto fwdarg_2_2_2_1_1 = ConstantInt::getSigned(call.getOperand(0)->getType(), 1);
                      Builder2.CreateAdd(fwdarg_2_2_2_1_0, fwdarg_2_2_2_1_1);
                    });
                    llvm::Value* fwdarg_2_2_2_1 = gutils->getNewFromOriginal(call.getOperand(1));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_2_0, fwdarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Builder2.CreateFSub(fwdarg_2_2_0, fwdarg_2_2_1);
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FMul
                auto revarg_2_0 = ConstantFP::get(call.getOperand(1)->getType(), "0.5");
                auto revarg_2_1 = ({
                  // Computing FSub
                  auto revarg_2_2_0 = ({
                    // Computing Call
                    auto revarg_2_2_1_0 = ({
                      // Computing Sub
                      llvm::Value* revarg_2_2_1_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                      auto revarg_2_2_1_1_1 = ConstantInt::getSigned(call.getOperand(0)->getType(), 1);
                      Builder2.CreateSub(revarg_2_2_1_1_0, revarg_2_2_1_1_1);
                    });
                    llvm::Value* revarg_2_2_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_1_0, revarg_2_2_1_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto revarg_2_2_1 = ({
                    // Computing Call
                    auto revarg_2_2_2_0 = ({
                      // Computing Add
                      llvm::Value* revarg_2_2_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                      auto revarg_2_2_2_1_1 = ConstantInt::getSigned(call.getOperand(0)->getType(), 1);
                      Builder2.CreateAdd(revarg_2_2_2_1_0, revarg_2_2_2_1_1);
                    });
                    llvm::Value* revarg_2_2_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_2_0, revarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Builder2.CreateFSub(revarg_2_2_0, revarg_2_2_1);
                });
                Builder2.CreateFMul(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(1)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(1), toadd, Builder2, call.getOperand(1)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "cmplx_in") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
            Value *res = ({
              // Computing subroutine CFMul
              auto fwdnsrarg_0 = gutils->invertPointerM(call.getOperand(1), Builder2, TR.query(&call));
              auto fwdnsrarg_1 = ({
                // Computing subroutine CFMul
                auto fwdnsrarg_2_0 = ({
                  auto ty = call.getOperand(1)->getType();
                  Value *ret = nullptr;
                  if (auto ST = dyn_cast<StructType>(ty)) {
                    ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "0.5"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                    ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "0.5"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                  } else {
                    llvm::errs() << *ty << "\n";
                    assert(0 && "unhandled cfp");
                  }
                  ret;
                })
;
                auto fwdnsrarg_2_1 = ({
                  // Computing subroutine CFAdd
                  auto fwdnsrarg_2_2_0 = ({
                    // Computing Call
                    auto fwdnsrarg_2_2_1_0 = ({
                      // Computing FSub
                      llvm::Value* fwdnsrarg_2_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto fwdnsrarg_2_2_1_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1");
                      Builder2.CreateFSub(fwdnsrarg_2_2_1_1_0, fwdnsrarg_2_2_1_1_1);
                    });
                    llvm::Value* fwdnsrarg_2_2_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdnsrarg_2_2_1_0, fwdnsrarg_2_2_1_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto fwdnsrarg_2_2_1 = ({
                    // Computing Call
                    auto fwdnsrarg_2_2_2_0 = ({
                      // Computing FAdd
                      llvm::Value* fwdnsrarg_2_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto fwdnsrarg_2_2_2_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1");
                      Builder2.CreateFAdd(fwdnsrarg_2_2_2_1_0, fwdnsrarg_2_2_2_1_1);
                    });
                    llvm::Value* fwdnsrarg_2_2_2_1 = gutils->getNewFromOriginal(call.getOperand(1));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdnsrarg_2_2_2_0, fwdnsrarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Value* local_re1 = Builder2.CreateExtractValue(fwdnsrarg_2_2_0, ArrayRef<unsigned>({0}), "re1");
                  Value* local_im1 = Builder2.CreateExtractValue(fwdnsrarg_2_2_0, ArrayRef<unsigned>({1}), "im1");
                  Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_2_2_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_2_2_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    auto fwdnsrarg_2_2_sr_0 = ({
                      // Computing FAdd
                      auto fwdnsrarg_2_2_sr_1_0 = local_re1;
                      auto fwdnsrarg_2_2_sr_1_1 = local_re2;
                      Builder2.CreateFAdd(fwdnsrarg_2_2_sr_1_0, fwdnsrarg_2_2_sr_1_1);
                    });
                    auto fwdnsrarg_2_2_sr_1 = ({
                      // Computing FAdd
                      auto fwdnsrarg_2_2_sr_2_0 = local_im1;
                      auto fwdnsrarg_2_2_sr_2_1 = local_im2;
                      Builder2.CreateFAdd(fwdnsrarg_2_2_sr_2_0, fwdnsrarg_2_2_sr_2_1);
                    });
                    Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_2_2_sr_0->getType(), 2));
                    {
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, fwdnsrarg_2_2_sr_0, idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, fwdnsrarg_2_2_sr_1, idxs);
                      }
                    }
                     res;
                  });
                });
                Value* local_re1 = Builder2.CreateExtractValue(fwdnsrarg_2_0, ArrayRef<unsigned>({0}), "re1");
                Value* local_im1 = Builder2.CreateExtractValue(fwdnsrarg_2_0, ArrayRef<unsigned>({1}), "im1");
                Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_2_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_2_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFMul_splat
                  auto fwdnsrarg_2_sr_0 = local_re1;
                  auto fwdnsrarg_2_sr_1 = local_im1;
                  auto fwdnsrarg_2_sr_2 = local_re2;
                  auto fwdnsrarg_2_sr_3 = local_im2;
                  ({
                    auto fwdnsrarg_2_sr_sr_0 = ({
                      // Computing FSub
                      auto fwdnsrarg_2_sr_sr_1_0 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_2_sr_sr_1_1_0 = fwdnsrarg_2_sr_0;
                        llvm::Value* fwdnsrarg_2_sr_sr_1_1_1 = fwdnsrarg_2_sr_2;
                        Builder2.CreateFMul(fwdnsrarg_2_sr_sr_1_1_0, fwdnsrarg_2_sr_sr_1_1_1);
                      });
                      auto fwdnsrarg_2_sr_sr_1_1 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_2_sr_sr_1_2_0 = fwdnsrarg_2_sr_1;
                        llvm::Value* fwdnsrarg_2_sr_sr_1_2_1 = fwdnsrarg_2_sr_3;
                        Builder2.CreateFMul(fwdnsrarg_2_sr_sr_1_2_0, fwdnsrarg_2_sr_sr_1_2_1);
                      });
                      Builder2.CreateFSub(fwdnsrarg_2_sr_sr_1_0, fwdnsrarg_2_sr_sr_1_1);
                    });
                    auto fwdnsrarg_2_sr_sr_1 = ({
                      // Computing FAdd
                      auto fwdnsrarg_2_sr_sr_2_0 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_2_sr_sr_2_1_0 = fwdnsrarg_2_sr_0;
                        llvm::Value* fwdnsrarg_2_sr_sr_2_1_1 = fwdnsrarg_2_sr_3;
                        Builder2.CreateFMul(fwdnsrarg_2_sr_sr_2_1_0, fwdnsrarg_2_sr_sr_2_1_1);
                      });
                      auto fwdnsrarg_2_sr_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_2_sr_sr_2_2_0 = fwdnsrarg_2_sr_2;
                        llvm::Value* fwdnsrarg_2_sr_sr_2_2_1 = fwdnsrarg_2_sr_1;
                        Builder2.CreateFMul(fwdnsrarg_2_sr_sr_2_2_0, fwdnsrarg_2_sr_sr_2_2_1);
                      });
                      Builder2.CreateFAdd(fwdnsrarg_2_sr_sr_2_0, fwdnsrarg_2_sr_sr_2_1);
                    });
                    Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_2_sr_sr_0->getType(), 2));
                    {
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, fwdnsrarg_2_sr_sr_0, idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, fwdnsrarg_2_sr_sr_1, idxs);
                      }
                    }
                     res;
                  });
                });
              });
              Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_1, ArrayRef<unsigned>({0}), "re2");
              Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_1, ArrayRef<unsigned>({1}), "im2");
              ({
                // Computing subroutine CFMul_splat
                auto fwdnsrarg_sr_0 = local_re1;
                auto fwdnsrarg_sr_1 = local_im1;
                auto fwdnsrarg_sr_2 = local_re2;
                auto fwdnsrarg_sr_3 = local_im2;
                ({
                  auto fwdnsrarg_sr_sr_0 = ({
                    // Computing FSub
                    auto fwdnsrarg_sr_sr_1_0 = ({
                      // Computing FMul
                      auto fwdnsrarg_sr_sr_1_1_0 = fwdnsrarg_sr_0;
                      llvm::Value* fwdnsrarg_sr_sr_1_1_1 = fwdnsrarg_sr_2;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_1_1_0, idx), fwdnsrarg_sr_sr_1_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdnsrarg_sr_sr_1_1 = ({
                      // Computing FMul
                      auto fwdnsrarg_sr_sr_1_2_0 = fwdnsrarg_sr_1;
                      llvm::Value* fwdnsrarg_sr_sr_1_2_1 = fwdnsrarg_sr_3;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_1_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_1_2_0, idx), fwdnsrarg_sr_sr_1_2_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_1_1, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  auto fwdnsrarg_sr_sr_1 = ({
                    // Computing FAdd
                    auto fwdnsrarg_sr_sr_2_0 = ({
                      // Computing FMul
                      auto fwdnsrarg_sr_sr_2_1_0 = fwdnsrarg_sr_0;
                      llvm::Value* fwdnsrarg_sr_sr_2_1_1 = fwdnsrarg_sr_3;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_2_1_0, idx), fwdnsrarg_sr_sr_2_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdnsrarg_sr_sr_2_1 = ({
                      // Computing FMul
                      llvm::Value* fwdnsrarg_sr_sr_2_2_0 = fwdnsrarg_sr_2;
                      auto fwdnsrarg_sr_sr_2_2_1 = fwdnsrarg_sr_1;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul(fwdnsrarg_sr_sr_2_2_0, (gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_2_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_2_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_2_1, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_0->getType() : getSubType(fwdnsrarg_sr_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
            });
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
 assert(gutils->isConstantValue(call.getOperand(0)));
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(1))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing subroutine CFMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing subroutine Conj
                auto fwdarg_2_0 = ({
                  // Computing subroutine CFMul
                  auto fwdarg_2_1_0 = ({
                    auto ty = call.getOperand(1)->getType();
                    Value *ret = nullptr;
                    if (auto ST = dyn_cast<StructType>(ty)) {
                      ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "0.5"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                      ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "0.5"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                    } else {
                      llvm::errs() << *ty << "\n";
                      assert(0 && "unhandled cfp");
                    }
                    ret;
                  })
;
                  auto fwdarg_2_1_1 = ({
                    // Computing subroutine CFAdd
                    auto fwdarg_2_1_2_0 = ({
                      // Computing Call
                      auto fwdarg_2_1_2_1_0 = ({
                        // Computing FSub
                        llvm::Value* fwdarg_2_1_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                        auto fwdarg_2_1_2_1_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1");
                        Builder2.CreateFSub(fwdarg_2_1_2_1_1_0, fwdarg_2_1_2_1_1_1);
                      });
                      llvm::Value* fwdarg_2_1_2_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_2_1_0, fwdarg_2_1_2_1_1})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    auto fwdarg_2_1_2_1 = ({
                      // Computing Call
                      auto fwdarg_2_1_2_2_0 = ({
                        // Computing FAdd
                        llvm::Value* fwdarg_2_1_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                        auto fwdarg_2_1_2_2_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1");
                        Builder2.CreateFAdd(fwdarg_2_1_2_2_1_0, fwdarg_2_1_2_2_1_1);
                      });
                      llvm::Value* fwdarg_2_1_2_2_1 = gutils->getNewFromOriginal(call.getOperand(1));
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_2_2_0, fwdarg_2_1_2_2_1})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    Value* local_re1 = Builder2.CreateExtractValue(fwdarg_2_1_2_0, ArrayRef<unsigned>({0}), "re1");
                    Value* local_im1 = Builder2.CreateExtractValue(fwdarg_2_1_2_0, ArrayRef<unsigned>({1}), "im1");
                    Value* local_re2 = Builder2.CreateExtractValue(fwdarg_2_1_2_1, ArrayRef<unsigned>({0}), "re2");
                    Value* local_im2 = Builder2.CreateExtractValue(fwdarg_2_1_2_1, ArrayRef<unsigned>({1}), "im2");
                    ({
                      auto fwdarg_2_1_2_sr_0 = ({
                        // Computing FAdd
                        auto fwdarg_2_1_2_sr_1_0 = local_re1;
                        auto fwdarg_2_1_2_sr_1_1 = local_re2;
                        Builder2.CreateFAdd(fwdarg_2_1_2_sr_1_0, fwdarg_2_1_2_sr_1_1);
                      });
                      auto fwdarg_2_1_2_sr_1 = ({
                        // Computing FAdd
                        auto fwdarg_2_1_2_sr_2_0 = local_im1;
                        auto fwdarg_2_1_2_sr_2_1 = local_im2;
                        Builder2.CreateFAdd(fwdarg_2_1_2_sr_2_0, fwdarg_2_1_2_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(fwdarg_2_1_2_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, fwdarg_2_1_2_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, fwdarg_2_1_2_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                  Value* local_re1 = Builder2.CreateExtractValue(fwdarg_2_1_0, ArrayRef<unsigned>({0}), "re1");
                  Value* local_im1 = Builder2.CreateExtractValue(fwdarg_2_1_0, ArrayRef<unsigned>({1}), "im1");
                  Value* local_re2 = Builder2.CreateExtractValue(fwdarg_2_1_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(fwdarg_2_1_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto fwdarg_2_1_sr_0 = local_re1;
                    auto fwdarg_2_1_sr_1 = local_im1;
                    auto fwdarg_2_1_sr_2 = local_re2;
                    auto fwdarg_2_1_sr_3 = local_im2;
                    ({
                      auto fwdarg_2_1_sr_sr_0 = ({
                        // Computing FSub
                        auto fwdarg_2_1_sr_sr_1_0 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_2_1_sr_sr_1_1_0 = fwdarg_2_1_sr_0;
                          llvm::Value* fwdarg_2_1_sr_sr_1_1_1 = fwdarg_2_1_sr_2;
                          Builder2.CreateFMul(fwdarg_2_1_sr_sr_1_1_0, fwdarg_2_1_sr_sr_1_1_1);
                        });
                        auto fwdarg_2_1_sr_sr_1_1 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_2_1_sr_sr_1_2_0 = fwdarg_2_1_sr_1;
                          llvm::Value* fwdarg_2_1_sr_sr_1_2_1 = fwdarg_2_1_sr_3;
                          Builder2.CreateFMul(fwdarg_2_1_sr_sr_1_2_0, fwdarg_2_1_sr_sr_1_2_1);
                        });
                        Builder2.CreateFSub(fwdarg_2_1_sr_sr_1_0, fwdarg_2_1_sr_sr_1_1);
                      });
                      auto fwdarg_2_1_sr_sr_1 = ({
                        // Computing FAdd
                        auto fwdarg_2_1_sr_sr_2_0 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_2_1_sr_sr_2_1_0 = fwdarg_2_1_sr_0;
                          llvm::Value* fwdarg_2_1_sr_sr_2_1_1 = fwdarg_2_1_sr_3;
                          Builder2.CreateFMul(fwdarg_2_1_sr_sr_2_1_0, fwdarg_2_1_sr_sr_2_1_1);
                        });
                        auto fwdarg_2_1_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_2_1_sr_sr_2_2_0 = fwdarg_2_1_sr_2;
                          llvm::Value* fwdarg_2_1_sr_sr_2_2_1 = fwdarg_2_1_sr_1;
                          Builder2.CreateFMul(fwdarg_2_1_sr_sr_2_2_0, fwdarg_2_1_sr_sr_2_2_1);
                        });
                        Builder2.CreateFAdd(fwdarg_2_1_sr_sr_2_0, fwdarg_2_1_sr_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(fwdarg_2_1_sr_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, fwdarg_2_1_sr_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, fwdarg_2_1_sr_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re = Builder2.CreateExtractValue(fwdarg_2_0, ArrayRef<unsigned>({0}), "re");
                Value* local_im = Builder2.CreateExtractValue(fwdarg_2_0, ArrayRef<unsigned>({1}), "im");
                ({
                  auto fwdarg_2_sr_0 = local_re;
                  auto fwdarg_2_sr_1 = ({
                    // Computing FNeg
                    auto fwdarg_2_sr_2_0 = local_im;
                    Builder2.CreateFNeg(fwdarg_2_sr_2_0);
                  });
                  Value *res = UndefValue::get(ArrayType::get(fwdarg_2_sr_0->getType(), 2));
                  {
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, fwdarg_2_sr_0, idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, fwdarg_2_sr_1, idxs);
                    }
                  }
                   res;
                });
              });
              Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1, ArrayRef<unsigned>({0}), "re2");
              Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1, ArrayRef<unsigned>({1}), "im2");
              ({
                // Computing subroutine CFMul_splat
                auto fwdarg_sr_0 = local_re1;
                auto fwdarg_sr_1 = local_im1;
                auto fwdarg_sr_2 = local_re2;
                auto fwdarg_sr_3 = local_im2;
                ({
                  auto fwdarg_sr_sr_0 = ({
                    // Computing FSub
                    auto fwdarg_sr_sr_1_0 = ({
                      // Computing FMul
                      auto fwdarg_sr_sr_1_1_0 = fwdarg_sr_0;
                      llvm::Value* fwdarg_sr_sr_1_1_1 = fwdarg_sr_2;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_sr_sr_1_1_0, idx), fwdarg_sr_sr_1_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdarg_sr_sr_1_1 = ({
                      // Computing FMul
                      auto fwdarg_sr_sr_1_2_0 = fwdarg_sr_1;
                      llvm::Value* fwdarg_sr_sr_1_2_1 = fwdarg_sr_3;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_sr_sr_1_2_0 : gutils->extractMeta(Builder2, fwdarg_sr_sr_1_2_0, idx), fwdarg_sr_sr_1_2_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_sr_sr_1_1 : gutils->extractMeta(Builder2, fwdarg_sr_sr_1_1, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  auto fwdarg_sr_sr_1 = ({
                    // Computing FAdd
                    auto fwdarg_sr_sr_2_0 = ({
                      // Computing FMul
                      auto fwdarg_sr_sr_2_1_0 = fwdarg_sr_0;
                      llvm::Value* fwdarg_sr_sr_2_1_1 = fwdarg_sr_3;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_sr_sr_2_1_0, idx), fwdarg_sr_sr_2_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdarg_sr_sr_2_1 = ({
                      // Computing FMul
                      llvm::Value* fwdarg_sr_sr_2_2_0 = fwdarg_sr_2;
                      auto fwdarg_sr_sr_2_2_1 = fwdarg_sr_1;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul(fwdarg_sr_sr_2_2_0, (gutils->getWidth() == 1) ? fwdarg_sr_sr_2_2_1 : gutils->extractMeta(Builder2, fwdarg_sr_sr_2_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdarg_sr_sr_2_1 : gutils->extractMeta(Builder2, fwdarg_sr_sr_2_1, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_sr_sr_0->getType() : getSubType(fwdarg_sr_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_sr_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_sr_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
 assert(gutils->isConstantValue(call.getOperand(0)));
        if (!dif && !gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing subroutine CFMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing subroutine Conj
                auto revarg_2_0 = ({
                  // Computing subroutine CFMul
                  auto revarg_2_1_0 = ({
                    auto ty = call.getOperand(1)->getType();
                    Value *ret = nullptr;
                    if (auto ST = dyn_cast<StructType>(ty)) {
                      ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "0.5"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                      ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "0.5"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                    } else {
                      llvm::errs() << *ty << "\n";
                      assert(0 && "unhandled cfp");
                    }
                    ret;
                  })
;
                  auto revarg_2_1_1 = ({
                    // Computing subroutine CFAdd
                    auto revarg_2_1_2_0 = ({
                      // Computing Call
                      auto revarg_2_1_2_1_0 = ({
                        // Computing FSub
                        llvm::Value* revarg_2_1_2_1_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                        auto revarg_2_1_2_1_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1");
                        Builder2.CreateFSub(revarg_2_1_2_1_1_0, revarg_2_1_2_1_1_1);
                      });
                      llvm::Value* revarg_2_1_2_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_1_2_1_0, revarg_2_1_2_1_1})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    auto revarg_2_1_2_1 = ({
                      // Computing Call
                      auto revarg_2_1_2_2_0 = ({
                        // Computing FAdd
                        llvm::Value* revarg_2_1_2_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                        auto revarg_2_1_2_2_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1");
                        Builder2.CreateFAdd(revarg_2_1_2_2_1_0, revarg_2_1_2_2_1_1);
                      });
                      llvm::Value* revarg_2_1_2_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_1_2_2_0, revarg_2_1_2_2_1})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    Value* local_re1 = Builder2.CreateExtractValue(revarg_2_1_2_0, ArrayRef<unsigned>({0}), "re1");
                    Value* local_im1 = Builder2.CreateExtractValue(revarg_2_1_2_0, ArrayRef<unsigned>({1}), "im1");
                    Value* local_re2 = Builder2.CreateExtractValue(revarg_2_1_2_1, ArrayRef<unsigned>({0}), "re2");
                    Value* local_im2 = Builder2.CreateExtractValue(revarg_2_1_2_1, ArrayRef<unsigned>({1}), "im2");
                    ({
                      auto revarg_2_1_2_sr_0 = ({
                        // Computing FAdd
                        auto revarg_2_1_2_sr_1_0 = local_re1;
                        auto revarg_2_1_2_sr_1_1 = local_re2;
                        Builder2.CreateFAdd(revarg_2_1_2_sr_1_0, revarg_2_1_2_sr_1_1);
                      });
                      auto revarg_2_1_2_sr_1 = ({
                        // Computing FAdd
                        auto revarg_2_1_2_sr_2_0 = local_im1;
                        auto revarg_2_1_2_sr_2_1 = local_im2;
                        Builder2.CreateFAdd(revarg_2_1_2_sr_2_0, revarg_2_1_2_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(revarg_2_1_2_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, revarg_2_1_2_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, revarg_2_1_2_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                  Value* local_re1 = Builder2.CreateExtractValue(revarg_2_1_0, ArrayRef<unsigned>({0}), "re1");
                  Value* local_im1 = Builder2.CreateExtractValue(revarg_2_1_0, ArrayRef<unsigned>({1}), "im1");
                  Value* local_re2 = Builder2.CreateExtractValue(revarg_2_1_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(revarg_2_1_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto revarg_2_1_sr_0 = local_re1;
                    auto revarg_2_1_sr_1 = local_im1;
                    auto revarg_2_1_sr_2 = local_re2;
                    auto revarg_2_1_sr_3 = local_im2;
                    ({
                      auto revarg_2_1_sr_sr_0 = ({
                        // Computing FSub
                        auto revarg_2_1_sr_sr_1_0 = ({
                          // Computing FMul
                          llvm::Value* revarg_2_1_sr_sr_1_1_0 = revarg_2_1_sr_0;
                          llvm::Value* revarg_2_1_sr_sr_1_1_1 = revarg_2_1_sr_2;
                          Builder2.CreateFMul(revarg_2_1_sr_sr_1_1_0, revarg_2_1_sr_sr_1_1_1);
                        });
                        auto revarg_2_1_sr_sr_1_1 = ({
                          // Computing FMul
                          llvm::Value* revarg_2_1_sr_sr_1_2_0 = revarg_2_1_sr_1;
                          llvm::Value* revarg_2_1_sr_sr_1_2_1 = revarg_2_1_sr_3;
                          Builder2.CreateFMul(revarg_2_1_sr_sr_1_2_0, revarg_2_1_sr_sr_1_2_1);
                        });
                        Builder2.CreateFSub(revarg_2_1_sr_sr_1_0, revarg_2_1_sr_sr_1_1);
                      });
                      auto revarg_2_1_sr_sr_1 = ({
                        // Computing FAdd
                        auto revarg_2_1_sr_sr_2_0 = ({
                          // Computing FMul
                          llvm::Value* revarg_2_1_sr_sr_2_1_0 = revarg_2_1_sr_0;
                          llvm::Value* revarg_2_1_sr_sr_2_1_1 = revarg_2_1_sr_3;
                          Builder2.CreateFMul(revarg_2_1_sr_sr_2_1_0, revarg_2_1_sr_sr_2_1_1);
                        });
                        auto revarg_2_1_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* revarg_2_1_sr_sr_2_2_0 = revarg_2_1_sr_2;
                          llvm::Value* revarg_2_1_sr_sr_2_2_1 = revarg_2_1_sr_1;
                          Builder2.CreateFMul(revarg_2_1_sr_sr_2_2_0, revarg_2_1_sr_sr_2_2_1);
                        });
                        Builder2.CreateFAdd(revarg_2_1_sr_sr_2_0, revarg_2_1_sr_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(revarg_2_1_sr_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, revarg_2_1_sr_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, revarg_2_1_sr_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re = Builder2.CreateExtractValue(revarg_2_0, ArrayRef<unsigned>({0}), "re");
                Value* local_im = Builder2.CreateExtractValue(revarg_2_0, ArrayRef<unsigned>({1}), "im");
                ({
                  auto revarg_2_sr_0 = local_re;
                  auto revarg_2_sr_1 = ({
                    // Computing FNeg
                    auto revarg_2_sr_2_0 = local_im;
                    Builder2.CreateFNeg(revarg_2_sr_2_0);
                  });
                  Value *res = UndefValue::get(ArrayType::get(revarg_2_sr_0->getType(), 2));
                  {
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, revarg_2_sr_0, idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, revarg_2_sr_1, idxs);
                    }
                  }
                   res;
                });
              });
              Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_re2 = Builder2.CreateExtractValue(revarg_1, ArrayRef<unsigned>({0}), "re2");
              Value* local_im2 = Builder2.CreateExtractValue(revarg_1, ArrayRef<unsigned>({1}), "im2");
              ({
                // Computing subroutine CFMul_splat
                auto revarg_sr_0 = local_re1;
                auto revarg_sr_1 = local_im1;
                auto revarg_sr_2 = local_re2;
                auto revarg_sr_3 = local_im2;
                ({
                  auto revarg_sr_sr_0 = ({
                    // Computing FSub
                    auto revarg_sr_sr_1_0 = ({
                      // Computing FMul
                      auto revarg_sr_sr_1_1_0 = revarg_sr_0;
                      llvm::Value* revarg_sr_sr_1_1_1 = revarg_sr_2;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_sr_sr_1_1_0, idx), revarg_sr_sr_1_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto revarg_sr_sr_1_1 = ({
                      // Computing FMul
                      auto revarg_sr_sr_1_2_0 = revarg_sr_1;
                      llvm::Value* revarg_sr_sr_1_2_1 = revarg_sr_3;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_sr_sr_1_2_0 : gutils->extractMeta(Builder2, revarg_sr_sr_1_2_0, idx), revarg_sr_sr_1_2_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? revarg_sr_sr_1_1 : gutils->extractMeta(Builder2, revarg_sr_sr_1_1, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  auto revarg_sr_sr_1 = ({
                    // Computing FAdd
                    auto revarg_sr_sr_2_0 = ({
                      // Computing FMul
                      auto revarg_sr_sr_2_1_0 = revarg_sr_0;
                      llvm::Value* revarg_sr_sr_2_1_1 = revarg_sr_3;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_sr_sr_2_1_0, idx), revarg_sr_sr_2_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto revarg_sr_sr_2_1 = ({
                      // Computing FMul
                      llvm::Value* revarg_sr_sr_2_2_0 = revarg_sr_2;
                      auto revarg_sr_sr_2_2_1 = revarg_sr_1;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul(revarg_sr_sr_2_2_0, (gutils->getWidth() == 1) ? revarg_sr_sr_2_2_1 : gutils->extractMeta(Builder2, revarg_sr_sr_2_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? revarg_sr_sr_2_1 : gutils->extractMeta(Builder2, revarg_sr_sr_2_1, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_sr_sr_0->getType() : getSubType(revarg_sr_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_sr_sr_0 : gutils->extractMeta(Builder2, revarg_sr_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_sr_sr_1 : gutils->extractMeta(Builder2, revarg_sr_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(1)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(1), toadd, Builder2, call.getOperand(1)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "cmplx_jn" ||
      funcName == "cmplx_yn") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
            Value *res = ({
              // Computing subroutine CFMul
              auto fwdnsrarg_0 = gutils->invertPointerM(call.getOperand(1), Builder2, TR.query(&call));
              auto fwdnsrarg_1 = ({
                // Computing subroutine CFMul
                auto fwdnsrarg_2_0 = ({
                  auto ty = call.getOperand(1)->getType();
                  Value *ret = nullptr;
                  if (auto ST = dyn_cast<StructType>(ty)) {
                    ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "0.5"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                    ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "0.5"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                  } else {
                    llvm::errs() << *ty << "\n";
                    assert(0 && "unhandled cfp");
                  }
                  ret;
                })
;
                auto fwdnsrarg_2_1 = ({
                  // Computing subroutine CFSub
                  auto fwdnsrarg_2_2_0 = ({
                    // Computing Call
                    auto fwdnsrarg_2_2_1_0 = ({
                      // Computing FSub
                      llvm::Value* fwdnsrarg_2_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto fwdnsrarg_2_2_1_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1");
                      Builder2.CreateFSub(fwdnsrarg_2_2_1_1_0, fwdnsrarg_2_2_1_1_1);
                    });
                    llvm::Value* fwdnsrarg_2_2_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdnsrarg_2_2_1_0, fwdnsrarg_2_2_1_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  auto fwdnsrarg_2_2_1 = ({
                    // Computing Call
                    auto fwdnsrarg_2_2_2_0 = ({
                      // Computing FAdd
                      llvm::Value* fwdnsrarg_2_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto fwdnsrarg_2_2_2_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1");
                      Builder2.CreateFAdd(fwdnsrarg_2_2_2_1_0, fwdnsrarg_2_2_2_1_1);
                    });
                    llvm::Value* fwdnsrarg_2_2_2_1 = gutils->getNewFromOriginal(call.getOperand(1));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdnsrarg_2_2_2_0, fwdnsrarg_2_2_2_1})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Value* local_re1 = Builder2.CreateExtractValue(fwdnsrarg_2_2_0, ArrayRef<unsigned>({0}), "re1");
                  Value* local_im1 = Builder2.CreateExtractValue(fwdnsrarg_2_2_0, ArrayRef<unsigned>({1}), "im1");
                  Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_2_2_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_2_2_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    auto fwdnsrarg_2_2_sr_0 = ({
                      // Computing FSub
                      auto fwdnsrarg_2_2_sr_1_0 = local_re1;
                      auto fwdnsrarg_2_2_sr_1_1 = local_re2;
                      Builder2.CreateFSub(fwdnsrarg_2_2_sr_1_0, fwdnsrarg_2_2_sr_1_1);
                    });
                    auto fwdnsrarg_2_2_sr_1 = ({
                      // Computing FSub
                      auto fwdnsrarg_2_2_sr_2_0 = local_im1;
                      auto fwdnsrarg_2_2_sr_2_1 = local_im2;
                      Builder2.CreateFSub(fwdnsrarg_2_2_sr_2_0, fwdnsrarg_2_2_sr_2_1);
                    });
                    Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_2_2_sr_0->getType(), 2));
                    {
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, fwdnsrarg_2_2_sr_0, idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, fwdnsrarg_2_2_sr_1, idxs);
                      }
                    }
                     res;
                  });
                });
                Value* local_re1 = Builder2.CreateExtractValue(fwdnsrarg_2_0, ArrayRef<unsigned>({0}), "re1");
                Value* local_im1 = Builder2.CreateExtractValue(fwdnsrarg_2_0, ArrayRef<unsigned>({1}), "im1");
                Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_2_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_2_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFMul_splat
                  auto fwdnsrarg_2_sr_0 = local_re1;
                  auto fwdnsrarg_2_sr_1 = local_im1;
                  auto fwdnsrarg_2_sr_2 = local_re2;
                  auto fwdnsrarg_2_sr_3 = local_im2;
                  ({
                    auto fwdnsrarg_2_sr_sr_0 = ({
                      // Computing FSub
                      auto fwdnsrarg_2_sr_sr_1_0 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_2_sr_sr_1_1_0 = fwdnsrarg_2_sr_0;
                        llvm::Value* fwdnsrarg_2_sr_sr_1_1_1 = fwdnsrarg_2_sr_2;
                        Builder2.CreateFMul(fwdnsrarg_2_sr_sr_1_1_0, fwdnsrarg_2_sr_sr_1_1_1);
                      });
                      auto fwdnsrarg_2_sr_sr_1_1 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_2_sr_sr_1_2_0 = fwdnsrarg_2_sr_1;
                        llvm::Value* fwdnsrarg_2_sr_sr_1_2_1 = fwdnsrarg_2_sr_3;
                        Builder2.CreateFMul(fwdnsrarg_2_sr_sr_1_2_0, fwdnsrarg_2_sr_sr_1_2_1);
                      });
                      Builder2.CreateFSub(fwdnsrarg_2_sr_sr_1_0, fwdnsrarg_2_sr_sr_1_1);
                    });
                    auto fwdnsrarg_2_sr_sr_1 = ({
                      // Computing FAdd
                      auto fwdnsrarg_2_sr_sr_2_0 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_2_sr_sr_2_1_0 = fwdnsrarg_2_sr_0;
                        llvm::Value* fwdnsrarg_2_sr_sr_2_1_1 = fwdnsrarg_2_sr_3;
                        Builder2.CreateFMul(fwdnsrarg_2_sr_sr_2_1_0, fwdnsrarg_2_sr_sr_2_1_1);
                      });
                      auto fwdnsrarg_2_sr_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_2_sr_sr_2_2_0 = fwdnsrarg_2_sr_2;
                        llvm::Value* fwdnsrarg_2_sr_sr_2_2_1 = fwdnsrarg_2_sr_1;
                        Builder2.CreateFMul(fwdnsrarg_2_sr_sr_2_2_0, fwdnsrarg_2_sr_sr_2_2_1);
                      });
                      Builder2.CreateFAdd(fwdnsrarg_2_sr_sr_2_0, fwdnsrarg_2_sr_sr_2_1);
                    });
                    Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_2_sr_sr_0->getType(), 2));
                    {
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, fwdnsrarg_2_sr_sr_0, idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, fwdnsrarg_2_sr_sr_1, idxs);
                      }
                    }
                     res;
                  });
                });
              });
              Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_1, ArrayRef<unsigned>({0}), "re2");
              Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_1, ArrayRef<unsigned>({1}), "im2");
              ({
                // Computing subroutine CFMul_splat
                auto fwdnsrarg_sr_0 = local_re1;
                auto fwdnsrarg_sr_1 = local_im1;
                auto fwdnsrarg_sr_2 = local_re2;
                auto fwdnsrarg_sr_3 = local_im2;
                ({
                  auto fwdnsrarg_sr_sr_0 = ({
                    // Computing FSub
                    auto fwdnsrarg_sr_sr_1_0 = ({
                      // Computing FMul
                      auto fwdnsrarg_sr_sr_1_1_0 = fwdnsrarg_sr_0;
                      llvm::Value* fwdnsrarg_sr_sr_1_1_1 = fwdnsrarg_sr_2;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_1_1_0, idx), fwdnsrarg_sr_sr_1_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdnsrarg_sr_sr_1_1 = ({
                      // Computing FMul
                      auto fwdnsrarg_sr_sr_1_2_0 = fwdnsrarg_sr_1;
                      llvm::Value* fwdnsrarg_sr_sr_1_2_1 = fwdnsrarg_sr_3;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_1_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_1_2_0, idx), fwdnsrarg_sr_sr_1_2_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_1_1, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  auto fwdnsrarg_sr_sr_1 = ({
                    // Computing FAdd
                    auto fwdnsrarg_sr_sr_2_0 = ({
                      // Computing FMul
                      auto fwdnsrarg_sr_sr_2_1_0 = fwdnsrarg_sr_0;
                      llvm::Value* fwdnsrarg_sr_sr_2_1_1 = fwdnsrarg_sr_3;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_2_1_0, idx), fwdnsrarg_sr_sr_2_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdnsrarg_sr_sr_2_1 = ({
                      // Computing FMul
                      llvm::Value* fwdnsrarg_sr_sr_2_2_0 = fwdnsrarg_sr_2;
                      auto fwdnsrarg_sr_sr_2_2_1 = fwdnsrarg_sr_1;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul(fwdnsrarg_sr_sr_2_2_0, (gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_2_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_2_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_2_1, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_0->getType() : getSubType(fwdnsrarg_sr_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
            });
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
 assert(gutils->isConstantValue(call.getOperand(0)));
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(1))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing subroutine CFMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing subroutine Conj
                auto fwdarg_2_0 = ({
                  // Computing subroutine CFMul
                  auto fwdarg_2_1_0 = ({
                    auto ty = call.getOperand(1)->getType();
                    Value *ret = nullptr;
                    if (auto ST = dyn_cast<StructType>(ty)) {
                      ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "0.5"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                      ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "0.5"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                    } else {
                      llvm::errs() << *ty << "\n";
                      assert(0 && "unhandled cfp");
                    }
                    ret;
                  })
;
                  auto fwdarg_2_1_1 = ({
                    // Computing subroutine CFSub
                    auto fwdarg_2_1_2_0 = ({
                      // Computing Call
                      auto fwdarg_2_1_2_1_0 = ({
                        // Computing FSub
                        llvm::Value* fwdarg_2_1_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                        auto fwdarg_2_1_2_1_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1");
                        Builder2.CreateFSub(fwdarg_2_1_2_1_1_0, fwdarg_2_1_2_1_1_1);
                      });
                      llvm::Value* fwdarg_2_1_2_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_2_1_0, fwdarg_2_1_2_1_1})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    auto fwdarg_2_1_2_1 = ({
                      // Computing Call
                      auto fwdarg_2_1_2_2_0 = ({
                        // Computing FAdd
                        llvm::Value* fwdarg_2_1_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                        auto fwdarg_2_1_2_2_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1");
                        Builder2.CreateFAdd(fwdarg_2_1_2_2_1_0, fwdarg_2_1_2_2_1_1);
                      });
                      llvm::Value* fwdarg_2_1_2_2_1 = gutils->getNewFromOriginal(call.getOperand(1));
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_1_2_2_0, fwdarg_2_1_2_2_1})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    Value* local_re1 = Builder2.CreateExtractValue(fwdarg_2_1_2_0, ArrayRef<unsigned>({0}), "re1");
                    Value* local_im1 = Builder2.CreateExtractValue(fwdarg_2_1_2_0, ArrayRef<unsigned>({1}), "im1");
                    Value* local_re2 = Builder2.CreateExtractValue(fwdarg_2_1_2_1, ArrayRef<unsigned>({0}), "re2");
                    Value* local_im2 = Builder2.CreateExtractValue(fwdarg_2_1_2_1, ArrayRef<unsigned>({1}), "im2");
                    ({
                      auto fwdarg_2_1_2_sr_0 = ({
                        // Computing FSub
                        auto fwdarg_2_1_2_sr_1_0 = local_re1;
                        auto fwdarg_2_1_2_sr_1_1 = local_re2;
                        Builder2.CreateFSub(fwdarg_2_1_2_sr_1_0, fwdarg_2_1_2_sr_1_1);
                      });
                      auto fwdarg_2_1_2_sr_1 = ({
                        // Computing FSub
                        auto fwdarg_2_1_2_sr_2_0 = local_im1;
                        auto fwdarg_2_1_2_sr_2_1 = local_im2;
                        Builder2.CreateFSub(fwdarg_2_1_2_sr_2_0, fwdarg_2_1_2_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(fwdarg_2_1_2_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, fwdarg_2_1_2_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, fwdarg_2_1_2_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                  Value* local_re1 = Builder2.CreateExtractValue(fwdarg_2_1_0, ArrayRef<unsigned>({0}), "re1");
                  Value* local_im1 = Builder2.CreateExtractValue(fwdarg_2_1_0, ArrayRef<unsigned>({1}), "im1");
                  Value* local_re2 = Builder2.CreateExtractValue(fwdarg_2_1_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(fwdarg_2_1_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto fwdarg_2_1_sr_0 = local_re1;
                    auto fwdarg_2_1_sr_1 = local_im1;
                    auto fwdarg_2_1_sr_2 = local_re2;
                    auto fwdarg_2_1_sr_3 = local_im2;
                    ({
                      auto fwdarg_2_1_sr_sr_0 = ({
                        // Computing FSub
                        auto fwdarg_2_1_sr_sr_1_0 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_2_1_sr_sr_1_1_0 = fwdarg_2_1_sr_0;
                          llvm::Value* fwdarg_2_1_sr_sr_1_1_1 = fwdarg_2_1_sr_2;
                          Builder2.CreateFMul(fwdarg_2_1_sr_sr_1_1_0, fwdarg_2_1_sr_sr_1_1_1);
                        });
                        auto fwdarg_2_1_sr_sr_1_1 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_2_1_sr_sr_1_2_0 = fwdarg_2_1_sr_1;
                          llvm::Value* fwdarg_2_1_sr_sr_1_2_1 = fwdarg_2_1_sr_3;
                          Builder2.CreateFMul(fwdarg_2_1_sr_sr_1_2_0, fwdarg_2_1_sr_sr_1_2_1);
                        });
                        Builder2.CreateFSub(fwdarg_2_1_sr_sr_1_0, fwdarg_2_1_sr_sr_1_1);
                      });
                      auto fwdarg_2_1_sr_sr_1 = ({
                        // Computing FAdd
                        auto fwdarg_2_1_sr_sr_2_0 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_2_1_sr_sr_2_1_0 = fwdarg_2_1_sr_0;
                          llvm::Value* fwdarg_2_1_sr_sr_2_1_1 = fwdarg_2_1_sr_3;
                          Builder2.CreateFMul(fwdarg_2_1_sr_sr_2_1_0, fwdarg_2_1_sr_sr_2_1_1);
                        });
                        auto fwdarg_2_1_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_2_1_sr_sr_2_2_0 = fwdarg_2_1_sr_2;
                          llvm::Value* fwdarg_2_1_sr_sr_2_2_1 = fwdarg_2_1_sr_1;
                          Builder2.CreateFMul(fwdarg_2_1_sr_sr_2_2_0, fwdarg_2_1_sr_sr_2_2_1);
                        });
                        Builder2.CreateFAdd(fwdarg_2_1_sr_sr_2_0, fwdarg_2_1_sr_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(fwdarg_2_1_sr_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, fwdarg_2_1_sr_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, fwdarg_2_1_sr_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re = Builder2.CreateExtractValue(fwdarg_2_0, ArrayRef<unsigned>({0}), "re");
                Value* local_im = Builder2.CreateExtractValue(fwdarg_2_0, ArrayRef<unsigned>({1}), "im");
                ({
                  auto fwdarg_2_sr_0 = local_re;
                  auto fwdarg_2_sr_1 = ({
                    // Computing FNeg
                    auto fwdarg_2_sr_2_0 = local_im;
                    Builder2.CreateFNeg(fwdarg_2_sr_2_0);
                  });
                  Value *res = UndefValue::get(ArrayType::get(fwdarg_2_sr_0->getType(), 2));
                  {
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, fwdarg_2_sr_0, idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, fwdarg_2_sr_1, idxs);
                    }
                  }
                   res;
                });
              });
              Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1, ArrayRef<unsigned>({0}), "re2");
              Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1, ArrayRef<unsigned>({1}), "im2");
              ({
                // Computing subroutine CFMul_splat
                auto fwdarg_sr_0 = local_re1;
                auto fwdarg_sr_1 = local_im1;
                auto fwdarg_sr_2 = local_re2;
                auto fwdarg_sr_3 = local_im2;
                ({
                  auto fwdarg_sr_sr_0 = ({
                    // Computing FSub
                    auto fwdarg_sr_sr_1_0 = ({
                      // Computing FMul
                      auto fwdarg_sr_sr_1_1_0 = fwdarg_sr_0;
                      llvm::Value* fwdarg_sr_sr_1_1_1 = fwdarg_sr_2;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_sr_sr_1_1_0, idx), fwdarg_sr_sr_1_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdarg_sr_sr_1_1 = ({
                      // Computing FMul
                      auto fwdarg_sr_sr_1_2_0 = fwdarg_sr_1;
                      llvm::Value* fwdarg_sr_sr_1_2_1 = fwdarg_sr_3;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_sr_sr_1_2_0 : gutils->extractMeta(Builder2, fwdarg_sr_sr_1_2_0, idx), fwdarg_sr_sr_1_2_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_sr_sr_1_1 : gutils->extractMeta(Builder2, fwdarg_sr_sr_1_1, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  auto fwdarg_sr_sr_1 = ({
                    // Computing FAdd
                    auto fwdarg_sr_sr_2_0 = ({
                      // Computing FMul
                      auto fwdarg_sr_sr_2_1_0 = fwdarg_sr_0;
                      llvm::Value* fwdarg_sr_sr_2_1_1 = fwdarg_sr_3;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_sr_sr_2_1_0, idx), fwdarg_sr_sr_2_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdarg_sr_sr_2_1 = ({
                      // Computing FMul
                      llvm::Value* fwdarg_sr_sr_2_2_0 = fwdarg_sr_2;
                      auto fwdarg_sr_sr_2_2_1 = fwdarg_sr_1;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul(fwdarg_sr_sr_2_2_0, (gutils->getWidth() == 1) ? fwdarg_sr_sr_2_2_1 : gutils->extractMeta(Builder2, fwdarg_sr_sr_2_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdarg_sr_sr_2_1 : gutils->extractMeta(Builder2, fwdarg_sr_sr_2_1, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_sr_sr_0->getType() : getSubType(fwdarg_sr_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_sr_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_sr_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
 assert(gutils->isConstantValue(call.getOperand(0)));
        if (!dif && !gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing subroutine CFMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing subroutine Conj
                auto revarg_2_0 = ({
                  // Computing subroutine CFMul
                  auto revarg_2_1_0 = ({
                    auto ty = call.getOperand(1)->getType();
                    Value *ret = nullptr;
                    if (auto ST = dyn_cast<StructType>(ty)) {
                      ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "0.5"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                      ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "0.5"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                    } else {
                      llvm::errs() << *ty << "\n";
                      assert(0 && "unhandled cfp");
                    }
                    ret;
                  })
;
                  auto revarg_2_1_1 = ({
                    // Computing subroutine CFSub
                    auto revarg_2_1_2_0 = ({
                      // Computing Call
                      auto revarg_2_1_2_1_0 = ({
                        // Computing FSub
                        llvm::Value* revarg_2_1_2_1_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                        auto revarg_2_1_2_1_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1");
                        Builder2.CreateFSub(revarg_2_1_2_1_1_0, revarg_2_1_2_1_1_1);
                      });
                      llvm::Value* revarg_2_1_2_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_1_2_1_0, revarg_2_1_2_1_1})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    auto revarg_2_1_2_1 = ({
                      // Computing Call
                      auto revarg_2_1_2_2_0 = ({
                        // Computing FAdd
                        llvm::Value* revarg_2_1_2_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                        auto revarg_2_1_2_2_1_1 = ConstantFP::get(call.getOperand(0)->getType(), "1");
                        Builder2.CreateFAdd(revarg_2_1_2_2_1_0, revarg_2_1_2_2_1_1);
                      });
                      llvm::Value* revarg_2_1_2_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_1_2_2_0, revarg_2_1_2_2_1})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    Value* local_re1 = Builder2.CreateExtractValue(revarg_2_1_2_0, ArrayRef<unsigned>({0}), "re1");
                    Value* local_im1 = Builder2.CreateExtractValue(revarg_2_1_2_0, ArrayRef<unsigned>({1}), "im1");
                    Value* local_re2 = Builder2.CreateExtractValue(revarg_2_1_2_1, ArrayRef<unsigned>({0}), "re2");
                    Value* local_im2 = Builder2.CreateExtractValue(revarg_2_1_2_1, ArrayRef<unsigned>({1}), "im2");
                    ({
                      auto revarg_2_1_2_sr_0 = ({
                        // Computing FSub
                        auto revarg_2_1_2_sr_1_0 = local_re1;
                        auto revarg_2_1_2_sr_1_1 = local_re2;
                        Builder2.CreateFSub(revarg_2_1_2_sr_1_0, revarg_2_1_2_sr_1_1);
                      });
                      auto revarg_2_1_2_sr_1 = ({
                        // Computing FSub
                        auto revarg_2_1_2_sr_2_0 = local_im1;
                        auto revarg_2_1_2_sr_2_1 = local_im2;
                        Builder2.CreateFSub(revarg_2_1_2_sr_2_0, revarg_2_1_2_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(revarg_2_1_2_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, revarg_2_1_2_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, revarg_2_1_2_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                  Value* local_re1 = Builder2.CreateExtractValue(revarg_2_1_0, ArrayRef<unsigned>({0}), "re1");
                  Value* local_im1 = Builder2.CreateExtractValue(revarg_2_1_0, ArrayRef<unsigned>({1}), "im1");
                  Value* local_re2 = Builder2.CreateExtractValue(revarg_2_1_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(revarg_2_1_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto revarg_2_1_sr_0 = local_re1;
                    auto revarg_2_1_sr_1 = local_im1;
                    auto revarg_2_1_sr_2 = local_re2;
                    auto revarg_2_1_sr_3 = local_im2;
                    ({
                      auto revarg_2_1_sr_sr_0 = ({
                        // Computing FSub
                        auto revarg_2_1_sr_sr_1_0 = ({
                          // Computing FMul
                          llvm::Value* revarg_2_1_sr_sr_1_1_0 = revarg_2_1_sr_0;
                          llvm::Value* revarg_2_1_sr_sr_1_1_1 = revarg_2_1_sr_2;
                          Builder2.CreateFMul(revarg_2_1_sr_sr_1_1_0, revarg_2_1_sr_sr_1_1_1);
                        });
                        auto revarg_2_1_sr_sr_1_1 = ({
                          // Computing FMul
                          llvm::Value* revarg_2_1_sr_sr_1_2_0 = revarg_2_1_sr_1;
                          llvm::Value* revarg_2_1_sr_sr_1_2_1 = revarg_2_1_sr_3;
                          Builder2.CreateFMul(revarg_2_1_sr_sr_1_2_0, revarg_2_1_sr_sr_1_2_1);
                        });
                        Builder2.CreateFSub(revarg_2_1_sr_sr_1_0, revarg_2_1_sr_sr_1_1);
                      });
                      auto revarg_2_1_sr_sr_1 = ({
                        // Computing FAdd
                        auto revarg_2_1_sr_sr_2_0 = ({
                          // Computing FMul
                          llvm::Value* revarg_2_1_sr_sr_2_1_0 = revarg_2_1_sr_0;
                          llvm::Value* revarg_2_1_sr_sr_2_1_1 = revarg_2_1_sr_3;
                          Builder2.CreateFMul(revarg_2_1_sr_sr_2_1_0, revarg_2_1_sr_sr_2_1_1);
                        });
                        auto revarg_2_1_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* revarg_2_1_sr_sr_2_2_0 = revarg_2_1_sr_2;
                          llvm::Value* revarg_2_1_sr_sr_2_2_1 = revarg_2_1_sr_1;
                          Builder2.CreateFMul(revarg_2_1_sr_sr_2_2_0, revarg_2_1_sr_sr_2_2_1);
                        });
                        Builder2.CreateFAdd(revarg_2_1_sr_sr_2_0, revarg_2_1_sr_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(revarg_2_1_sr_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, revarg_2_1_sr_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, revarg_2_1_sr_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re = Builder2.CreateExtractValue(revarg_2_0, ArrayRef<unsigned>({0}), "re");
                Value* local_im = Builder2.CreateExtractValue(revarg_2_0, ArrayRef<unsigned>({1}), "im");
                ({
                  auto revarg_2_sr_0 = local_re;
                  auto revarg_2_sr_1 = ({
                    // Computing FNeg
                    auto revarg_2_sr_2_0 = local_im;
                    Builder2.CreateFNeg(revarg_2_sr_2_0);
                  });
                  Value *res = UndefValue::get(ArrayType::get(revarg_2_sr_0->getType(), 2));
                  {
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, revarg_2_sr_0, idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, revarg_2_sr_1, idxs);
                    }
                  }
                   res;
                });
              });
              Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_re2 = Builder2.CreateExtractValue(revarg_1, ArrayRef<unsigned>({0}), "re2");
              Value* local_im2 = Builder2.CreateExtractValue(revarg_1, ArrayRef<unsigned>({1}), "im2");
              ({
                // Computing subroutine CFMul_splat
                auto revarg_sr_0 = local_re1;
                auto revarg_sr_1 = local_im1;
                auto revarg_sr_2 = local_re2;
                auto revarg_sr_3 = local_im2;
                ({
                  auto revarg_sr_sr_0 = ({
                    // Computing FSub
                    auto revarg_sr_sr_1_0 = ({
                      // Computing FMul
                      auto revarg_sr_sr_1_1_0 = revarg_sr_0;
                      llvm::Value* revarg_sr_sr_1_1_1 = revarg_sr_2;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_sr_sr_1_1_0, idx), revarg_sr_sr_1_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto revarg_sr_sr_1_1 = ({
                      // Computing FMul
                      auto revarg_sr_sr_1_2_0 = revarg_sr_1;
                      llvm::Value* revarg_sr_sr_1_2_1 = revarg_sr_3;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_sr_sr_1_2_0 : gutils->extractMeta(Builder2, revarg_sr_sr_1_2_0, idx), revarg_sr_sr_1_2_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? revarg_sr_sr_1_1 : gutils->extractMeta(Builder2, revarg_sr_sr_1_1, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  auto revarg_sr_sr_1 = ({
                    // Computing FAdd
                    auto revarg_sr_sr_2_0 = ({
                      // Computing FMul
                      auto revarg_sr_sr_2_1_0 = revarg_sr_0;
                      llvm::Value* revarg_sr_sr_2_1_1 = revarg_sr_3;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_sr_sr_2_1_0, idx), revarg_sr_sr_2_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto revarg_sr_sr_2_1 = ({
                      // Computing FMul
                      llvm::Value* revarg_sr_sr_2_2_0 = revarg_sr_2;
                      auto revarg_sr_sr_2_2_1 = revarg_sr_1;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul(revarg_sr_sr_2_2_0, (gutils->getWidth() == 1) ? revarg_sr_sr_2_2_1 : gutils->extractMeta(Builder2, revarg_sr_sr_2_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? revarg_sr_sr_2_1 : gutils->extractMeta(Builder2, revarg_sr_sr_2_1, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_sr_sr_0->getType() : getSubType(revarg_sr_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_sr_sr_0 : gutils->extractMeta(Builder2, revarg_sr_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_sr_sr_1 : gutils->extractMeta(Builder2, revarg_sr_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(1)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(1), toadd, Builder2, call.getOperand(1)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "erf" ||
      funcName == "erff" ||
      funcName == "erfl") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.1283791670955125738961589031215451716881012586580");
                auto fwdarg_2_1 = ({
                  // Computing exp
                  auto fwdarg_2_2_0 = ({
                    // Computing FNeg
                    auto fwdarg_2_2_1_0 = ({
                      // Computing FMul
                      llvm::Value* fwdarg_2_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      llvm::Value* fwdarg_2_2_1_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                      Builder2.CreateFMul(fwdarg_2_2_1_1_0, fwdarg_2_2_1_1_1);
                    });
                    Builder2.CreateFNeg(fwdarg_2_2_1_0);
                  });
                  Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({fwdarg_2_2_0->getType()})), ArrayRef<Value*>({fwdarg_2_2_0}));
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.1283791670955125738961589031215451716881012586580");
                auto fwdarg_2_1 = ({
                  // Computing exp
                  auto fwdarg_2_2_0 = ({
                    // Computing FNeg
                    auto fwdarg_2_2_1_0 = ({
                      // Computing FMul
                      llvm::Value* fwdarg_2_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      llvm::Value* fwdarg_2_2_1_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                      Builder2.CreateFMul(fwdarg_2_2_1_1_0, fwdarg_2_2_1_1_1);
                    });
                    Builder2.CreateFNeg(fwdarg_2_2_1_0);
                  });
                  Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({fwdarg_2_2_0->getType()})), ArrayRef<Value*>({fwdarg_2_2_0}));
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FMul
                auto revarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.1283791670955125738961589031215451716881012586580");
                auto revarg_2_1 = ({
                  // Computing exp
                  auto revarg_2_2_0 = ({
                    // Computing FNeg
                    auto revarg_2_2_1_0 = ({
                      // Computing FMul
                      llvm::Value* revarg_2_2_1_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                      llvm::Value* revarg_2_2_1_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                      Builder2.CreateFMul(revarg_2_2_1_1_0, revarg_2_2_1_1_1);
                    });
                    Builder2.CreateFNeg(revarg_2_2_1_0);
                  });
                  Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({revarg_2_2_0->getType()})), ArrayRef<Value*>({revarg_2_2_0}));
                });
                Builder2.CreateFMul(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "erfi") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.1283791670955125738961589031215451716881012586580");
                auto fwdarg_2_1 = ({
                  // Computing exp
                  auto fwdarg_2_2_0 = ({
                    // Computing FMul
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    llvm::Value* fwdarg_2_2_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    Builder2.CreateFMul(fwdarg_2_2_1_0, fwdarg_2_2_1_1);
                  });
                  Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({fwdarg_2_2_0->getType()})), ArrayRef<Value*>({fwdarg_2_2_0}));
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.1283791670955125738961589031215451716881012586580");
                auto fwdarg_2_1 = ({
                  // Computing exp
                  auto fwdarg_2_2_0 = ({
                    // Computing FMul
                    llvm::Value* fwdarg_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    llvm::Value* fwdarg_2_2_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                    Builder2.CreateFMul(fwdarg_2_2_1_0, fwdarg_2_2_1_1);
                  });
                  Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({fwdarg_2_2_0->getType()})), ArrayRef<Value*>({fwdarg_2_2_0}));
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FMul
                auto revarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "1.1283791670955125738961589031215451716881012586580");
                auto revarg_2_1 = ({
                  // Computing exp
                  auto revarg_2_2_0 = ({
                    // Computing FMul
                    llvm::Value* revarg_2_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    llvm::Value* revarg_2_2_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    Builder2.CreateFMul(revarg_2_2_1_0, revarg_2_2_1_1);
                  });
                  Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({revarg_2_2_0->getType()})), ArrayRef<Value*>({revarg_2_2_0}));
                });
                Builder2.CreateFMul(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "erfc" ||
      funcName == "erfcf" ||
      funcName == "erfcl") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "-1.1283791670955125738961589031215451716881012586580");
                auto fwdarg_2_1 = ({
                  // Computing exp
                  auto fwdarg_2_2_0 = ({
                    // Computing FNeg
                    auto fwdarg_2_2_1_0 = ({
                      // Computing FMul
                      llvm::Value* fwdarg_2_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      llvm::Value* fwdarg_2_2_1_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                      Builder2.CreateFMul(fwdarg_2_2_1_1_0, fwdarg_2_2_1_1_1);
                    });
                    Builder2.CreateFNeg(fwdarg_2_2_1_0);
                  });
                  Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({fwdarg_2_2_0->getType()})), ArrayRef<Value*>({fwdarg_2_2_0}));
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "-1.1283791670955125738961589031215451716881012586580");
                auto fwdarg_2_1 = ({
                  // Computing exp
                  auto fwdarg_2_2_0 = ({
                    // Computing FNeg
                    auto fwdarg_2_2_1_0 = ({
                      // Computing FMul
                      llvm::Value* fwdarg_2_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      llvm::Value* fwdarg_2_2_1_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                      Builder2.CreateFMul(fwdarg_2_2_1_1_0, fwdarg_2_2_1_1_1);
                    });
                    Builder2.CreateFNeg(fwdarg_2_2_1_0);
                  });
                  Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({fwdarg_2_2_0->getType()})), ArrayRef<Value*>({fwdarg_2_2_0}));
                });
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FMul
                auto revarg_2_0 = ConstantFP::get(call.getOperand(0)->getType(), "-1.1283791670955125738961589031215451716881012586580");
                auto revarg_2_1 = ({
                  // Computing exp
                  auto revarg_2_2_0 = ({
                    // Computing FNeg
                    auto revarg_2_2_1_0 = ({
                      // Computing FMul
                      llvm::Value* revarg_2_2_1_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                      llvm::Value* revarg_2_2_1_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                      Builder2.CreateFMul(revarg_2_2_1_1_0, revarg_2_2_1_1_1);
                    });
                    Builder2.CreateFNeg(revarg_2_2_1_0);
                  });
                  Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({revarg_2_2_0->getType()})), ArrayRef<Value*>({revarg_2_2_0}));
                });
                Builder2.CreateFMul(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "erfinv" ||
      funcName == "erfinvf" ||
      funcName == "erfinfvl" ||
      funcName == "__nv_erfinv" ||
      funcName == "__nv_erfinvf" ||
      funcName == "__nv_erfinfvl") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_ei = nullptr;
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing FMul
                auto fwdarg_1_0 = ConstantFP::get(call.getOperand(0)->getType(), "0.8862269254527580136490837416705725913987747280611935641069038949264556422955160906874753283692723327");
                auto fwdarg_1_1 = ({
                  // Computing FMul
                  auto fwdarg_1_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_1_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_1_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_ei = fwdarg_1_2_0;
                  auto fwdarg_1_2_1 = local_ei;
                  Builder2.CreateFMul(fwdarg_1_2_0, fwdarg_1_2_1);
                });
                Builder2.CreateFMul(fwdarg_1_0, fwdarg_1_1);
              });
              auto fwdarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_ei = nullptr;
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = ({
                // Computing FMul
                auto fwdarg_1_0 = ConstantFP::get(call.getOperand(0)->getType(), "0.8862269254527580136490837416705725913987747280611935641069038949264556422955160906874753283692723327");
                auto fwdarg_1_1 = ({
                  // Computing FMul
                  auto fwdarg_1_2_0 = ({
                    // Computing Call
                    llvm::Value* fwdarg_1_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_1_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_ei = fwdarg_1_2_0;
                  auto fwdarg_1_2_1 = local_ei;
                  Builder2.CreateFMul(fwdarg_1_2_0, fwdarg_1_2_1);
                });
                Builder2.CreateFMul(fwdarg_1_0, fwdarg_1_1);
              });
              auto fwdarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(fwdarg_0, (gutils->getWidth() == 1) ? fwdarg_1 : gutils->extractMeta(Builder2, fwdarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          llvm::Value *local_ei = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = ({
                // Computing FMul
                auto revarg_1_0 = ConstantFP::get(call.getOperand(0)->getType(), "0.8862269254527580136490837416705725913987747280611935641069038949264556422955160906874753283692723327");
                auto revarg_1_1 = ({
                  // Computing FMul
                  auto revarg_1_2_0 = ({
                    // Computing Call
                    llvm::Value* revarg_1_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_1_2_1_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  local_ei = revarg_1_2_0;
                  auto revarg_1_2_1 = local_ei;
                  Builder2.CreateFMul(revarg_1_2_0, revarg_1_2_1);
                });
                Builder2.CreateFMul(revarg_1_0, revarg_1_1);
              });
              auto revarg_1 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul(revarg_0, (gutils->getWidth() == 1) ? revarg_1 : gutils->extractMeta(Builder2, revarg_1, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "Faddeeva_erf") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
            Value *res = ({
              // Computing subroutine ToStruct2
              auto fwdnsrarg_0 = ({
                // Computing subroutine CFMul
                auto fwdnsrarg_1_0 = gutils->invertPointerM(call.getOperand(0), Builder2, TR.query(&call));
                auto fwdnsrarg_1_1 = ({
                  // Computing subroutine CFMul
                  auto fwdnsrarg_1_2_0 = ({
                    auto ty = call.getOperand(0)->getType();
                    Value *ret = nullptr;
                    if (auto ST = dyn_cast<StructType>(ty)) {
                      ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                      ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                    } else {
                      llvm::errs() << *ty << "\n";
                      assert(0 && "unhandled cfp");
                    }
                    ret;
                  })
;
                  auto fwdnsrarg_1_2_1 = ({
                    // Computing subroutine CFExp
                    auto fwdnsrarg_1_2_2_0 = ({
                      // Computing subroutine CFNeg
                      auto fwdnsrarg_1_2_2_1_0 = ({
                        // Computing subroutine CFMul
                        auto fwdnsrarg_1_2_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                        auto fwdnsrarg_1_2_2_1_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                        Value* local_re1 = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_1_1_0, ArrayRef<unsigned>({0}), "re1");
                        Value* local_im1 = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_1_1_0, ArrayRef<unsigned>({1}), "im1");
                        Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_1_1_1, ArrayRef<unsigned>({0}), "re2");
                        Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_1_1_1, ArrayRef<unsigned>({1}), "im2");
                        ({
                          // Computing subroutine CFMul_splat
                          auto fwdnsrarg_1_2_2_1_1_sr_0 = local_re1;
                          auto fwdnsrarg_1_2_2_1_1_sr_1 = local_im1;
                          auto fwdnsrarg_1_2_2_1_1_sr_2 = local_re2;
                          auto fwdnsrarg_1_2_2_1_1_sr_3 = local_im2;
                          ({
                            auto fwdnsrarg_1_2_2_1_1_sr_sr_0 = ({
                              // Computing FSub
                              auto fwdnsrarg_1_2_2_1_1_sr_sr_1_0 = ({
                                // Computing FMul
                                llvm::Value* fwdnsrarg_1_2_2_1_1_sr_sr_1_1_0 = fwdnsrarg_1_2_2_1_1_sr_0;
                                llvm::Value* fwdnsrarg_1_2_2_1_1_sr_sr_1_1_1 = fwdnsrarg_1_2_2_1_1_sr_2;
                                Builder2.CreateFMul(fwdnsrarg_1_2_2_1_1_sr_sr_1_1_0, fwdnsrarg_1_2_2_1_1_sr_sr_1_1_1);
                              });
                              auto fwdnsrarg_1_2_2_1_1_sr_sr_1_1 = ({
                                // Computing FMul
                                llvm::Value* fwdnsrarg_1_2_2_1_1_sr_sr_1_2_0 = fwdnsrarg_1_2_2_1_1_sr_1;
                                llvm::Value* fwdnsrarg_1_2_2_1_1_sr_sr_1_2_1 = fwdnsrarg_1_2_2_1_1_sr_3;
                                Builder2.CreateFMul(fwdnsrarg_1_2_2_1_1_sr_sr_1_2_0, fwdnsrarg_1_2_2_1_1_sr_sr_1_2_1);
                              });
                              Builder2.CreateFSub(fwdnsrarg_1_2_2_1_1_sr_sr_1_0, fwdnsrarg_1_2_2_1_1_sr_sr_1_1);
                            });
                            auto fwdnsrarg_1_2_2_1_1_sr_sr_1 = ({
                              // Computing FAdd
                              auto fwdnsrarg_1_2_2_1_1_sr_sr_2_0 = ({
                                // Computing FMul
                                llvm::Value* fwdnsrarg_1_2_2_1_1_sr_sr_2_1_0 = fwdnsrarg_1_2_2_1_1_sr_0;
                                llvm::Value* fwdnsrarg_1_2_2_1_1_sr_sr_2_1_1 = fwdnsrarg_1_2_2_1_1_sr_3;
                                Builder2.CreateFMul(fwdnsrarg_1_2_2_1_1_sr_sr_2_1_0, fwdnsrarg_1_2_2_1_1_sr_sr_2_1_1);
                              });
                              auto fwdnsrarg_1_2_2_1_1_sr_sr_2_1 = ({
                                // Computing FMul
                                llvm::Value* fwdnsrarg_1_2_2_1_1_sr_sr_2_2_0 = fwdnsrarg_1_2_2_1_1_sr_2;
                                llvm::Value* fwdnsrarg_1_2_2_1_1_sr_sr_2_2_1 = fwdnsrarg_1_2_2_1_1_sr_1;
                                Builder2.CreateFMul(fwdnsrarg_1_2_2_1_1_sr_sr_2_2_0, fwdnsrarg_1_2_2_1_1_sr_sr_2_2_1);
                              });
                              Builder2.CreateFAdd(fwdnsrarg_1_2_2_1_1_sr_sr_2_0, fwdnsrarg_1_2_2_1_1_sr_sr_2_1);
                            });
                            Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_1_2_2_1_1_sr_sr_0->getType(), 2));
                            {
                              {
                                std::vector<unsigned> idxs;
                                idxs.push_back(0);
                                res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_2_1_1_sr_sr_0, idxs);
                              }
                              {
                                std::vector<unsigned> idxs;
                                idxs.push_back(1);
                                res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_2_1_1_sr_sr_1, idxs);
                              }
                            }
                             res;
                          });
                        });
                      });
                      Value* local_re = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_1_0, ArrayRef<unsigned>({0}), "re");
                      Value* local_im = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_1_0, ArrayRef<unsigned>({1}), "im");
                      ({
                        auto fwdnsrarg_1_2_2_1_sr_0 = ({
                          // Computing FNeg
                          auto fwdnsrarg_1_2_2_1_sr_1_0 = local_re;
                          Builder2.CreateFNeg(fwdnsrarg_1_2_2_1_sr_1_0);
                        });
                        auto fwdnsrarg_1_2_2_1_sr_1 = ({
                          // Computing FNeg
                          auto fwdnsrarg_1_2_2_1_sr_2_0 = local_im;
                          Builder2.CreateFNeg(fwdnsrarg_1_2_2_1_sr_2_0);
                        });
                        Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_1_2_2_1_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_2_1_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_2_1_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                    Value* local_re = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_0, ArrayRef<unsigned>({0}), "re");
                    Value* local_im = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_0, ArrayRef<unsigned>({1}), "im");
                    llvm::Value *local_exp = nullptr;
                    ({
                      auto fwdnsrarg_1_2_2_sr_0 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_2_2_sr_1_0 = ({
                          // Computing exp
                          auto fwdnsrarg_1_2_2_sr_1_1_0 = local_re;
                          Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({fwdnsrarg_1_2_2_sr_1_1_0->getType()})), ArrayRef<Value*>({fwdnsrarg_1_2_2_sr_1_1_0}));
                        });
                        local_exp = fwdnsrarg_1_2_2_sr_1_0;
                        auto fwdnsrarg_1_2_2_sr_1_1 = ({
                          // Computing cos
                          auto fwdnsrarg_1_2_2_sr_1_2_0 = local_im;
                          Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::cos, std::vector<Type*>({fwdnsrarg_1_2_2_sr_1_2_0->getType()})), ArrayRef<Value*>({fwdnsrarg_1_2_2_sr_1_2_0}));
                        });
                        Builder2.CreateFMul(fwdnsrarg_1_2_2_sr_1_0, fwdnsrarg_1_2_2_sr_1_1);
                      });
                      auto fwdnsrarg_1_2_2_sr_1 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_2_2_sr_2_0 = local_exp;
                        auto fwdnsrarg_1_2_2_sr_2_1 = ({
                          // Computing sin
                          auto fwdnsrarg_1_2_2_sr_2_2_0 = local_im;
                          Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sin, std::vector<Type*>({fwdnsrarg_1_2_2_sr_2_2_0->getType()})), ArrayRef<Value*>({fwdnsrarg_1_2_2_sr_2_2_0}));
                        });
                        Builder2.CreateFMul(fwdnsrarg_1_2_2_sr_2_0, fwdnsrarg_1_2_2_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_1_2_2_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_2_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_2_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                  Value* local_re1 = Builder2.CreateExtractValue(fwdnsrarg_1_2_0, ArrayRef<unsigned>({0}), "re1");
                  Value* local_im1 = Builder2.CreateExtractValue(fwdnsrarg_1_2_0, ArrayRef<unsigned>({1}), "im1");
                  Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_1_2_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_1_2_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto fwdnsrarg_1_2_sr_0 = local_re1;
                    auto fwdnsrarg_1_2_sr_1 = local_im1;
                    auto fwdnsrarg_1_2_sr_2 = local_re2;
                    auto fwdnsrarg_1_2_sr_3 = local_im2;
                    ({
                      auto fwdnsrarg_1_2_sr_sr_0 = ({
                        // Computing FSub
                        auto fwdnsrarg_1_2_sr_sr_1_0 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_1_2_sr_sr_1_1_0 = fwdnsrarg_1_2_sr_0;
                          llvm::Value* fwdnsrarg_1_2_sr_sr_1_1_1 = fwdnsrarg_1_2_sr_2;
                          Builder2.CreateFMul(fwdnsrarg_1_2_sr_sr_1_1_0, fwdnsrarg_1_2_sr_sr_1_1_1);
                        });
                        auto fwdnsrarg_1_2_sr_sr_1_1 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_1_2_sr_sr_1_2_0 = fwdnsrarg_1_2_sr_1;
                          llvm::Value* fwdnsrarg_1_2_sr_sr_1_2_1 = fwdnsrarg_1_2_sr_3;
                          Builder2.CreateFMul(fwdnsrarg_1_2_sr_sr_1_2_0, fwdnsrarg_1_2_sr_sr_1_2_1);
                        });
                        Builder2.CreateFSub(fwdnsrarg_1_2_sr_sr_1_0, fwdnsrarg_1_2_sr_sr_1_1);
                      });
                      auto fwdnsrarg_1_2_sr_sr_1 = ({
                        // Computing FAdd
                        auto fwdnsrarg_1_2_sr_sr_2_0 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_1_2_sr_sr_2_1_0 = fwdnsrarg_1_2_sr_0;
                          llvm::Value* fwdnsrarg_1_2_sr_sr_2_1_1 = fwdnsrarg_1_2_sr_3;
                          Builder2.CreateFMul(fwdnsrarg_1_2_sr_sr_2_1_0, fwdnsrarg_1_2_sr_sr_2_1_1);
                        });
                        auto fwdnsrarg_1_2_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_1_2_sr_sr_2_2_0 = fwdnsrarg_1_2_sr_2;
                          llvm::Value* fwdnsrarg_1_2_sr_sr_2_2_1 = fwdnsrarg_1_2_sr_1;
                          Builder2.CreateFMul(fwdnsrarg_1_2_sr_sr_2_2_0, fwdnsrarg_1_2_sr_sr_2_2_1);
                        });
                        Builder2.CreateFAdd(fwdnsrarg_1_2_sr_sr_2_0, fwdnsrarg_1_2_sr_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_1_2_sr_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_sr_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_sr_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFMul_splat
                  auto fwdnsrarg_1_sr_0 = local_re1;
                  auto fwdnsrarg_1_sr_1 = local_im1;
                  auto fwdnsrarg_1_sr_2 = local_re2;
                  auto fwdnsrarg_1_sr_3 = local_im2;
                  ({
                    auto fwdnsrarg_1_sr_sr_0 = ({
                      // Computing FSub
                      auto fwdnsrarg_1_sr_sr_1_0 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_sr_sr_1_1_0 = fwdnsrarg_1_sr_0;
                        llvm::Value* fwdnsrarg_1_sr_sr_1_1_1 = fwdnsrarg_1_sr_2;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_1_1_0, idx), fwdnsrarg_1_sr_sr_1_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_1_sr_sr_1_1 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_sr_sr_1_2_0 = fwdnsrarg_1_sr_1;
                        llvm::Value* fwdnsrarg_1_sr_sr_1_2_1 = fwdnsrarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_1_2_0, idx), fwdnsrarg_1_sr_sr_1_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_1_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdnsrarg_1_sr_sr_1 = ({
                      // Computing FAdd
                      auto fwdnsrarg_1_sr_sr_2_0 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_sr_sr_2_1_0 = fwdnsrarg_1_sr_0;
                        llvm::Value* fwdnsrarg_1_sr_sr_2_1_1 = fwdnsrarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_2_1_0, idx), fwdnsrarg_1_sr_sr_2_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_1_sr_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_1_sr_sr_2_2_0 = fwdnsrarg_1_sr_2;
                        auto fwdnsrarg_1_sr_sr_2_2_1 = fwdnsrarg_1_sr_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul(fwdnsrarg_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_2_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_0->getType() : getSubType(fwdnsrarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              ({
                auto fwdnsrarg_sr_0 = local_re;
                auto fwdnsrarg_sr_1 = local_im;
                Value *res = UndefValue::get(gutils->getShadowType(call.getType()));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(0);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_0, idx), idxs);
                  }
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(1);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_1, idx), idxs);
                  }
                }
                 res;
              });
            });
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing subroutine ToStruct2
              auto fwdarg_0 = ({
                // Computing subroutine Conj
                auto fwdarg_1_0 = ({
                  // Computing subroutine CFMul
                  auto fwdarg_1_1_0 = ({
                    // Computing subroutine Conj
                    auto fwdarg_1_1_1_0 = dif;
                    Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_1_0->getType(), 0, 0)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_1_0->getType(), 0, 1)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    ({
                      auto fwdarg_1_1_1_sr_0 = local_re;
                      auto fwdarg_1_1_1_sr_1 = ({
                        // Computing FNeg
                        auto fwdarg_1_1_1_sr_2_0 = local_im;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_2_0, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_0->getType() : getSubType(fwdarg_1_1_1_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                  auto fwdarg_1_1_1 = ({
                    // Computing subroutine CFMul
                    auto fwdarg_1_1_2_0 = ({
                      auto ty = call.getOperand(0)->getType();
                      Value *ret = nullptr;
                      if (auto ST = dyn_cast<StructType>(ty)) {
                        ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                        ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                      } else {
                        llvm::errs() << *ty << "\n";
                        assert(0 && "unhandled cfp");
                      }
                      ret;
                    })
;
                    auto fwdarg_1_1_2_1 = ({
                      // Computing subroutine CFExp
                      auto fwdarg_1_1_2_2_0 = ({
                        // Computing subroutine CFNeg
                        auto fwdarg_1_1_2_2_1_0 = ({
                          // Computing subroutine CFMul
                          auto fwdarg_1_1_2_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                          auto fwdarg_1_1_2_2_1_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                          Value* local_re1 = Builder2.CreateExtractValue(fwdarg_1_1_2_2_1_1_0, ArrayRef<unsigned>({0}), "re1");
                          Value* local_im1 = Builder2.CreateExtractValue(fwdarg_1_1_2_2_1_1_0, ArrayRef<unsigned>({1}), "im1");
                          Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1_2_2_1_1_1, ArrayRef<unsigned>({0}), "re2");
                          Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1_2_2_1_1_1, ArrayRef<unsigned>({1}), "im2");
                          ({
                            // Computing subroutine CFMul_splat
                            auto fwdarg_1_1_2_2_1_1_sr_0 = local_re1;
                            auto fwdarg_1_1_2_2_1_1_sr_1 = local_im1;
                            auto fwdarg_1_1_2_2_1_1_sr_2 = local_re2;
                            auto fwdarg_1_1_2_2_1_1_sr_3 = local_im2;
                            ({
                              auto fwdarg_1_1_2_2_1_1_sr_sr_0 = ({
                                // Computing FSub
                                auto fwdarg_1_1_2_2_1_1_sr_sr_1_0 = ({
                                  // Computing FMul
                                  llvm::Value* fwdarg_1_1_2_2_1_1_sr_sr_1_1_0 = fwdarg_1_1_2_2_1_1_sr_0;
                                  llvm::Value* fwdarg_1_1_2_2_1_1_sr_sr_1_1_1 = fwdarg_1_1_2_2_1_1_sr_2;
                                  Builder2.CreateFMul(fwdarg_1_1_2_2_1_1_sr_sr_1_1_0, fwdarg_1_1_2_2_1_1_sr_sr_1_1_1);
                                });
                                auto fwdarg_1_1_2_2_1_1_sr_sr_1_1 = ({
                                  // Computing FMul
                                  llvm::Value* fwdarg_1_1_2_2_1_1_sr_sr_1_2_0 = fwdarg_1_1_2_2_1_1_sr_1;
                                  llvm::Value* fwdarg_1_1_2_2_1_1_sr_sr_1_2_1 = fwdarg_1_1_2_2_1_1_sr_3;
                                  Builder2.CreateFMul(fwdarg_1_1_2_2_1_1_sr_sr_1_2_0, fwdarg_1_1_2_2_1_1_sr_sr_1_2_1);
                                });
                                Builder2.CreateFSub(fwdarg_1_1_2_2_1_1_sr_sr_1_0, fwdarg_1_1_2_2_1_1_sr_sr_1_1);
                              });
                              auto fwdarg_1_1_2_2_1_1_sr_sr_1 = ({
                                // Computing FAdd
                                auto fwdarg_1_1_2_2_1_1_sr_sr_2_0 = ({
                                  // Computing FMul
                                  llvm::Value* fwdarg_1_1_2_2_1_1_sr_sr_2_1_0 = fwdarg_1_1_2_2_1_1_sr_0;
                                  llvm::Value* fwdarg_1_1_2_2_1_1_sr_sr_2_1_1 = fwdarg_1_1_2_2_1_1_sr_3;
                                  Builder2.CreateFMul(fwdarg_1_1_2_2_1_1_sr_sr_2_1_0, fwdarg_1_1_2_2_1_1_sr_sr_2_1_1);
                                });
                                auto fwdarg_1_1_2_2_1_1_sr_sr_2_1 = ({
                                  // Computing FMul
                                  llvm::Value* fwdarg_1_1_2_2_1_1_sr_sr_2_2_0 = fwdarg_1_1_2_2_1_1_sr_2;
                                  llvm::Value* fwdarg_1_1_2_2_1_1_sr_sr_2_2_1 = fwdarg_1_1_2_2_1_1_sr_1;
                                  Builder2.CreateFMul(fwdarg_1_1_2_2_1_1_sr_sr_2_2_0, fwdarg_1_1_2_2_1_1_sr_sr_2_2_1);
                                });
                                Builder2.CreateFAdd(fwdarg_1_1_2_2_1_1_sr_sr_2_0, fwdarg_1_1_2_2_1_1_sr_sr_2_1);
                              });
                              Value *res = UndefValue::get(ArrayType::get(fwdarg_1_1_2_2_1_1_sr_sr_0->getType(), 2));
                              {
                                {
                                  std::vector<unsigned> idxs;
                                  idxs.push_back(0);
                                  res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_2_1_1_sr_sr_0, idxs);
                                }
                                {
                                  std::vector<unsigned> idxs;
                                  idxs.push_back(1);
                                  res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_2_1_1_sr_sr_1, idxs);
                                }
                              }
                               res;
                            });
                          });
                        });
                        Value* local_re = Builder2.CreateExtractValue(fwdarg_1_1_2_2_1_0, ArrayRef<unsigned>({0}), "re");
                        Value* local_im = Builder2.CreateExtractValue(fwdarg_1_1_2_2_1_0, ArrayRef<unsigned>({1}), "im");
                        ({
                          auto fwdarg_1_1_2_2_1_sr_0 = ({
                            // Computing FNeg
                            auto fwdarg_1_1_2_2_1_sr_1_0 = local_re;
                            Builder2.CreateFNeg(fwdarg_1_1_2_2_1_sr_1_0);
                          });
                          auto fwdarg_1_1_2_2_1_sr_1 = ({
                            // Computing FNeg
                            auto fwdarg_1_1_2_2_1_sr_2_0 = local_im;
                            Builder2.CreateFNeg(fwdarg_1_1_2_2_1_sr_2_0);
                          });
                          Value *res = UndefValue::get(ArrayType::get(fwdarg_1_1_2_2_1_sr_0->getType(), 2));
                          {
                            {
                              std::vector<unsigned> idxs;
                              idxs.push_back(0);
                              res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_2_1_sr_0, idxs);
                            }
                            {
                              std::vector<unsigned> idxs;
                              idxs.push_back(1);
                              res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_2_1_sr_1, idxs);
                            }
                          }
                           res;
                        });
                      });
                      Value* local_re = Builder2.CreateExtractValue(fwdarg_1_1_2_2_0, ArrayRef<unsigned>({0}), "re");
                      Value* local_im = Builder2.CreateExtractValue(fwdarg_1_1_2_2_0, ArrayRef<unsigned>({1}), "im");
                      llvm::Value *local_exp = nullptr;
                      ({
                        auto fwdarg_1_1_2_2_sr_0 = ({
                          // Computing FMul
                          auto fwdarg_1_1_2_2_sr_1_0 = ({
                            // Computing exp
                            auto fwdarg_1_1_2_2_sr_1_1_0 = local_re;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({fwdarg_1_1_2_2_sr_1_1_0->getType()})), ArrayRef<Value*>({fwdarg_1_1_2_2_sr_1_1_0}));
                          });
                          local_exp = fwdarg_1_1_2_2_sr_1_0;
                          auto fwdarg_1_1_2_2_sr_1_1 = ({
                            // Computing cos
                            auto fwdarg_1_1_2_2_sr_1_2_0 = local_im;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::cos, std::vector<Type*>({fwdarg_1_1_2_2_sr_1_2_0->getType()})), ArrayRef<Value*>({fwdarg_1_1_2_2_sr_1_2_0}));
                          });
                          Builder2.CreateFMul(fwdarg_1_1_2_2_sr_1_0, fwdarg_1_1_2_2_sr_1_1);
                        });
                        auto fwdarg_1_1_2_2_sr_1 = ({
                          // Computing FMul
                          auto fwdarg_1_1_2_2_sr_2_0 = local_exp;
                          auto fwdarg_1_1_2_2_sr_2_1 = ({
                            // Computing sin
                            auto fwdarg_1_1_2_2_sr_2_2_0 = local_im;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sin, std::vector<Type*>({fwdarg_1_1_2_2_sr_2_2_0->getType()})), ArrayRef<Value*>({fwdarg_1_1_2_2_sr_2_2_0}));
                          });
                          Builder2.CreateFMul(fwdarg_1_1_2_2_sr_2_0, fwdarg_1_1_2_2_sr_2_1);
                        });
                        Value *res = UndefValue::get(ArrayType::get(fwdarg_1_1_2_2_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_2_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_2_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                    Value* local_re1 = Builder2.CreateExtractValue(fwdarg_1_1_2_0, ArrayRef<unsigned>({0}), "re1");
                    Value* local_im1 = Builder2.CreateExtractValue(fwdarg_1_1_2_0, ArrayRef<unsigned>({1}), "im1");
                    Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1_2_1, ArrayRef<unsigned>({0}), "re2");
                    Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1_2_1, ArrayRef<unsigned>({1}), "im2");
                    ({
                      // Computing subroutine CFMul_splat
                      auto fwdarg_1_1_2_sr_0 = local_re1;
                      auto fwdarg_1_1_2_sr_1 = local_im1;
                      auto fwdarg_1_1_2_sr_2 = local_re2;
                      auto fwdarg_1_1_2_sr_3 = local_im2;
                      ({
                        auto fwdarg_1_1_2_sr_sr_0 = ({
                          // Computing FSub
                          auto fwdarg_1_1_2_sr_sr_1_0 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_1_1_2_sr_sr_1_1_0 = fwdarg_1_1_2_sr_0;
                            llvm::Value* fwdarg_1_1_2_sr_sr_1_1_1 = fwdarg_1_1_2_sr_2;
                            Builder2.CreateFMul(fwdarg_1_1_2_sr_sr_1_1_0, fwdarg_1_1_2_sr_sr_1_1_1);
                          });
                          auto fwdarg_1_1_2_sr_sr_1_1 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_1_1_2_sr_sr_1_2_0 = fwdarg_1_1_2_sr_1;
                            llvm::Value* fwdarg_1_1_2_sr_sr_1_2_1 = fwdarg_1_1_2_sr_3;
                            Builder2.CreateFMul(fwdarg_1_1_2_sr_sr_1_2_0, fwdarg_1_1_2_sr_sr_1_2_1);
                          });
                          Builder2.CreateFSub(fwdarg_1_1_2_sr_sr_1_0, fwdarg_1_1_2_sr_sr_1_1);
                        });
                        auto fwdarg_1_1_2_sr_sr_1 = ({
                          // Computing FAdd
                          auto fwdarg_1_1_2_sr_sr_2_0 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_1_1_2_sr_sr_2_1_0 = fwdarg_1_1_2_sr_0;
                            llvm::Value* fwdarg_1_1_2_sr_sr_2_1_1 = fwdarg_1_1_2_sr_3;
                            Builder2.CreateFMul(fwdarg_1_1_2_sr_sr_2_1_0, fwdarg_1_1_2_sr_sr_2_1_1);
                          });
                          auto fwdarg_1_1_2_sr_sr_2_1 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_1_1_2_sr_sr_2_2_0 = fwdarg_1_1_2_sr_2;
                            llvm::Value* fwdarg_1_1_2_sr_sr_2_2_1 = fwdarg_1_1_2_sr_1;
                            Builder2.CreateFMul(fwdarg_1_1_2_sr_sr_2_2_0, fwdarg_1_1_2_sr_sr_2_2_1);
                          });
                          Builder2.CreateFAdd(fwdarg_1_1_2_sr_sr_2_0, fwdarg_1_1_2_sr_sr_2_1);
                        });
                        Value *res = UndefValue::get(ArrayType::get(fwdarg_1_1_2_sr_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_sr_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_sr_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                  });
                  Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_0->getType(), 0, 0)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_0->getType(), 0, 1)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto fwdarg_1_1_sr_0 = local_re1;
                    auto fwdarg_1_1_sr_1 = local_im1;
                    auto fwdarg_1_1_sr_2 = local_re2;
                    auto fwdarg_1_1_sr_3 = local_im2;
                    ({
                      auto fwdarg_1_1_sr_sr_0 = ({
                        // Computing FSub
                        auto fwdarg_1_1_sr_sr_1_0 = ({
                          // Computing FMul
                          auto fwdarg_1_1_sr_sr_1_1_0 = fwdarg_1_1_sr_0;
                          llvm::Value* fwdarg_1_1_sr_sr_1_1_1 = fwdarg_1_1_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_1_0, idx), fwdarg_1_1_sr_sr_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_1_sr_sr_1_1 = ({
                          // Computing FMul
                          auto fwdarg_1_1_sr_sr_1_2_0 = fwdarg_1_1_sr_1;
                          llvm::Value* fwdarg_1_1_sr_sr_1_2_1 = fwdarg_1_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_2_0, idx), fwdarg_1_1_sr_sr_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_1_sr_sr_1 = ({
                        // Computing FAdd
                        auto fwdarg_1_1_sr_sr_2_0 = ({
                          // Computing FMul
                          auto fwdarg_1_1_sr_sr_2_1_0 = fwdarg_1_1_sr_0;
                          llvm::Value* fwdarg_1_1_sr_sr_2_1_1 = fwdarg_1_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_1_0, idx), fwdarg_1_1_sr_sr_2_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_1_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_1_sr_sr_2_2_0 = fwdarg_1_1_sr_2;
                          auto fwdarg_1_1_sr_sr_2_2_1 = fwdarg_1_1_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(fwdarg_1_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_2_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_0->getType() : getSubType(fwdarg_1_1_sr_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                ({
                  auto fwdarg_1_sr_0 = local_re;
                  auto fwdarg_1_sr_1 = ({
                    // Computing FNeg
                    auto fwdarg_1_sr_2_0 = local_im;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_1_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_2_0, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_sr_0->getType() : getSubType(fwdarg_1_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
              Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              ({
                auto fwdarg_sr_0 = local_re;
                auto fwdarg_sr_1 = local_im;
                Value *res = UndefValue::get(gutils->getShadowType(call.getType()));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(0);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_sr_0 : gutils->extractMeta(Builder2, fwdarg_sr_0, idx), idxs);
                  }
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(1);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_sr_1 : gutils->extractMeta(Builder2, fwdarg_sr_1, idx), idxs);
                  }
                }
                 res;
              });
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing subroutine ToStruct2
              auto revarg_0 = ({
                // Computing subroutine Conj
                auto revarg_1_0 = ({
                  // Computing subroutine CFMul
                  auto revarg_1_1_0 = ({
                    // Computing subroutine Conj
                    auto revarg_1_1_1_0 = dif;
                    Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_1_0->getType(), 0, 0)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_1_0->getType(), 0, 1)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    ({
                      auto revarg_1_1_1_sr_0 = local_re;
                      auto revarg_1_1_1_sr_1 = ({
                        // Computing FNeg
                        auto revarg_1_1_1_sr_2_0 = local_im;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_1_1_1_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_2_0, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_1_1_sr_0->getType() : getSubType(revarg_1_1_1_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_1_sr_0 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_1_sr_1 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                  auto revarg_1_1_1 = ({
                    // Computing subroutine CFMul
                    auto revarg_1_1_2_0 = ({
                      auto ty = call.getOperand(0)->getType();
                      Value *ret = nullptr;
                      if (auto ST = dyn_cast<StructType>(ty)) {
                        ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                        ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                      } else {
                        llvm::errs() << *ty << "\n";
                        assert(0 && "unhandled cfp");
                      }
                      ret;
                    })
;
                    auto revarg_1_1_2_1 = ({
                      // Computing subroutine CFExp
                      auto revarg_1_1_2_2_0 = ({
                        // Computing subroutine CFNeg
                        auto revarg_1_1_2_2_1_0 = ({
                          // Computing subroutine CFMul
                          auto revarg_1_1_2_2_1_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                          auto revarg_1_1_2_2_1_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                          Value* local_re1 = Builder2.CreateExtractValue(revarg_1_1_2_2_1_1_0, ArrayRef<unsigned>({0}), "re1");
                          Value* local_im1 = Builder2.CreateExtractValue(revarg_1_1_2_2_1_1_0, ArrayRef<unsigned>({1}), "im1");
                          Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1_2_2_1_1_1, ArrayRef<unsigned>({0}), "re2");
                          Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1_2_2_1_1_1, ArrayRef<unsigned>({1}), "im2");
                          ({
                            // Computing subroutine CFMul_splat
                            auto revarg_1_1_2_2_1_1_sr_0 = local_re1;
                            auto revarg_1_1_2_2_1_1_sr_1 = local_im1;
                            auto revarg_1_1_2_2_1_1_sr_2 = local_re2;
                            auto revarg_1_1_2_2_1_1_sr_3 = local_im2;
                            ({
                              auto revarg_1_1_2_2_1_1_sr_sr_0 = ({
                                // Computing FSub
                                auto revarg_1_1_2_2_1_1_sr_sr_1_0 = ({
                                  // Computing FMul
                                  llvm::Value* revarg_1_1_2_2_1_1_sr_sr_1_1_0 = revarg_1_1_2_2_1_1_sr_0;
                                  llvm::Value* revarg_1_1_2_2_1_1_sr_sr_1_1_1 = revarg_1_1_2_2_1_1_sr_2;
                                  Builder2.CreateFMul(revarg_1_1_2_2_1_1_sr_sr_1_1_0, revarg_1_1_2_2_1_1_sr_sr_1_1_1);
                                });
                                auto revarg_1_1_2_2_1_1_sr_sr_1_1 = ({
                                  // Computing FMul
                                  llvm::Value* revarg_1_1_2_2_1_1_sr_sr_1_2_0 = revarg_1_1_2_2_1_1_sr_1;
                                  llvm::Value* revarg_1_1_2_2_1_1_sr_sr_1_2_1 = revarg_1_1_2_2_1_1_sr_3;
                                  Builder2.CreateFMul(revarg_1_1_2_2_1_1_sr_sr_1_2_0, revarg_1_1_2_2_1_1_sr_sr_1_2_1);
                                });
                                Builder2.CreateFSub(revarg_1_1_2_2_1_1_sr_sr_1_0, revarg_1_1_2_2_1_1_sr_sr_1_1);
                              });
                              auto revarg_1_1_2_2_1_1_sr_sr_1 = ({
                                // Computing FAdd
                                auto revarg_1_1_2_2_1_1_sr_sr_2_0 = ({
                                  // Computing FMul
                                  llvm::Value* revarg_1_1_2_2_1_1_sr_sr_2_1_0 = revarg_1_1_2_2_1_1_sr_0;
                                  llvm::Value* revarg_1_1_2_2_1_1_sr_sr_2_1_1 = revarg_1_1_2_2_1_1_sr_3;
                                  Builder2.CreateFMul(revarg_1_1_2_2_1_1_sr_sr_2_1_0, revarg_1_1_2_2_1_1_sr_sr_2_1_1);
                                });
                                auto revarg_1_1_2_2_1_1_sr_sr_2_1 = ({
                                  // Computing FMul
                                  llvm::Value* revarg_1_1_2_2_1_1_sr_sr_2_2_0 = revarg_1_1_2_2_1_1_sr_2;
                                  llvm::Value* revarg_1_1_2_2_1_1_sr_sr_2_2_1 = revarg_1_1_2_2_1_1_sr_1;
                                  Builder2.CreateFMul(revarg_1_1_2_2_1_1_sr_sr_2_2_0, revarg_1_1_2_2_1_1_sr_sr_2_2_1);
                                });
                                Builder2.CreateFAdd(revarg_1_1_2_2_1_1_sr_sr_2_0, revarg_1_1_2_2_1_1_sr_sr_2_1);
                              });
                              Value *res = UndefValue::get(ArrayType::get(revarg_1_1_2_2_1_1_sr_sr_0->getType(), 2));
                              {
                                {
                                  std::vector<unsigned> idxs;
                                  idxs.push_back(0);
                                  res = Builder2.CreateInsertValue(res, revarg_1_1_2_2_1_1_sr_sr_0, idxs);
                                }
                                {
                                  std::vector<unsigned> idxs;
                                  idxs.push_back(1);
                                  res = Builder2.CreateInsertValue(res, revarg_1_1_2_2_1_1_sr_sr_1, idxs);
                                }
                              }
                               res;
                            });
                          });
                        });
                        Value* local_re = Builder2.CreateExtractValue(revarg_1_1_2_2_1_0, ArrayRef<unsigned>({0}), "re");
                        Value* local_im = Builder2.CreateExtractValue(revarg_1_1_2_2_1_0, ArrayRef<unsigned>({1}), "im");
                        ({
                          auto revarg_1_1_2_2_1_sr_0 = ({
                            // Computing FNeg
                            auto revarg_1_1_2_2_1_sr_1_0 = local_re;
                            Builder2.CreateFNeg(revarg_1_1_2_2_1_sr_1_0);
                          });
                          auto revarg_1_1_2_2_1_sr_1 = ({
                            // Computing FNeg
                            auto revarg_1_1_2_2_1_sr_2_0 = local_im;
                            Builder2.CreateFNeg(revarg_1_1_2_2_1_sr_2_0);
                          });
                          Value *res = UndefValue::get(ArrayType::get(revarg_1_1_2_2_1_sr_0->getType(), 2));
                          {
                            {
                              std::vector<unsigned> idxs;
                              idxs.push_back(0);
                              res = Builder2.CreateInsertValue(res, revarg_1_1_2_2_1_sr_0, idxs);
                            }
                            {
                              std::vector<unsigned> idxs;
                              idxs.push_back(1);
                              res = Builder2.CreateInsertValue(res, revarg_1_1_2_2_1_sr_1, idxs);
                            }
                          }
                           res;
                        });
                      });
                      Value* local_re = Builder2.CreateExtractValue(revarg_1_1_2_2_0, ArrayRef<unsigned>({0}), "re");
                      Value* local_im = Builder2.CreateExtractValue(revarg_1_1_2_2_0, ArrayRef<unsigned>({1}), "im");
                      llvm::Value *local_exp = nullptr;
                      ({
                        auto revarg_1_1_2_2_sr_0 = ({
                          // Computing FMul
                          auto revarg_1_1_2_2_sr_1_0 = ({
                            // Computing exp
                            auto revarg_1_1_2_2_sr_1_1_0 = local_re;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({revarg_1_1_2_2_sr_1_1_0->getType()})), ArrayRef<Value*>({revarg_1_1_2_2_sr_1_1_0}));
                          });
                          local_exp = revarg_1_1_2_2_sr_1_0;
                          auto revarg_1_1_2_2_sr_1_1 = ({
                            // Computing cos
                            auto revarg_1_1_2_2_sr_1_2_0 = local_im;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::cos, std::vector<Type*>({revarg_1_1_2_2_sr_1_2_0->getType()})), ArrayRef<Value*>({revarg_1_1_2_2_sr_1_2_0}));
                          });
                          Builder2.CreateFMul(revarg_1_1_2_2_sr_1_0, revarg_1_1_2_2_sr_1_1);
                        });
                        auto revarg_1_1_2_2_sr_1 = ({
                          // Computing FMul
                          auto revarg_1_1_2_2_sr_2_0 = local_exp;
                          auto revarg_1_1_2_2_sr_2_1 = ({
                            // Computing sin
                            auto revarg_1_1_2_2_sr_2_2_0 = local_im;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sin, std::vector<Type*>({revarg_1_1_2_2_sr_2_2_0->getType()})), ArrayRef<Value*>({revarg_1_1_2_2_sr_2_2_0}));
                          });
                          Builder2.CreateFMul(revarg_1_1_2_2_sr_2_0, revarg_1_1_2_2_sr_2_1);
                        });
                        Value *res = UndefValue::get(ArrayType::get(revarg_1_1_2_2_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, revarg_1_1_2_2_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, revarg_1_1_2_2_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                    Value* local_re1 = Builder2.CreateExtractValue(revarg_1_1_2_0, ArrayRef<unsigned>({0}), "re1");
                    Value* local_im1 = Builder2.CreateExtractValue(revarg_1_1_2_0, ArrayRef<unsigned>({1}), "im1");
                    Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1_2_1, ArrayRef<unsigned>({0}), "re2");
                    Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1_2_1, ArrayRef<unsigned>({1}), "im2");
                    ({
                      // Computing subroutine CFMul_splat
                      auto revarg_1_1_2_sr_0 = local_re1;
                      auto revarg_1_1_2_sr_1 = local_im1;
                      auto revarg_1_1_2_sr_2 = local_re2;
                      auto revarg_1_1_2_sr_3 = local_im2;
                      ({
                        auto revarg_1_1_2_sr_sr_0 = ({
                          // Computing FSub
                          auto revarg_1_1_2_sr_sr_1_0 = ({
                            // Computing FMul
                            llvm::Value* revarg_1_1_2_sr_sr_1_1_0 = revarg_1_1_2_sr_0;
                            llvm::Value* revarg_1_1_2_sr_sr_1_1_1 = revarg_1_1_2_sr_2;
                            Builder2.CreateFMul(revarg_1_1_2_sr_sr_1_1_0, revarg_1_1_2_sr_sr_1_1_1);
                          });
                          auto revarg_1_1_2_sr_sr_1_1 = ({
                            // Computing FMul
                            llvm::Value* revarg_1_1_2_sr_sr_1_2_0 = revarg_1_1_2_sr_1;
                            llvm::Value* revarg_1_1_2_sr_sr_1_2_1 = revarg_1_1_2_sr_3;
                            Builder2.CreateFMul(revarg_1_1_2_sr_sr_1_2_0, revarg_1_1_2_sr_sr_1_2_1);
                          });
                          Builder2.CreateFSub(revarg_1_1_2_sr_sr_1_0, revarg_1_1_2_sr_sr_1_1);
                        });
                        auto revarg_1_1_2_sr_sr_1 = ({
                          // Computing FAdd
                          auto revarg_1_1_2_sr_sr_2_0 = ({
                            // Computing FMul
                            llvm::Value* revarg_1_1_2_sr_sr_2_1_0 = revarg_1_1_2_sr_0;
                            llvm::Value* revarg_1_1_2_sr_sr_2_1_1 = revarg_1_1_2_sr_3;
                            Builder2.CreateFMul(revarg_1_1_2_sr_sr_2_1_0, revarg_1_1_2_sr_sr_2_1_1);
                          });
                          auto revarg_1_1_2_sr_sr_2_1 = ({
                            // Computing FMul
                            llvm::Value* revarg_1_1_2_sr_sr_2_2_0 = revarg_1_1_2_sr_2;
                            llvm::Value* revarg_1_1_2_sr_sr_2_2_1 = revarg_1_1_2_sr_1;
                            Builder2.CreateFMul(revarg_1_1_2_sr_sr_2_2_0, revarg_1_1_2_sr_sr_2_2_1);
                          });
                          Builder2.CreateFAdd(revarg_1_1_2_sr_sr_2_0, revarg_1_1_2_sr_sr_2_1);
                        });
                        Value *res = UndefValue::get(ArrayType::get(revarg_1_1_2_sr_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, revarg_1_1_2_sr_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, revarg_1_1_2_sr_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                  });
                  Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_0->getType(), 0, 0)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_0->getType(), 0, 1)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto revarg_1_1_sr_0 = local_re1;
                    auto revarg_1_1_sr_1 = local_im1;
                    auto revarg_1_1_sr_2 = local_re2;
                    auto revarg_1_1_sr_3 = local_im2;
                    ({
                      auto revarg_1_1_sr_sr_0 = ({
                        // Computing FSub
                        auto revarg_1_1_sr_sr_1_0 = ({
                          // Computing FMul
                          auto revarg_1_1_sr_sr_1_1_0 = revarg_1_1_sr_0;
                          llvm::Value* revarg_1_1_sr_sr_1_1_1 = revarg_1_1_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_1_0, idx), revarg_1_1_sr_sr_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_1_sr_sr_1_1 = ({
                          // Computing FMul
                          auto revarg_1_1_sr_sr_1_2_0 = revarg_1_1_sr_1;
                          llvm::Value* revarg_1_1_sr_sr_1_2_1 = revarg_1_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_2_0, idx), revarg_1_1_sr_sr_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_1_sr_sr_1 = ({
                        // Computing FAdd
                        auto revarg_1_1_sr_sr_2_0 = ({
                          // Computing FMul
                          auto revarg_1_1_sr_sr_2_1_0 = revarg_1_1_sr_0;
                          llvm::Value* revarg_1_1_sr_sr_2_1_1 = revarg_1_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_1_0, idx), revarg_1_1_sr_sr_2_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_1_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_1_sr_sr_2_2_0 = revarg_1_1_sr_2;
                          auto revarg_1_1_sr_sr_2_2_1 = revarg_1_1_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(revarg_1_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_2_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_0->getType() : getSubType(revarg_1_1_sr_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                ({
                  auto revarg_1_sr_0 = local_re;
                  auto revarg_1_sr_1 = ({
                    // Computing FNeg
                    auto revarg_1_sr_2_0 = local_im;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_1_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_2_0, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_sr_0->getType() : getSubType(revarg_1_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_0 : gutils->extractMeta(Builder2, revarg_1_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_1 : gutils->extractMeta(Builder2, revarg_1_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
              Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(revarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(revarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              ({
                auto revarg_sr_0 = local_re;
                auto revarg_sr_1 = local_im;
                Value *res = UndefValue::get(gutils->getShadowType(call.getType()));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(0);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_sr_0 : gutils->extractMeta(Builder2, revarg_sr_0, idx), idxs);
                  }
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(1);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_sr_1 : gutils->extractMeta(Builder2, revarg_sr_1, idx), idxs);
                  }
                }
                 res;
              });
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "Faddeeva_erfi") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
            Value *res = ({
              // Computing subroutine ToStruct2
              auto fwdnsrarg_0 = ({
                // Computing subroutine CFMul
                auto fwdnsrarg_1_0 = gutils->invertPointerM(call.getOperand(0), Builder2, TR.query(&call));
                auto fwdnsrarg_1_1 = ({
                  // Computing subroutine CFMul
                  auto fwdnsrarg_1_2_0 = ({
                    auto ty = call.getOperand(0)->getType();
                    Value *ret = nullptr;
                    if (auto ST = dyn_cast<StructType>(ty)) {
                      ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                      ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                    } else {
                      llvm::errs() << *ty << "\n";
                      assert(0 && "unhandled cfp");
                    }
                    ret;
                  })
;
                  auto fwdnsrarg_1_2_1 = ({
                    // Computing subroutine CFExp
                    auto fwdnsrarg_1_2_2_0 = ({
                      // Computing subroutine CFMul
                      auto fwdnsrarg_1_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto fwdnsrarg_1_2_2_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                      Value* local_re1 = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_1_0, ArrayRef<unsigned>({0}), "re1");
                      Value* local_im1 = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_1_0, ArrayRef<unsigned>({1}), "im1");
                      Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_1_1, ArrayRef<unsigned>({0}), "re2");
                      Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_1_1, ArrayRef<unsigned>({1}), "im2");
                      ({
                        // Computing subroutine CFMul_splat
                        auto fwdnsrarg_1_2_2_1_sr_0 = local_re1;
                        auto fwdnsrarg_1_2_2_1_sr_1 = local_im1;
                        auto fwdnsrarg_1_2_2_1_sr_2 = local_re2;
                        auto fwdnsrarg_1_2_2_1_sr_3 = local_im2;
                        ({
                          auto fwdnsrarg_1_2_2_1_sr_sr_0 = ({
                            // Computing FSub
                            auto fwdnsrarg_1_2_2_1_sr_sr_1_0 = ({
                              // Computing FMul
                              llvm::Value* fwdnsrarg_1_2_2_1_sr_sr_1_1_0 = fwdnsrarg_1_2_2_1_sr_0;
                              llvm::Value* fwdnsrarg_1_2_2_1_sr_sr_1_1_1 = fwdnsrarg_1_2_2_1_sr_2;
                              Builder2.CreateFMul(fwdnsrarg_1_2_2_1_sr_sr_1_1_0, fwdnsrarg_1_2_2_1_sr_sr_1_1_1);
                            });
                            auto fwdnsrarg_1_2_2_1_sr_sr_1_1 = ({
                              // Computing FMul
                              llvm::Value* fwdnsrarg_1_2_2_1_sr_sr_1_2_0 = fwdnsrarg_1_2_2_1_sr_1;
                              llvm::Value* fwdnsrarg_1_2_2_1_sr_sr_1_2_1 = fwdnsrarg_1_2_2_1_sr_3;
                              Builder2.CreateFMul(fwdnsrarg_1_2_2_1_sr_sr_1_2_0, fwdnsrarg_1_2_2_1_sr_sr_1_2_1);
                            });
                            Builder2.CreateFSub(fwdnsrarg_1_2_2_1_sr_sr_1_0, fwdnsrarg_1_2_2_1_sr_sr_1_1);
                          });
                          auto fwdnsrarg_1_2_2_1_sr_sr_1 = ({
                            // Computing FAdd
                            auto fwdnsrarg_1_2_2_1_sr_sr_2_0 = ({
                              // Computing FMul
                              llvm::Value* fwdnsrarg_1_2_2_1_sr_sr_2_1_0 = fwdnsrarg_1_2_2_1_sr_0;
                              llvm::Value* fwdnsrarg_1_2_2_1_sr_sr_2_1_1 = fwdnsrarg_1_2_2_1_sr_3;
                              Builder2.CreateFMul(fwdnsrarg_1_2_2_1_sr_sr_2_1_0, fwdnsrarg_1_2_2_1_sr_sr_2_1_1);
                            });
                            auto fwdnsrarg_1_2_2_1_sr_sr_2_1 = ({
                              // Computing FMul
                              llvm::Value* fwdnsrarg_1_2_2_1_sr_sr_2_2_0 = fwdnsrarg_1_2_2_1_sr_2;
                              llvm::Value* fwdnsrarg_1_2_2_1_sr_sr_2_2_1 = fwdnsrarg_1_2_2_1_sr_1;
                              Builder2.CreateFMul(fwdnsrarg_1_2_2_1_sr_sr_2_2_0, fwdnsrarg_1_2_2_1_sr_sr_2_2_1);
                            });
                            Builder2.CreateFAdd(fwdnsrarg_1_2_2_1_sr_sr_2_0, fwdnsrarg_1_2_2_1_sr_sr_2_1);
                          });
                          Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_1_2_2_1_sr_sr_0->getType(), 2));
                          {
                            {
                              std::vector<unsigned> idxs;
                              idxs.push_back(0);
                              res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_2_1_sr_sr_0, idxs);
                            }
                            {
                              std::vector<unsigned> idxs;
                              idxs.push_back(1);
                              res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_2_1_sr_sr_1, idxs);
                            }
                          }
                           res;
                        });
                      });
                    });
                    Value* local_re = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_0, ArrayRef<unsigned>({0}), "re");
                    Value* local_im = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_0, ArrayRef<unsigned>({1}), "im");
                    llvm::Value *local_exp = nullptr;
                    ({
                      auto fwdnsrarg_1_2_2_sr_0 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_2_2_sr_1_0 = ({
                          // Computing exp
                          auto fwdnsrarg_1_2_2_sr_1_1_0 = local_re;
                          Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({fwdnsrarg_1_2_2_sr_1_1_0->getType()})), ArrayRef<Value*>({fwdnsrarg_1_2_2_sr_1_1_0}));
                        });
                        local_exp = fwdnsrarg_1_2_2_sr_1_0;
                        auto fwdnsrarg_1_2_2_sr_1_1 = ({
                          // Computing cos
                          auto fwdnsrarg_1_2_2_sr_1_2_0 = local_im;
                          Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::cos, std::vector<Type*>({fwdnsrarg_1_2_2_sr_1_2_0->getType()})), ArrayRef<Value*>({fwdnsrarg_1_2_2_sr_1_2_0}));
                        });
                        Builder2.CreateFMul(fwdnsrarg_1_2_2_sr_1_0, fwdnsrarg_1_2_2_sr_1_1);
                      });
                      auto fwdnsrarg_1_2_2_sr_1 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_2_2_sr_2_0 = local_exp;
                        auto fwdnsrarg_1_2_2_sr_2_1 = ({
                          // Computing sin
                          auto fwdnsrarg_1_2_2_sr_2_2_0 = local_im;
                          Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sin, std::vector<Type*>({fwdnsrarg_1_2_2_sr_2_2_0->getType()})), ArrayRef<Value*>({fwdnsrarg_1_2_2_sr_2_2_0}));
                        });
                        Builder2.CreateFMul(fwdnsrarg_1_2_2_sr_2_0, fwdnsrarg_1_2_2_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_1_2_2_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_2_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_2_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                  Value* local_re1 = Builder2.CreateExtractValue(fwdnsrarg_1_2_0, ArrayRef<unsigned>({0}), "re1");
                  Value* local_im1 = Builder2.CreateExtractValue(fwdnsrarg_1_2_0, ArrayRef<unsigned>({1}), "im1");
                  Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_1_2_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_1_2_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto fwdnsrarg_1_2_sr_0 = local_re1;
                    auto fwdnsrarg_1_2_sr_1 = local_im1;
                    auto fwdnsrarg_1_2_sr_2 = local_re2;
                    auto fwdnsrarg_1_2_sr_3 = local_im2;
                    ({
                      auto fwdnsrarg_1_2_sr_sr_0 = ({
                        // Computing FSub
                        auto fwdnsrarg_1_2_sr_sr_1_0 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_1_2_sr_sr_1_1_0 = fwdnsrarg_1_2_sr_0;
                          llvm::Value* fwdnsrarg_1_2_sr_sr_1_1_1 = fwdnsrarg_1_2_sr_2;
                          Builder2.CreateFMul(fwdnsrarg_1_2_sr_sr_1_1_0, fwdnsrarg_1_2_sr_sr_1_1_1);
                        });
                        auto fwdnsrarg_1_2_sr_sr_1_1 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_1_2_sr_sr_1_2_0 = fwdnsrarg_1_2_sr_1;
                          llvm::Value* fwdnsrarg_1_2_sr_sr_1_2_1 = fwdnsrarg_1_2_sr_3;
                          Builder2.CreateFMul(fwdnsrarg_1_2_sr_sr_1_2_0, fwdnsrarg_1_2_sr_sr_1_2_1);
                        });
                        Builder2.CreateFSub(fwdnsrarg_1_2_sr_sr_1_0, fwdnsrarg_1_2_sr_sr_1_1);
                      });
                      auto fwdnsrarg_1_2_sr_sr_1 = ({
                        // Computing FAdd
                        auto fwdnsrarg_1_2_sr_sr_2_0 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_1_2_sr_sr_2_1_0 = fwdnsrarg_1_2_sr_0;
                          llvm::Value* fwdnsrarg_1_2_sr_sr_2_1_1 = fwdnsrarg_1_2_sr_3;
                          Builder2.CreateFMul(fwdnsrarg_1_2_sr_sr_2_1_0, fwdnsrarg_1_2_sr_sr_2_1_1);
                        });
                        auto fwdnsrarg_1_2_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_1_2_sr_sr_2_2_0 = fwdnsrarg_1_2_sr_2;
                          llvm::Value* fwdnsrarg_1_2_sr_sr_2_2_1 = fwdnsrarg_1_2_sr_1;
                          Builder2.CreateFMul(fwdnsrarg_1_2_sr_sr_2_2_0, fwdnsrarg_1_2_sr_sr_2_2_1);
                        });
                        Builder2.CreateFAdd(fwdnsrarg_1_2_sr_sr_2_0, fwdnsrarg_1_2_sr_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_1_2_sr_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_sr_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_sr_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFMul_splat
                  auto fwdnsrarg_1_sr_0 = local_re1;
                  auto fwdnsrarg_1_sr_1 = local_im1;
                  auto fwdnsrarg_1_sr_2 = local_re2;
                  auto fwdnsrarg_1_sr_3 = local_im2;
                  ({
                    auto fwdnsrarg_1_sr_sr_0 = ({
                      // Computing FSub
                      auto fwdnsrarg_1_sr_sr_1_0 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_sr_sr_1_1_0 = fwdnsrarg_1_sr_0;
                        llvm::Value* fwdnsrarg_1_sr_sr_1_1_1 = fwdnsrarg_1_sr_2;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_1_1_0, idx), fwdnsrarg_1_sr_sr_1_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_1_sr_sr_1_1 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_sr_sr_1_2_0 = fwdnsrarg_1_sr_1;
                        llvm::Value* fwdnsrarg_1_sr_sr_1_2_1 = fwdnsrarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_1_2_0, idx), fwdnsrarg_1_sr_sr_1_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_1_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdnsrarg_1_sr_sr_1 = ({
                      // Computing FAdd
                      auto fwdnsrarg_1_sr_sr_2_0 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_sr_sr_2_1_0 = fwdnsrarg_1_sr_0;
                        llvm::Value* fwdnsrarg_1_sr_sr_2_1_1 = fwdnsrarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_2_1_0, idx), fwdnsrarg_1_sr_sr_2_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_1_sr_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_1_sr_sr_2_2_0 = fwdnsrarg_1_sr_2;
                        auto fwdnsrarg_1_sr_sr_2_2_1 = fwdnsrarg_1_sr_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul(fwdnsrarg_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_2_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_0->getType() : getSubType(fwdnsrarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              ({
                auto fwdnsrarg_sr_0 = local_re;
                auto fwdnsrarg_sr_1 = local_im;
                Value *res = UndefValue::get(gutils->getShadowType(call.getType()));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(0);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_0, idx), idxs);
                  }
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(1);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_1, idx), idxs);
                  }
                }
                 res;
              });
            });
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing subroutine ToStruct2
              auto fwdarg_0 = ({
                // Computing subroutine Conj
                auto fwdarg_1_0 = ({
                  // Computing subroutine CFMul
                  auto fwdarg_1_1_0 = ({
                    // Computing subroutine Conj
                    auto fwdarg_1_1_1_0 = dif;
                    Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_1_0->getType(), 0, 0)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_1_0->getType(), 0, 1)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    ({
                      auto fwdarg_1_1_1_sr_0 = local_re;
                      auto fwdarg_1_1_1_sr_1 = ({
                        // Computing FNeg
                        auto fwdarg_1_1_1_sr_2_0 = local_im;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_2_0, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_0->getType() : getSubType(fwdarg_1_1_1_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                  auto fwdarg_1_1_1 = ({
                    // Computing subroutine CFMul
                    auto fwdarg_1_1_2_0 = ({
                      auto ty = call.getOperand(0)->getType();
                      Value *ret = nullptr;
                      if (auto ST = dyn_cast<StructType>(ty)) {
                        ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                        ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                      } else {
                        llvm::errs() << *ty << "\n";
                        assert(0 && "unhandled cfp");
                      }
                      ret;
                    })
;
                    auto fwdarg_1_1_2_1 = ({
                      // Computing subroutine CFExp
                      auto fwdarg_1_1_2_2_0 = ({
                        // Computing subroutine CFMul
                        auto fwdarg_1_1_2_2_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                        auto fwdarg_1_1_2_2_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                        Value* local_re1 = Builder2.CreateExtractValue(fwdarg_1_1_2_2_1_0, ArrayRef<unsigned>({0}), "re1");
                        Value* local_im1 = Builder2.CreateExtractValue(fwdarg_1_1_2_2_1_0, ArrayRef<unsigned>({1}), "im1");
                        Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1_2_2_1_1, ArrayRef<unsigned>({0}), "re2");
                        Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1_2_2_1_1, ArrayRef<unsigned>({1}), "im2");
                        ({
                          // Computing subroutine CFMul_splat
                          auto fwdarg_1_1_2_2_1_sr_0 = local_re1;
                          auto fwdarg_1_1_2_2_1_sr_1 = local_im1;
                          auto fwdarg_1_1_2_2_1_sr_2 = local_re2;
                          auto fwdarg_1_1_2_2_1_sr_3 = local_im2;
                          ({
                            auto fwdarg_1_1_2_2_1_sr_sr_0 = ({
                              // Computing FSub
                              auto fwdarg_1_1_2_2_1_sr_sr_1_0 = ({
                                // Computing FMul
                                llvm::Value* fwdarg_1_1_2_2_1_sr_sr_1_1_0 = fwdarg_1_1_2_2_1_sr_0;
                                llvm::Value* fwdarg_1_1_2_2_1_sr_sr_1_1_1 = fwdarg_1_1_2_2_1_sr_2;
                                Builder2.CreateFMul(fwdarg_1_1_2_2_1_sr_sr_1_1_0, fwdarg_1_1_2_2_1_sr_sr_1_1_1);
                              });
                              auto fwdarg_1_1_2_2_1_sr_sr_1_1 = ({
                                // Computing FMul
                                llvm::Value* fwdarg_1_1_2_2_1_sr_sr_1_2_0 = fwdarg_1_1_2_2_1_sr_1;
                                llvm::Value* fwdarg_1_1_2_2_1_sr_sr_1_2_1 = fwdarg_1_1_2_2_1_sr_3;
                                Builder2.CreateFMul(fwdarg_1_1_2_2_1_sr_sr_1_2_0, fwdarg_1_1_2_2_1_sr_sr_1_2_1);
                              });
                              Builder2.CreateFSub(fwdarg_1_1_2_2_1_sr_sr_1_0, fwdarg_1_1_2_2_1_sr_sr_1_1);
                            });
                            auto fwdarg_1_1_2_2_1_sr_sr_1 = ({
                              // Computing FAdd
                              auto fwdarg_1_1_2_2_1_sr_sr_2_0 = ({
                                // Computing FMul
                                llvm::Value* fwdarg_1_1_2_2_1_sr_sr_2_1_0 = fwdarg_1_1_2_2_1_sr_0;
                                llvm::Value* fwdarg_1_1_2_2_1_sr_sr_2_1_1 = fwdarg_1_1_2_2_1_sr_3;
                                Builder2.CreateFMul(fwdarg_1_1_2_2_1_sr_sr_2_1_0, fwdarg_1_1_2_2_1_sr_sr_2_1_1);
                              });
                              auto fwdarg_1_1_2_2_1_sr_sr_2_1 = ({
                                // Computing FMul
                                llvm::Value* fwdarg_1_1_2_2_1_sr_sr_2_2_0 = fwdarg_1_1_2_2_1_sr_2;
                                llvm::Value* fwdarg_1_1_2_2_1_sr_sr_2_2_1 = fwdarg_1_1_2_2_1_sr_1;
                                Builder2.CreateFMul(fwdarg_1_1_2_2_1_sr_sr_2_2_0, fwdarg_1_1_2_2_1_sr_sr_2_2_1);
                              });
                              Builder2.CreateFAdd(fwdarg_1_1_2_2_1_sr_sr_2_0, fwdarg_1_1_2_2_1_sr_sr_2_1);
                            });
                            Value *res = UndefValue::get(ArrayType::get(fwdarg_1_1_2_2_1_sr_sr_0->getType(), 2));
                            {
                              {
                                std::vector<unsigned> idxs;
                                idxs.push_back(0);
                                res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_2_1_sr_sr_0, idxs);
                              }
                              {
                                std::vector<unsigned> idxs;
                                idxs.push_back(1);
                                res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_2_1_sr_sr_1, idxs);
                              }
                            }
                             res;
                          });
                        });
                      });
                      Value* local_re = Builder2.CreateExtractValue(fwdarg_1_1_2_2_0, ArrayRef<unsigned>({0}), "re");
                      Value* local_im = Builder2.CreateExtractValue(fwdarg_1_1_2_2_0, ArrayRef<unsigned>({1}), "im");
                      llvm::Value *local_exp = nullptr;
                      ({
                        auto fwdarg_1_1_2_2_sr_0 = ({
                          // Computing FMul
                          auto fwdarg_1_1_2_2_sr_1_0 = ({
                            // Computing exp
                            auto fwdarg_1_1_2_2_sr_1_1_0 = local_re;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({fwdarg_1_1_2_2_sr_1_1_0->getType()})), ArrayRef<Value*>({fwdarg_1_1_2_2_sr_1_1_0}));
                          });
                          local_exp = fwdarg_1_1_2_2_sr_1_0;
                          auto fwdarg_1_1_2_2_sr_1_1 = ({
                            // Computing cos
                            auto fwdarg_1_1_2_2_sr_1_2_0 = local_im;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::cos, std::vector<Type*>({fwdarg_1_1_2_2_sr_1_2_0->getType()})), ArrayRef<Value*>({fwdarg_1_1_2_2_sr_1_2_0}));
                          });
                          Builder2.CreateFMul(fwdarg_1_1_2_2_sr_1_0, fwdarg_1_1_2_2_sr_1_1);
                        });
                        auto fwdarg_1_1_2_2_sr_1 = ({
                          // Computing FMul
                          auto fwdarg_1_1_2_2_sr_2_0 = local_exp;
                          auto fwdarg_1_1_2_2_sr_2_1 = ({
                            // Computing sin
                            auto fwdarg_1_1_2_2_sr_2_2_0 = local_im;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sin, std::vector<Type*>({fwdarg_1_1_2_2_sr_2_2_0->getType()})), ArrayRef<Value*>({fwdarg_1_1_2_2_sr_2_2_0}));
                          });
                          Builder2.CreateFMul(fwdarg_1_1_2_2_sr_2_0, fwdarg_1_1_2_2_sr_2_1);
                        });
                        Value *res = UndefValue::get(ArrayType::get(fwdarg_1_1_2_2_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_2_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_2_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                    Value* local_re1 = Builder2.CreateExtractValue(fwdarg_1_1_2_0, ArrayRef<unsigned>({0}), "re1");
                    Value* local_im1 = Builder2.CreateExtractValue(fwdarg_1_1_2_0, ArrayRef<unsigned>({1}), "im1");
                    Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1_2_1, ArrayRef<unsigned>({0}), "re2");
                    Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1_2_1, ArrayRef<unsigned>({1}), "im2");
                    ({
                      // Computing subroutine CFMul_splat
                      auto fwdarg_1_1_2_sr_0 = local_re1;
                      auto fwdarg_1_1_2_sr_1 = local_im1;
                      auto fwdarg_1_1_2_sr_2 = local_re2;
                      auto fwdarg_1_1_2_sr_3 = local_im2;
                      ({
                        auto fwdarg_1_1_2_sr_sr_0 = ({
                          // Computing FSub
                          auto fwdarg_1_1_2_sr_sr_1_0 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_1_1_2_sr_sr_1_1_0 = fwdarg_1_1_2_sr_0;
                            llvm::Value* fwdarg_1_1_2_sr_sr_1_1_1 = fwdarg_1_1_2_sr_2;
                            Builder2.CreateFMul(fwdarg_1_1_2_sr_sr_1_1_0, fwdarg_1_1_2_sr_sr_1_1_1);
                          });
                          auto fwdarg_1_1_2_sr_sr_1_1 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_1_1_2_sr_sr_1_2_0 = fwdarg_1_1_2_sr_1;
                            llvm::Value* fwdarg_1_1_2_sr_sr_1_2_1 = fwdarg_1_1_2_sr_3;
                            Builder2.CreateFMul(fwdarg_1_1_2_sr_sr_1_2_0, fwdarg_1_1_2_sr_sr_1_2_1);
                          });
                          Builder2.CreateFSub(fwdarg_1_1_2_sr_sr_1_0, fwdarg_1_1_2_sr_sr_1_1);
                        });
                        auto fwdarg_1_1_2_sr_sr_1 = ({
                          // Computing FAdd
                          auto fwdarg_1_1_2_sr_sr_2_0 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_1_1_2_sr_sr_2_1_0 = fwdarg_1_1_2_sr_0;
                            llvm::Value* fwdarg_1_1_2_sr_sr_2_1_1 = fwdarg_1_1_2_sr_3;
                            Builder2.CreateFMul(fwdarg_1_1_2_sr_sr_2_1_0, fwdarg_1_1_2_sr_sr_2_1_1);
                          });
                          auto fwdarg_1_1_2_sr_sr_2_1 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_1_1_2_sr_sr_2_2_0 = fwdarg_1_1_2_sr_2;
                            llvm::Value* fwdarg_1_1_2_sr_sr_2_2_1 = fwdarg_1_1_2_sr_1;
                            Builder2.CreateFMul(fwdarg_1_1_2_sr_sr_2_2_0, fwdarg_1_1_2_sr_sr_2_2_1);
                          });
                          Builder2.CreateFAdd(fwdarg_1_1_2_sr_sr_2_0, fwdarg_1_1_2_sr_sr_2_1);
                        });
                        Value *res = UndefValue::get(ArrayType::get(fwdarg_1_1_2_sr_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_sr_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_sr_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                  });
                  Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_0->getType(), 0, 0)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_0->getType(), 0, 1)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto fwdarg_1_1_sr_0 = local_re1;
                    auto fwdarg_1_1_sr_1 = local_im1;
                    auto fwdarg_1_1_sr_2 = local_re2;
                    auto fwdarg_1_1_sr_3 = local_im2;
                    ({
                      auto fwdarg_1_1_sr_sr_0 = ({
                        // Computing FSub
                        auto fwdarg_1_1_sr_sr_1_0 = ({
                          // Computing FMul
                          auto fwdarg_1_1_sr_sr_1_1_0 = fwdarg_1_1_sr_0;
                          llvm::Value* fwdarg_1_1_sr_sr_1_1_1 = fwdarg_1_1_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_1_0, idx), fwdarg_1_1_sr_sr_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_1_sr_sr_1_1 = ({
                          // Computing FMul
                          auto fwdarg_1_1_sr_sr_1_2_0 = fwdarg_1_1_sr_1;
                          llvm::Value* fwdarg_1_1_sr_sr_1_2_1 = fwdarg_1_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_2_0, idx), fwdarg_1_1_sr_sr_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_1_sr_sr_1 = ({
                        // Computing FAdd
                        auto fwdarg_1_1_sr_sr_2_0 = ({
                          // Computing FMul
                          auto fwdarg_1_1_sr_sr_2_1_0 = fwdarg_1_1_sr_0;
                          llvm::Value* fwdarg_1_1_sr_sr_2_1_1 = fwdarg_1_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_1_0, idx), fwdarg_1_1_sr_sr_2_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_1_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_1_sr_sr_2_2_0 = fwdarg_1_1_sr_2;
                          auto fwdarg_1_1_sr_sr_2_2_1 = fwdarg_1_1_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(fwdarg_1_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_2_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_0->getType() : getSubType(fwdarg_1_1_sr_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                ({
                  auto fwdarg_1_sr_0 = local_re;
                  auto fwdarg_1_sr_1 = ({
                    // Computing FNeg
                    auto fwdarg_1_sr_2_0 = local_im;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_1_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_2_0, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_sr_0->getType() : getSubType(fwdarg_1_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
              Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              ({
                auto fwdarg_sr_0 = local_re;
                auto fwdarg_sr_1 = local_im;
                Value *res = UndefValue::get(gutils->getShadowType(call.getType()));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(0);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_sr_0 : gutils->extractMeta(Builder2, fwdarg_sr_0, idx), idxs);
                  }
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(1);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_sr_1 : gutils->extractMeta(Builder2, fwdarg_sr_1, idx), idxs);
                  }
                }
                 res;
              });
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing subroutine ToStruct2
              auto revarg_0 = ({
                // Computing subroutine Conj
                auto revarg_1_0 = ({
                  // Computing subroutine CFMul
                  auto revarg_1_1_0 = ({
                    // Computing subroutine Conj
                    auto revarg_1_1_1_0 = dif;
                    Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_1_0->getType(), 0, 0)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_1_0->getType(), 0, 1)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    ({
                      auto revarg_1_1_1_sr_0 = local_re;
                      auto revarg_1_1_1_sr_1 = ({
                        // Computing FNeg
                        auto revarg_1_1_1_sr_2_0 = local_im;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_1_1_1_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_2_0, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_1_1_sr_0->getType() : getSubType(revarg_1_1_1_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_1_sr_0 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_1_sr_1 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                  auto revarg_1_1_1 = ({
                    // Computing subroutine CFMul
                    auto revarg_1_1_2_0 = ({
                      auto ty = call.getOperand(0)->getType();
                      Value *ret = nullptr;
                      if (auto ST = dyn_cast<StructType>(ty)) {
                        ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                        ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                      } else {
                        llvm::errs() << *ty << "\n";
                        assert(0 && "unhandled cfp");
                      }
                      ret;
                    })
;
                    auto revarg_1_1_2_1 = ({
                      // Computing subroutine CFExp
                      auto revarg_1_1_2_2_0 = ({
                        // Computing subroutine CFMul
                        auto revarg_1_1_2_2_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                        auto revarg_1_1_2_2_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                        Value* local_re1 = Builder2.CreateExtractValue(revarg_1_1_2_2_1_0, ArrayRef<unsigned>({0}), "re1");
                        Value* local_im1 = Builder2.CreateExtractValue(revarg_1_1_2_2_1_0, ArrayRef<unsigned>({1}), "im1");
                        Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1_2_2_1_1, ArrayRef<unsigned>({0}), "re2");
                        Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1_2_2_1_1, ArrayRef<unsigned>({1}), "im2");
                        ({
                          // Computing subroutine CFMul_splat
                          auto revarg_1_1_2_2_1_sr_0 = local_re1;
                          auto revarg_1_1_2_2_1_sr_1 = local_im1;
                          auto revarg_1_1_2_2_1_sr_2 = local_re2;
                          auto revarg_1_1_2_2_1_sr_3 = local_im2;
                          ({
                            auto revarg_1_1_2_2_1_sr_sr_0 = ({
                              // Computing FSub
                              auto revarg_1_1_2_2_1_sr_sr_1_0 = ({
                                // Computing FMul
                                llvm::Value* revarg_1_1_2_2_1_sr_sr_1_1_0 = revarg_1_1_2_2_1_sr_0;
                                llvm::Value* revarg_1_1_2_2_1_sr_sr_1_1_1 = revarg_1_1_2_2_1_sr_2;
                                Builder2.CreateFMul(revarg_1_1_2_2_1_sr_sr_1_1_0, revarg_1_1_2_2_1_sr_sr_1_1_1);
                              });
                              auto revarg_1_1_2_2_1_sr_sr_1_1 = ({
                                // Computing FMul
                                llvm::Value* revarg_1_1_2_2_1_sr_sr_1_2_0 = revarg_1_1_2_2_1_sr_1;
                                llvm::Value* revarg_1_1_2_2_1_sr_sr_1_2_1 = revarg_1_1_2_2_1_sr_3;
                                Builder2.CreateFMul(revarg_1_1_2_2_1_sr_sr_1_2_0, revarg_1_1_2_2_1_sr_sr_1_2_1);
                              });
                              Builder2.CreateFSub(revarg_1_1_2_2_1_sr_sr_1_0, revarg_1_1_2_2_1_sr_sr_1_1);
                            });
                            auto revarg_1_1_2_2_1_sr_sr_1 = ({
                              // Computing FAdd
                              auto revarg_1_1_2_2_1_sr_sr_2_0 = ({
                                // Computing FMul
                                llvm::Value* revarg_1_1_2_2_1_sr_sr_2_1_0 = revarg_1_1_2_2_1_sr_0;
                                llvm::Value* revarg_1_1_2_2_1_sr_sr_2_1_1 = revarg_1_1_2_2_1_sr_3;
                                Builder2.CreateFMul(revarg_1_1_2_2_1_sr_sr_2_1_0, revarg_1_1_2_2_1_sr_sr_2_1_1);
                              });
                              auto revarg_1_1_2_2_1_sr_sr_2_1 = ({
                                // Computing FMul
                                llvm::Value* revarg_1_1_2_2_1_sr_sr_2_2_0 = revarg_1_1_2_2_1_sr_2;
                                llvm::Value* revarg_1_1_2_2_1_sr_sr_2_2_1 = revarg_1_1_2_2_1_sr_1;
                                Builder2.CreateFMul(revarg_1_1_2_2_1_sr_sr_2_2_0, revarg_1_1_2_2_1_sr_sr_2_2_1);
                              });
                              Builder2.CreateFAdd(revarg_1_1_2_2_1_sr_sr_2_0, revarg_1_1_2_2_1_sr_sr_2_1);
                            });
                            Value *res = UndefValue::get(ArrayType::get(revarg_1_1_2_2_1_sr_sr_0->getType(), 2));
                            {
                              {
                                std::vector<unsigned> idxs;
                                idxs.push_back(0);
                                res = Builder2.CreateInsertValue(res, revarg_1_1_2_2_1_sr_sr_0, idxs);
                              }
                              {
                                std::vector<unsigned> idxs;
                                idxs.push_back(1);
                                res = Builder2.CreateInsertValue(res, revarg_1_1_2_2_1_sr_sr_1, idxs);
                              }
                            }
                             res;
                          });
                        });
                      });
                      Value* local_re = Builder2.CreateExtractValue(revarg_1_1_2_2_0, ArrayRef<unsigned>({0}), "re");
                      Value* local_im = Builder2.CreateExtractValue(revarg_1_1_2_2_0, ArrayRef<unsigned>({1}), "im");
                      llvm::Value *local_exp = nullptr;
                      ({
                        auto revarg_1_1_2_2_sr_0 = ({
                          // Computing FMul
                          auto revarg_1_1_2_2_sr_1_0 = ({
                            // Computing exp
                            auto revarg_1_1_2_2_sr_1_1_0 = local_re;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({revarg_1_1_2_2_sr_1_1_0->getType()})), ArrayRef<Value*>({revarg_1_1_2_2_sr_1_1_0}));
                          });
                          local_exp = revarg_1_1_2_2_sr_1_0;
                          auto revarg_1_1_2_2_sr_1_1 = ({
                            // Computing cos
                            auto revarg_1_1_2_2_sr_1_2_0 = local_im;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::cos, std::vector<Type*>({revarg_1_1_2_2_sr_1_2_0->getType()})), ArrayRef<Value*>({revarg_1_1_2_2_sr_1_2_0}));
                          });
                          Builder2.CreateFMul(revarg_1_1_2_2_sr_1_0, revarg_1_1_2_2_sr_1_1);
                        });
                        auto revarg_1_1_2_2_sr_1 = ({
                          // Computing FMul
                          auto revarg_1_1_2_2_sr_2_0 = local_exp;
                          auto revarg_1_1_2_2_sr_2_1 = ({
                            // Computing sin
                            auto revarg_1_1_2_2_sr_2_2_0 = local_im;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sin, std::vector<Type*>({revarg_1_1_2_2_sr_2_2_0->getType()})), ArrayRef<Value*>({revarg_1_1_2_2_sr_2_2_0}));
                          });
                          Builder2.CreateFMul(revarg_1_1_2_2_sr_2_0, revarg_1_1_2_2_sr_2_1);
                        });
                        Value *res = UndefValue::get(ArrayType::get(revarg_1_1_2_2_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, revarg_1_1_2_2_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, revarg_1_1_2_2_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                    Value* local_re1 = Builder2.CreateExtractValue(revarg_1_1_2_0, ArrayRef<unsigned>({0}), "re1");
                    Value* local_im1 = Builder2.CreateExtractValue(revarg_1_1_2_0, ArrayRef<unsigned>({1}), "im1");
                    Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1_2_1, ArrayRef<unsigned>({0}), "re2");
                    Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1_2_1, ArrayRef<unsigned>({1}), "im2");
                    ({
                      // Computing subroutine CFMul_splat
                      auto revarg_1_1_2_sr_0 = local_re1;
                      auto revarg_1_1_2_sr_1 = local_im1;
                      auto revarg_1_1_2_sr_2 = local_re2;
                      auto revarg_1_1_2_sr_3 = local_im2;
                      ({
                        auto revarg_1_1_2_sr_sr_0 = ({
                          // Computing FSub
                          auto revarg_1_1_2_sr_sr_1_0 = ({
                            // Computing FMul
                            llvm::Value* revarg_1_1_2_sr_sr_1_1_0 = revarg_1_1_2_sr_0;
                            llvm::Value* revarg_1_1_2_sr_sr_1_1_1 = revarg_1_1_2_sr_2;
                            Builder2.CreateFMul(revarg_1_1_2_sr_sr_1_1_0, revarg_1_1_2_sr_sr_1_1_1);
                          });
                          auto revarg_1_1_2_sr_sr_1_1 = ({
                            // Computing FMul
                            llvm::Value* revarg_1_1_2_sr_sr_1_2_0 = revarg_1_1_2_sr_1;
                            llvm::Value* revarg_1_1_2_sr_sr_1_2_1 = revarg_1_1_2_sr_3;
                            Builder2.CreateFMul(revarg_1_1_2_sr_sr_1_2_0, revarg_1_1_2_sr_sr_1_2_1);
                          });
                          Builder2.CreateFSub(revarg_1_1_2_sr_sr_1_0, revarg_1_1_2_sr_sr_1_1);
                        });
                        auto revarg_1_1_2_sr_sr_1 = ({
                          // Computing FAdd
                          auto revarg_1_1_2_sr_sr_2_0 = ({
                            // Computing FMul
                            llvm::Value* revarg_1_1_2_sr_sr_2_1_0 = revarg_1_1_2_sr_0;
                            llvm::Value* revarg_1_1_2_sr_sr_2_1_1 = revarg_1_1_2_sr_3;
                            Builder2.CreateFMul(revarg_1_1_2_sr_sr_2_1_0, revarg_1_1_2_sr_sr_2_1_1);
                          });
                          auto revarg_1_1_2_sr_sr_2_1 = ({
                            // Computing FMul
                            llvm::Value* revarg_1_1_2_sr_sr_2_2_0 = revarg_1_1_2_sr_2;
                            llvm::Value* revarg_1_1_2_sr_sr_2_2_1 = revarg_1_1_2_sr_1;
                            Builder2.CreateFMul(revarg_1_1_2_sr_sr_2_2_0, revarg_1_1_2_sr_sr_2_2_1);
                          });
                          Builder2.CreateFAdd(revarg_1_1_2_sr_sr_2_0, revarg_1_1_2_sr_sr_2_1);
                        });
                        Value *res = UndefValue::get(ArrayType::get(revarg_1_1_2_sr_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, revarg_1_1_2_sr_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, revarg_1_1_2_sr_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                  });
                  Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_0->getType(), 0, 0)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_0->getType(), 0, 1)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto revarg_1_1_sr_0 = local_re1;
                    auto revarg_1_1_sr_1 = local_im1;
                    auto revarg_1_1_sr_2 = local_re2;
                    auto revarg_1_1_sr_3 = local_im2;
                    ({
                      auto revarg_1_1_sr_sr_0 = ({
                        // Computing FSub
                        auto revarg_1_1_sr_sr_1_0 = ({
                          // Computing FMul
                          auto revarg_1_1_sr_sr_1_1_0 = revarg_1_1_sr_0;
                          llvm::Value* revarg_1_1_sr_sr_1_1_1 = revarg_1_1_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_1_0, idx), revarg_1_1_sr_sr_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_1_sr_sr_1_1 = ({
                          // Computing FMul
                          auto revarg_1_1_sr_sr_1_2_0 = revarg_1_1_sr_1;
                          llvm::Value* revarg_1_1_sr_sr_1_2_1 = revarg_1_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_2_0, idx), revarg_1_1_sr_sr_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_1_sr_sr_1 = ({
                        // Computing FAdd
                        auto revarg_1_1_sr_sr_2_0 = ({
                          // Computing FMul
                          auto revarg_1_1_sr_sr_2_1_0 = revarg_1_1_sr_0;
                          llvm::Value* revarg_1_1_sr_sr_2_1_1 = revarg_1_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_1_0, idx), revarg_1_1_sr_sr_2_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_1_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_1_sr_sr_2_2_0 = revarg_1_1_sr_2;
                          auto revarg_1_1_sr_sr_2_2_1 = revarg_1_1_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(revarg_1_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_2_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_0->getType() : getSubType(revarg_1_1_sr_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                ({
                  auto revarg_1_sr_0 = local_re;
                  auto revarg_1_sr_1 = ({
                    // Computing FNeg
                    auto revarg_1_sr_2_0 = local_im;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_1_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_2_0, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_sr_0->getType() : getSubType(revarg_1_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_0 : gutils->extractMeta(Builder2, revarg_1_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_1 : gutils->extractMeta(Builder2, revarg_1_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
              Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(revarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(revarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              ({
                auto revarg_sr_0 = local_re;
                auto revarg_sr_1 = local_im;
                Value *res = UndefValue::get(gutils->getShadowType(call.getType()));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(0);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_sr_0 : gutils->extractMeta(Builder2, revarg_sr_0, idx), idxs);
                  }
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(1);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_sr_1 : gutils->extractMeta(Builder2, revarg_sr_1, idx), idxs);
                  }
                }
                 res;
              });
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "Faddeeva_erfc") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
            Value *res = ({
              // Computing subroutine ToStruct2
              auto fwdnsrarg_0 = ({
                // Computing subroutine CFMul
                auto fwdnsrarg_1_0 = gutils->invertPointerM(call.getOperand(0), Builder2, TR.query(&call));
                auto fwdnsrarg_1_1 = ({
                  // Computing subroutine CFMul
                  auto fwdnsrarg_1_2_0 = ({
                    auto ty = call.getOperand(0)->getType();
                    Value *ret = nullptr;
                    if (auto ST = dyn_cast<StructType>(ty)) {
                      ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "-1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                      ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "-1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                    } else {
                      llvm::errs() << *ty << "\n";
                      assert(0 && "unhandled cfp");
                    }
                    ret;
                  })
;
                  auto fwdnsrarg_1_2_1 = ({
                    // Computing subroutine CFExp
                    auto fwdnsrarg_1_2_2_0 = ({
                      // Computing subroutine CFNeg
                      auto fwdnsrarg_1_2_2_1_0 = ({
                        // Computing subroutine CFMul
                        auto fwdnsrarg_1_2_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                        auto fwdnsrarg_1_2_2_1_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                        Value* local_re1 = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_1_1_0, ArrayRef<unsigned>({0}), "re1");
                        Value* local_im1 = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_1_1_0, ArrayRef<unsigned>({1}), "im1");
                        Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_1_1_1, ArrayRef<unsigned>({0}), "re2");
                        Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_1_1_1, ArrayRef<unsigned>({1}), "im2");
                        ({
                          // Computing subroutine CFMul_splat
                          auto fwdnsrarg_1_2_2_1_1_sr_0 = local_re1;
                          auto fwdnsrarg_1_2_2_1_1_sr_1 = local_im1;
                          auto fwdnsrarg_1_2_2_1_1_sr_2 = local_re2;
                          auto fwdnsrarg_1_2_2_1_1_sr_3 = local_im2;
                          ({
                            auto fwdnsrarg_1_2_2_1_1_sr_sr_0 = ({
                              // Computing FSub
                              auto fwdnsrarg_1_2_2_1_1_sr_sr_1_0 = ({
                                // Computing FMul
                                llvm::Value* fwdnsrarg_1_2_2_1_1_sr_sr_1_1_0 = fwdnsrarg_1_2_2_1_1_sr_0;
                                llvm::Value* fwdnsrarg_1_2_2_1_1_sr_sr_1_1_1 = fwdnsrarg_1_2_2_1_1_sr_2;
                                Builder2.CreateFMul(fwdnsrarg_1_2_2_1_1_sr_sr_1_1_0, fwdnsrarg_1_2_2_1_1_sr_sr_1_1_1);
                              });
                              auto fwdnsrarg_1_2_2_1_1_sr_sr_1_1 = ({
                                // Computing FMul
                                llvm::Value* fwdnsrarg_1_2_2_1_1_sr_sr_1_2_0 = fwdnsrarg_1_2_2_1_1_sr_1;
                                llvm::Value* fwdnsrarg_1_2_2_1_1_sr_sr_1_2_1 = fwdnsrarg_1_2_2_1_1_sr_3;
                                Builder2.CreateFMul(fwdnsrarg_1_2_2_1_1_sr_sr_1_2_0, fwdnsrarg_1_2_2_1_1_sr_sr_1_2_1);
                              });
                              Builder2.CreateFSub(fwdnsrarg_1_2_2_1_1_sr_sr_1_0, fwdnsrarg_1_2_2_1_1_sr_sr_1_1);
                            });
                            auto fwdnsrarg_1_2_2_1_1_sr_sr_1 = ({
                              // Computing FAdd
                              auto fwdnsrarg_1_2_2_1_1_sr_sr_2_0 = ({
                                // Computing FMul
                                llvm::Value* fwdnsrarg_1_2_2_1_1_sr_sr_2_1_0 = fwdnsrarg_1_2_2_1_1_sr_0;
                                llvm::Value* fwdnsrarg_1_2_2_1_1_sr_sr_2_1_1 = fwdnsrarg_1_2_2_1_1_sr_3;
                                Builder2.CreateFMul(fwdnsrarg_1_2_2_1_1_sr_sr_2_1_0, fwdnsrarg_1_2_2_1_1_sr_sr_2_1_1);
                              });
                              auto fwdnsrarg_1_2_2_1_1_sr_sr_2_1 = ({
                                // Computing FMul
                                llvm::Value* fwdnsrarg_1_2_2_1_1_sr_sr_2_2_0 = fwdnsrarg_1_2_2_1_1_sr_2;
                                llvm::Value* fwdnsrarg_1_2_2_1_1_sr_sr_2_2_1 = fwdnsrarg_1_2_2_1_1_sr_1;
                                Builder2.CreateFMul(fwdnsrarg_1_2_2_1_1_sr_sr_2_2_0, fwdnsrarg_1_2_2_1_1_sr_sr_2_2_1);
                              });
                              Builder2.CreateFAdd(fwdnsrarg_1_2_2_1_1_sr_sr_2_0, fwdnsrarg_1_2_2_1_1_sr_sr_2_1);
                            });
                            Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_1_2_2_1_1_sr_sr_0->getType(), 2));
                            {
                              {
                                std::vector<unsigned> idxs;
                                idxs.push_back(0);
                                res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_2_1_1_sr_sr_0, idxs);
                              }
                              {
                                std::vector<unsigned> idxs;
                                idxs.push_back(1);
                                res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_2_1_1_sr_sr_1, idxs);
                              }
                            }
                             res;
                          });
                        });
                      });
                      Value* local_re = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_1_0, ArrayRef<unsigned>({0}), "re");
                      Value* local_im = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_1_0, ArrayRef<unsigned>({1}), "im");
                      ({
                        auto fwdnsrarg_1_2_2_1_sr_0 = ({
                          // Computing FNeg
                          auto fwdnsrarg_1_2_2_1_sr_1_0 = local_re;
                          Builder2.CreateFNeg(fwdnsrarg_1_2_2_1_sr_1_0);
                        });
                        auto fwdnsrarg_1_2_2_1_sr_1 = ({
                          // Computing FNeg
                          auto fwdnsrarg_1_2_2_1_sr_2_0 = local_im;
                          Builder2.CreateFNeg(fwdnsrarg_1_2_2_1_sr_2_0);
                        });
                        Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_1_2_2_1_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_2_1_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_2_1_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                    Value* local_re = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_0, ArrayRef<unsigned>({0}), "re");
                    Value* local_im = Builder2.CreateExtractValue(fwdnsrarg_1_2_2_0, ArrayRef<unsigned>({1}), "im");
                    llvm::Value *local_exp = nullptr;
                    ({
                      auto fwdnsrarg_1_2_2_sr_0 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_2_2_sr_1_0 = ({
                          // Computing exp
                          auto fwdnsrarg_1_2_2_sr_1_1_0 = local_re;
                          Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({fwdnsrarg_1_2_2_sr_1_1_0->getType()})), ArrayRef<Value*>({fwdnsrarg_1_2_2_sr_1_1_0}));
                        });
                        local_exp = fwdnsrarg_1_2_2_sr_1_0;
                        auto fwdnsrarg_1_2_2_sr_1_1 = ({
                          // Computing cos
                          auto fwdnsrarg_1_2_2_sr_1_2_0 = local_im;
                          Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::cos, std::vector<Type*>({fwdnsrarg_1_2_2_sr_1_2_0->getType()})), ArrayRef<Value*>({fwdnsrarg_1_2_2_sr_1_2_0}));
                        });
                        Builder2.CreateFMul(fwdnsrarg_1_2_2_sr_1_0, fwdnsrarg_1_2_2_sr_1_1);
                      });
                      auto fwdnsrarg_1_2_2_sr_1 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_2_2_sr_2_0 = local_exp;
                        auto fwdnsrarg_1_2_2_sr_2_1 = ({
                          // Computing sin
                          auto fwdnsrarg_1_2_2_sr_2_2_0 = local_im;
                          Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sin, std::vector<Type*>({fwdnsrarg_1_2_2_sr_2_2_0->getType()})), ArrayRef<Value*>({fwdnsrarg_1_2_2_sr_2_2_0}));
                        });
                        Builder2.CreateFMul(fwdnsrarg_1_2_2_sr_2_0, fwdnsrarg_1_2_2_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_1_2_2_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_2_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_2_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                  Value* local_re1 = Builder2.CreateExtractValue(fwdnsrarg_1_2_0, ArrayRef<unsigned>({0}), "re1");
                  Value* local_im1 = Builder2.CreateExtractValue(fwdnsrarg_1_2_0, ArrayRef<unsigned>({1}), "im1");
                  Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_1_2_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_1_2_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto fwdnsrarg_1_2_sr_0 = local_re1;
                    auto fwdnsrarg_1_2_sr_1 = local_im1;
                    auto fwdnsrarg_1_2_sr_2 = local_re2;
                    auto fwdnsrarg_1_2_sr_3 = local_im2;
                    ({
                      auto fwdnsrarg_1_2_sr_sr_0 = ({
                        // Computing FSub
                        auto fwdnsrarg_1_2_sr_sr_1_0 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_1_2_sr_sr_1_1_0 = fwdnsrarg_1_2_sr_0;
                          llvm::Value* fwdnsrarg_1_2_sr_sr_1_1_1 = fwdnsrarg_1_2_sr_2;
                          Builder2.CreateFMul(fwdnsrarg_1_2_sr_sr_1_1_0, fwdnsrarg_1_2_sr_sr_1_1_1);
                        });
                        auto fwdnsrarg_1_2_sr_sr_1_1 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_1_2_sr_sr_1_2_0 = fwdnsrarg_1_2_sr_1;
                          llvm::Value* fwdnsrarg_1_2_sr_sr_1_2_1 = fwdnsrarg_1_2_sr_3;
                          Builder2.CreateFMul(fwdnsrarg_1_2_sr_sr_1_2_0, fwdnsrarg_1_2_sr_sr_1_2_1);
                        });
                        Builder2.CreateFSub(fwdnsrarg_1_2_sr_sr_1_0, fwdnsrarg_1_2_sr_sr_1_1);
                      });
                      auto fwdnsrarg_1_2_sr_sr_1 = ({
                        // Computing FAdd
                        auto fwdnsrarg_1_2_sr_sr_2_0 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_1_2_sr_sr_2_1_0 = fwdnsrarg_1_2_sr_0;
                          llvm::Value* fwdnsrarg_1_2_sr_sr_2_1_1 = fwdnsrarg_1_2_sr_3;
                          Builder2.CreateFMul(fwdnsrarg_1_2_sr_sr_2_1_0, fwdnsrarg_1_2_sr_sr_2_1_1);
                        });
                        auto fwdnsrarg_1_2_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_1_2_sr_sr_2_2_0 = fwdnsrarg_1_2_sr_2;
                          llvm::Value* fwdnsrarg_1_2_sr_sr_2_2_1 = fwdnsrarg_1_2_sr_1;
                          Builder2.CreateFMul(fwdnsrarg_1_2_sr_sr_2_2_0, fwdnsrarg_1_2_sr_sr_2_2_1);
                        });
                        Builder2.CreateFAdd(fwdnsrarg_1_2_sr_sr_2_0, fwdnsrarg_1_2_sr_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_1_2_sr_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_sr_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_sr_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFMul_splat
                  auto fwdnsrarg_1_sr_0 = local_re1;
                  auto fwdnsrarg_1_sr_1 = local_im1;
                  auto fwdnsrarg_1_sr_2 = local_re2;
                  auto fwdnsrarg_1_sr_3 = local_im2;
                  ({
                    auto fwdnsrarg_1_sr_sr_0 = ({
                      // Computing FSub
                      auto fwdnsrarg_1_sr_sr_1_0 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_sr_sr_1_1_0 = fwdnsrarg_1_sr_0;
                        llvm::Value* fwdnsrarg_1_sr_sr_1_1_1 = fwdnsrarg_1_sr_2;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_1_1_0, idx), fwdnsrarg_1_sr_sr_1_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_1_sr_sr_1_1 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_sr_sr_1_2_0 = fwdnsrarg_1_sr_1;
                        llvm::Value* fwdnsrarg_1_sr_sr_1_2_1 = fwdnsrarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_1_2_0, idx), fwdnsrarg_1_sr_sr_1_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_1_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdnsrarg_1_sr_sr_1 = ({
                      // Computing FAdd
                      auto fwdnsrarg_1_sr_sr_2_0 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_sr_sr_2_1_0 = fwdnsrarg_1_sr_0;
                        llvm::Value* fwdnsrarg_1_sr_sr_2_1_1 = fwdnsrarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_2_1_0, idx), fwdnsrarg_1_sr_sr_2_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_1_sr_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_1_sr_sr_2_2_0 = fwdnsrarg_1_sr_2;
                        auto fwdnsrarg_1_sr_sr_2_2_1 = fwdnsrarg_1_sr_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul(fwdnsrarg_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_2_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_0->getType() : getSubType(fwdnsrarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              ({
                auto fwdnsrarg_sr_0 = local_re;
                auto fwdnsrarg_sr_1 = local_im;
                Value *res = UndefValue::get(gutils->getShadowType(call.getType()));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(0);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_0, idx), idxs);
                  }
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(1);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_1, idx), idxs);
                  }
                }
                 res;
              });
            });
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing subroutine ToStruct2
              auto fwdarg_0 = ({
                // Computing subroutine Conj
                auto fwdarg_1_0 = ({
                  // Computing subroutine CFMul
                  auto fwdarg_1_1_0 = ({
                    // Computing subroutine Conj
                    auto fwdarg_1_1_1_0 = dif;
                    Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_1_0->getType(), 0, 0)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_1_0->getType(), 0, 1)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    ({
                      auto fwdarg_1_1_1_sr_0 = local_re;
                      auto fwdarg_1_1_1_sr_1 = ({
                        // Computing FNeg
                        auto fwdarg_1_1_1_sr_2_0 = local_im;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_2_0, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_0->getType() : getSubType(fwdarg_1_1_1_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                  auto fwdarg_1_1_1 = ({
                    // Computing subroutine CFMul
                    auto fwdarg_1_1_2_0 = ({
                      auto ty = call.getOperand(0)->getType();
                      Value *ret = nullptr;
                      if (auto ST = dyn_cast<StructType>(ty)) {
                        ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "-1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                        ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "-1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                      } else {
                        llvm::errs() << *ty << "\n";
                        assert(0 && "unhandled cfp");
                      }
                      ret;
                    })
;
                    auto fwdarg_1_1_2_1 = ({
                      // Computing subroutine CFExp
                      auto fwdarg_1_1_2_2_0 = ({
                        // Computing subroutine CFNeg
                        auto fwdarg_1_1_2_2_1_0 = ({
                          // Computing subroutine CFMul
                          auto fwdarg_1_1_2_2_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                          auto fwdarg_1_1_2_2_1_1_1 = gutils->getNewFromOriginal(call.getOperand(0));
                          Value* local_re1 = Builder2.CreateExtractValue(fwdarg_1_1_2_2_1_1_0, ArrayRef<unsigned>({0}), "re1");
                          Value* local_im1 = Builder2.CreateExtractValue(fwdarg_1_1_2_2_1_1_0, ArrayRef<unsigned>({1}), "im1");
                          Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1_2_2_1_1_1, ArrayRef<unsigned>({0}), "re2");
                          Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1_2_2_1_1_1, ArrayRef<unsigned>({1}), "im2");
                          ({
                            // Computing subroutine CFMul_splat
                            auto fwdarg_1_1_2_2_1_1_sr_0 = local_re1;
                            auto fwdarg_1_1_2_2_1_1_sr_1 = local_im1;
                            auto fwdarg_1_1_2_2_1_1_sr_2 = local_re2;
                            auto fwdarg_1_1_2_2_1_1_sr_3 = local_im2;
                            ({
                              auto fwdarg_1_1_2_2_1_1_sr_sr_0 = ({
                                // Computing FSub
                                auto fwdarg_1_1_2_2_1_1_sr_sr_1_0 = ({
                                  // Computing FMul
                                  llvm::Value* fwdarg_1_1_2_2_1_1_sr_sr_1_1_0 = fwdarg_1_1_2_2_1_1_sr_0;
                                  llvm::Value* fwdarg_1_1_2_2_1_1_sr_sr_1_1_1 = fwdarg_1_1_2_2_1_1_sr_2;
                                  Builder2.CreateFMul(fwdarg_1_1_2_2_1_1_sr_sr_1_1_0, fwdarg_1_1_2_2_1_1_sr_sr_1_1_1);
                                });
                                auto fwdarg_1_1_2_2_1_1_sr_sr_1_1 = ({
                                  // Computing FMul
                                  llvm::Value* fwdarg_1_1_2_2_1_1_sr_sr_1_2_0 = fwdarg_1_1_2_2_1_1_sr_1;
                                  llvm::Value* fwdarg_1_1_2_2_1_1_sr_sr_1_2_1 = fwdarg_1_1_2_2_1_1_sr_3;
                                  Builder2.CreateFMul(fwdarg_1_1_2_2_1_1_sr_sr_1_2_0, fwdarg_1_1_2_2_1_1_sr_sr_1_2_1);
                                });
                                Builder2.CreateFSub(fwdarg_1_1_2_2_1_1_sr_sr_1_0, fwdarg_1_1_2_2_1_1_sr_sr_1_1);
                              });
                              auto fwdarg_1_1_2_2_1_1_sr_sr_1 = ({
                                // Computing FAdd
                                auto fwdarg_1_1_2_2_1_1_sr_sr_2_0 = ({
                                  // Computing FMul
                                  llvm::Value* fwdarg_1_1_2_2_1_1_sr_sr_2_1_0 = fwdarg_1_1_2_2_1_1_sr_0;
                                  llvm::Value* fwdarg_1_1_2_2_1_1_sr_sr_2_1_1 = fwdarg_1_1_2_2_1_1_sr_3;
                                  Builder2.CreateFMul(fwdarg_1_1_2_2_1_1_sr_sr_2_1_0, fwdarg_1_1_2_2_1_1_sr_sr_2_1_1);
                                });
                                auto fwdarg_1_1_2_2_1_1_sr_sr_2_1 = ({
                                  // Computing FMul
                                  llvm::Value* fwdarg_1_1_2_2_1_1_sr_sr_2_2_0 = fwdarg_1_1_2_2_1_1_sr_2;
                                  llvm::Value* fwdarg_1_1_2_2_1_1_sr_sr_2_2_1 = fwdarg_1_1_2_2_1_1_sr_1;
                                  Builder2.CreateFMul(fwdarg_1_1_2_2_1_1_sr_sr_2_2_0, fwdarg_1_1_2_2_1_1_sr_sr_2_2_1);
                                });
                                Builder2.CreateFAdd(fwdarg_1_1_2_2_1_1_sr_sr_2_0, fwdarg_1_1_2_2_1_1_sr_sr_2_1);
                              });
                              Value *res = UndefValue::get(ArrayType::get(fwdarg_1_1_2_2_1_1_sr_sr_0->getType(), 2));
                              {
                                {
                                  std::vector<unsigned> idxs;
                                  idxs.push_back(0);
                                  res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_2_1_1_sr_sr_0, idxs);
                                }
                                {
                                  std::vector<unsigned> idxs;
                                  idxs.push_back(1);
                                  res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_2_1_1_sr_sr_1, idxs);
                                }
                              }
                               res;
                            });
                          });
                        });
                        Value* local_re = Builder2.CreateExtractValue(fwdarg_1_1_2_2_1_0, ArrayRef<unsigned>({0}), "re");
                        Value* local_im = Builder2.CreateExtractValue(fwdarg_1_1_2_2_1_0, ArrayRef<unsigned>({1}), "im");
                        ({
                          auto fwdarg_1_1_2_2_1_sr_0 = ({
                            // Computing FNeg
                            auto fwdarg_1_1_2_2_1_sr_1_0 = local_re;
                            Builder2.CreateFNeg(fwdarg_1_1_2_2_1_sr_1_0);
                          });
                          auto fwdarg_1_1_2_2_1_sr_1 = ({
                            // Computing FNeg
                            auto fwdarg_1_1_2_2_1_sr_2_0 = local_im;
                            Builder2.CreateFNeg(fwdarg_1_1_2_2_1_sr_2_0);
                          });
                          Value *res = UndefValue::get(ArrayType::get(fwdarg_1_1_2_2_1_sr_0->getType(), 2));
                          {
                            {
                              std::vector<unsigned> idxs;
                              idxs.push_back(0);
                              res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_2_1_sr_0, idxs);
                            }
                            {
                              std::vector<unsigned> idxs;
                              idxs.push_back(1);
                              res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_2_1_sr_1, idxs);
                            }
                          }
                           res;
                        });
                      });
                      Value* local_re = Builder2.CreateExtractValue(fwdarg_1_1_2_2_0, ArrayRef<unsigned>({0}), "re");
                      Value* local_im = Builder2.CreateExtractValue(fwdarg_1_1_2_2_0, ArrayRef<unsigned>({1}), "im");
                      llvm::Value *local_exp = nullptr;
                      ({
                        auto fwdarg_1_1_2_2_sr_0 = ({
                          // Computing FMul
                          auto fwdarg_1_1_2_2_sr_1_0 = ({
                            // Computing exp
                            auto fwdarg_1_1_2_2_sr_1_1_0 = local_re;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({fwdarg_1_1_2_2_sr_1_1_0->getType()})), ArrayRef<Value*>({fwdarg_1_1_2_2_sr_1_1_0}));
                          });
                          local_exp = fwdarg_1_1_2_2_sr_1_0;
                          auto fwdarg_1_1_2_2_sr_1_1 = ({
                            // Computing cos
                            auto fwdarg_1_1_2_2_sr_1_2_0 = local_im;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::cos, std::vector<Type*>({fwdarg_1_1_2_2_sr_1_2_0->getType()})), ArrayRef<Value*>({fwdarg_1_1_2_2_sr_1_2_0}));
                          });
                          Builder2.CreateFMul(fwdarg_1_1_2_2_sr_1_0, fwdarg_1_1_2_2_sr_1_1);
                        });
                        auto fwdarg_1_1_2_2_sr_1 = ({
                          // Computing FMul
                          auto fwdarg_1_1_2_2_sr_2_0 = local_exp;
                          auto fwdarg_1_1_2_2_sr_2_1 = ({
                            // Computing sin
                            auto fwdarg_1_1_2_2_sr_2_2_0 = local_im;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sin, std::vector<Type*>({fwdarg_1_1_2_2_sr_2_2_0->getType()})), ArrayRef<Value*>({fwdarg_1_1_2_2_sr_2_2_0}));
                          });
                          Builder2.CreateFMul(fwdarg_1_1_2_2_sr_2_0, fwdarg_1_1_2_2_sr_2_1);
                        });
                        Value *res = UndefValue::get(ArrayType::get(fwdarg_1_1_2_2_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_2_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_2_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                    Value* local_re1 = Builder2.CreateExtractValue(fwdarg_1_1_2_0, ArrayRef<unsigned>({0}), "re1");
                    Value* local_im1 = Builder2.CreateExtractValue(fwdarg_1_1_2_0, ArrayRef<unsigned>({1}), "im1");
                    Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1_2_1, ArrayRef<unsigned>({0}), "re2");
                    Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1_2_1, ArrayRef<unsigned>({1}), "im2");
                    ({
                      // Computing subroutine CFMul_splat
                      auto fwdarg_1_1_2_sr_0 = local_re1;
                      auto fwdarg_1_1_2_sr_1 = local_im1;
                      auto fwdarg_1_1_2_sr_2 = local_re2;
                      auto fwdarg_1_1_2_sr_3 = local_im2;
                      ({
                        auto fwdarg_1_1_2_sr_sr_0 = ({
                          // Computing FSub
                          auto fwdarg_1_1_2_sr_sr_1_0 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_1_1_2_sr_sr_1_1_0 = fwdarg_1_1_2_sr_0;
                            llvm::Value* fwdarg_1_1_2_sr_sr_1_1_1 = fwdarg_1_1_2_sr_2;
                            Builder2.CreateFMul(fwdarg_1_1_2_sr_sr_1_1_0, fwdarg_1_1_2_sr_sr_1_1_1);
                          });
                          auto fwdarg_1_1_2_sr_sr_1_1 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_1_1_2_sr_sr_1_2_0 = fwdarg_1_1_2_sr_1;
                            llvm::Value* fwdarg_1_1_2_sr_sr_1_2_1 = fwdarg_1_1_2_sr_3;
                            Builder2.CreateFMul(fwdarg_1_1_2_sr_sr_1_2_0, fwdarg_1_1_2_sr_sr_1_2_1);
                          });
                          Builder2.CreateFSub(fwdarg_1_1_2_sr_sr_1_0, fwdarg_1_1_2_sr_sr_1_1);
                        });
                        auto fwdarg_1_1_2_sr_sr_1 = ({
                          // Computing FAdd
                          auto fwdarg_1_1_2_sr_sr_2_0 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_1_1_2_sr_sr_2_1_0 = fwdarg_1_1_2_sr_0;
                            llvm::Value* fwdarg_1_1_2_sr_sr_2_1_1 = fwdarg_1_1_2_sr_3;
                            Builder2.CreateFMul(fwdarg_1_1_2_sr_sr_2_1_0, fwdarg_1_1_2_sr_sr_2_1_1);
                          });
                          auto fwdarg_1_1_2_sr_sr_2_1 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_1_1_2_sr_sr_2_2_0 = fwdarg_1_1_2_sr_2;
                            llvm::Value* fwdarg_1_1_2_sr_sr_2_2_1 = fwdarg_1_1_2_sr_1;
                            Builder2.CreateFMul(fwdarg_1_1_2_sr_sr_2_2_0, fwdarg_1_1_2_sr_sr_2_2_1);
                          });
                          Builder2.CreateFAdd(fwdarg_1_1_2_sr_sr_2_0, fwdarg_1_1_2_sr_sr_2_1);
                        });
                        Value *res = UndefValue::get(ArrayType::get(fwdarg_1_1_2_sr_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_sr_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_sr_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                  });
                  Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_0->getType(), 0, 0)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_0->getType(), 0, 1)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto fwdarg_1_1_sr_0 = local_re1;
                    auto fwdarg_1_1_sr_1 = local_im1;
                    auto fwdarg_1_1_sr_2 = local_re2;
                    auto fwdarg_1_1_sr_3 = local_im2;
                    ({
                      auto fwdarg_1_1_sr_sr_0 = ({
                        // Computing FSub
                        auto fwdarg_1_1_sr_sr_1_0 = ({
                          // Computing FMul
                          auto fwdarg_1_1_sr_sr_1_1_0 = fwdarg_1_1_sr_0;
                          llvm::Value* fwdarg_1_1_sr_sr_1_1_1 = fwdarg_1_1_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_1_0, idx), fwdarg_1_1_sr_sr_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_1_sr_sr_1_1 = ({
                          // Computing FMul
                          auto fwdarg_1_1_sr_sr_1_2_0 = fwdarg_1_1_sr_1;
                          llvm::Value* fwdarg_1_1_sr_sr_1_2_1 = fwdarg_1_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_2_0, idx), fwdarg_1_1_sr_sr_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_1_sr_sr_1 = ({
                        // Computing FAdd
                        auto fwdarg_1_1_sr_sr_2_0 = ({
                          // Computing FMul
                          auto fwdarg_1_1_sr_sr_2_1_0 = fwdarg_1_1_sr_0;
                          llvm::Value* fwdarg_1_1_sr_sr_2_1_1 = fwdarg_1_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_1_0, idx), fwdarg_1_1_sr_sr_2_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_1_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_1_sr_sr_2_2_0 = fwdarg_1_1_sr_2;
                          auto fwdarg_1_1_sr_sr_2_2_1 = fwdarg_1_1_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(fwdarg_1_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_2_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_0->getType() : getSubType(fwdarg_1_1_sr_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                ({
                  auto fwdarg_1_sr_0 = local_re;
                  auto fwdarg_1_sr_1 = ({
                    // Computing FNeg
                    auto fwdarg_1_sr_2_0 = local_im;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_1_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_2_0, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_sr_0->getType() : getSubType(fwdarg_1_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
              Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              ({
                auto fwdarg_sr_0 = local_re;
                auto fwdarg_sr_1 = local_im;
                Value *res = UndefValue::get(gutils->getShadowType(call.getType()));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(0);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_sr_0 : gutils->extractMeta(Builder2, fwdarg_sr_0, idx), idxs);
                  }
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(1);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_sr_1 : gutils->extractMeta(Builder2, fwdarg_sr_1, idx), idxs);
                  }
                }
                 res;
              });
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing subroutine ToStruct2
              auto revarg_0 = ({
                // Computing subroutine Conj
                auto revarg_1_0 = ({
                  // Computing subroutine CFMul
                  auto revarg_1_1_0 = ({
                    // Computing subroutine Conj
                    auto revarg_1_1_1_0 = dif;
                    Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_1_0->getType(), 0, 0)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_1_0->getType(), 0, 1)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    ({
                      auto revarg_1_1_1_sr_0 = local_re;
                      auto revarg_1_1_1_sr_1 = ({
                        // Computing FNeg
                        auto revarg_1_1_1_sr_2_0 = local_im;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_1_1_1_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_2_0, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_1_1_sr_0->getType() : getSubType(revarg_1_1_1_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_1_sr_0 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_1_sr_1 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                  auto revarg_1_1_1 = ({
                    // Computing subroutine CFMul
                    auto revarg_1_1_2_0 = ({
                      auto ty = call.getOperand(0)->getType();
                      Value *ret = nullptr;
                      if (auto ST = dyn_cast<StructType>(ty)) {
                        ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "-1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                        ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "-1.1283791670955125738961589031215451716881012586580"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                      } else {
                        llvm::errs() << *ty << "\n";
                        assert(0 && "unhandled cfp");
                      }
                      ret;
                    })
;
                    auto revarg_1_1_2_1 = ({
                      // Computing subroutine CFExp
                      auto revarg_1_1_2_2_0 = ({
                        // Computing subroutine CFNeg
                        auto revarg_1_1_2_2_1_0 = ({
                          // Computing subroutine CFMul
                          auto revarg_1_1_2_2_1_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                          auto revarg_1_1_2_2_1_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                          Value* local_re1 = Builder2.CreateExtractValue(revarg_1_1_2_2_1_1_0, ArrayRef<unsigned>({0}), "re1");
                          Value* local_im1 = Builder2.CreateExtractValue(revarg_1_1_2_2_1_1_0, ArrayRef<unsigned>({1}), "im1");
                          Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1_2_2_1_1_1, ArrayRef<unsigned>({0}), "re2");
                          Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1_2_2_1_1_1, ArrayRef<unsigned>({1}), "im2");
                          ({
                            // Computing subroutine CFMul_splat
                            auto revarg_1_1_2_2_1_1_sr_0 = local_re1;
                            auto revarg_1_1_2_2_1_1_sr_1 = local_im1;
                            auto revarg_1_1_2_2_1_1_sr_2 = local_re2;
                            auto revarg_1_1_2_2_1_1_sr_3 = local_im2;
                            ({
                              auto revarg_1_1_2_2_1_1_sr_sr_0 = ({
                                // Computing FSub
                                auto revarg_1_1_2_2_1_1_sr_sr_1_0 = ({
                                  // Computing FMul
                                  llvm::Value* revarg_1_1_2_2_1_1_sr_sr_1_1_0 = revarg_1_1_2_2_1_1_sr_0;
                                  llvm::Value* revarg_1_1_2_2_1_1_sr_sr_1_1_1 = revarg_1_1_2_2_1_1_sr_2;
                                  Builder2.CreateFMul(revarg_1_1_2_2_1_1_sr_sr_1_1_0, revarg_1_1_2_2_1_1_sr_sr_1_1_1);
                                });
                                auto revarg_1_1_2_2_1_1_sr_sr_1_1 = ({
                                  // Computing FMul
                                  llvm::Value* revarg_1_1_2_2_1_1_sr_sr_1_2_0 = revarg_1_1_2_2_1_1_sr_1;
                                  llvm::Value* revarg_1_1_2_2_1_1_sr_sr_1_2_1 = revarg_1_1_2_2_1_1_sr_3;
                                  Builder2.CreateFMul(revarg_1_1_2_2_1_1_sr_sr_1_2_0, revarg_1_1_2_2_1_1_sr_sr_1_2_1);
                                });
                                Builder2.CreateFSub(revarg_1_1_2_2_1_1_sr_sr_1_0, revarg_1_1_2_2_1_1_sr_sr_1_1);
                              });
                              auto revarg_1_1_2_2_1_1_sr_sr_1 = ({
                                // Computing FAdd
                                auto revarg_1_1_2_2_1_1_sr_sr_2_0 = ({
                                  // Computing FMul
                                  llvm::Value* revarg_1_1_2_2_1_1_sr_sr_2_1_0 = revarg_1_1_2_2_1_1_sr_0;
                                  llvm::Value* revarg_1_1_2_2_1_1_sr_sr_2_1_1 = revarg_1_1_2_2_1_1_sr_3;
                                  Builder2.CreateFMul(revarg_1_1_2_2_1_1_sr_sr_2_1_0, revarg_1_1_2_2_1_1_sr_sr_2_1_1);
                                });
                                auto revarg_1_1_2_2_1_1_sr_sr_2_1 = ({
                                  // Computing FMul
                                  llvm::Value* revarg_1_1_2_2_1_1_sr_sr_2_2_0 = revarg_1_1_2_2_1_1_sr_2;
                                  llvm::Value* revarg_1_1_2_2_1_1_sr_sr_2_2_1 = revarg_1_1_2_2_1_1_sr_1;
                                  Builder2.CreateFMul(revarg_1_1_2_2_1_1_sr_sr_2_2_0, revarg_1_1_2_2_1_1_sr_sr_2_2_1);
                                });
                                Builder2.CreateFAdd(revarg_1_1_2_2_1_1_sr_sr_2_0, revarg_1_1_2_2_1_1_sr_sr_2_1);
                              });
                              Value *res = UndefValue::get(ArrayType::get(revarg_1_1_2_2_1_1_sr_sr_0->getType(), 2));
                              {
                                {
                                  std::vector<unsigned> idxs;
                                  idxs.push_back(0);
                                  res = Builder2.CreateInsertValue(res, revarg_1_1_2_2_1_1_sr_sr_0, idxs);
                                }
                                {
                                  std::vector<unsigned> idxs;
                                  idxs.push_back(1);
                                  res = Builder2.CreateInsertValue(res, revarg_1_1_2_2_1_1_sr_sr_1, idxs);
                                }
                              }
                               res;
                            });
                          });
                        });
                        Value* local_re = Builder2.CreateExtractValue(revarg_1_1_2_2_1_0, ArrayRef<unsigned>({0}), "re");
                        Value* local_im = Builder2.CreateExtractValue(revarg_1_1_2_2_1_0, ArrayRef<unsigned>({1}), "im");
                        ({
                          auto revarg_1_1_2_2_1_sr_0 = ({
                            // Computing FNeg
                            auto revarg_1_1_2_2_1_sr_1_0 = local_re;
                            Builder2.CreateFNeg(revarg_1_1_2_2_1_sr_1_0);
                          });
                          auto revarg_1_1_2_2_1_sr_1 = ({
                            // Computing FNeg
                            auto revarg_1_1_2_2_1_sr_2_0 = local_im;
                            Builder2.CreateFNeg(revarg_1_1_2_2_1_sr_2_0);
                          });
                          Value *res = UndefValue::get(ArrayType::get(revarg_1_1_2_2_1_sr_0->getType(), 2));
                          {
                            {
                              std::vector<unsigned> idxs;
                              idxs.push_back(0);
                              res = Builder2.CreateInsertValue(res, revarg_1_1_2_2_1_sr_0, idxs);
                            }
                            {
                              std::vector<unsigned> idxs;
                              idxs.push_back(1);
                              res = Builder2.CreateInsertValue(res, revarg_1_1_2_2_1_sr_1, idxs);
                            }
                          }
                           res;
                        });
                      });
                      Value* local_re = Builder2.CreateExtractValue(revarg_1_1_2_2_0, ArrayRef<unsigned>({0}), "re");
                      Value* local_im = Builder2.CreateExtractValue(revarg_1_1_2_2_0, ArrayRef<unsigned>({1}), "im");
                      llvm::Value *local_exp = nullptr;
                      ({
                        auto revarg_1_1_2_2_sr_0 = ({
                          // Computing FMul
                          auto revarg_1_1_2_2_sr_1_0 = ({
                            // Computing exp
                            auto revarg_1_1_2_2_sr_1_1_0 = local_re;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::exp, std::vector<Type*>({revarg_1_1_2_2_sr_1_1_0->getType()})), ArrayRef<Value*>({revarg_1_1_2_2_sr_1_1_0}));
                          });
                          local_exp = revarg_1_1_2_2_sr_1_0;
                          auto revarg_1_1_2_2_sr_1_1 = ({
                            // Computing cos
                            auto revarg_1_1_2_2_sr_1_2_0 = local_im;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::cos, std::vector<Type*>({revarg_1_1_2_2_sr_1_2_0->getType()})), ArrayRef<Value*>({revarg_1_1_2_2_sr_1_2_0}));
                          });
                          Builder2.CreateFMul(revarg_1_1_2_2_sr_1_0, revarg_1_1_2_2_sr_1_1);
                        });
                        auto revarg_1_1_2_2_sr_1 = ({
                          // Computing FMul
                          auto revarg_1_1_2_2_sr_2_0 = local_exp;
                          auto revarg_1_1_2_2_sr_2_1 = ({
                            // Computing sin
                            auto revarg_1_1_2_2_sr_2_2_0 = local_im;
                            Builder2.CreateCall(getIntrinsicDeclaration(mod, Intrinsic::sin, std::vector<Type*>({revarg_1_1_2_2_sr_2_2_0->getType()})), ArrayRef<Value*>({revarg_1_1_2_2_sr_2_2_0}));
                          });
                          Builder2.CreateFMul(revarg_1_1_2_2_sr_2_0, revarg_1_1_2_2_sr_2_1);
                        });
                        Value *res = UndefValue::get(ArrayType::get(revarg_1_1_2_2_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, revarg_1_1_2_2_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, revarg_1_1_2_2_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                    Value* local_re1 = Builder2.CreateExtractValue(revarg_1_1_2_0, ArrayRef<unsigned>({0}), "re1");
                    Value* local_im1 = Builder2.CreateExtractValue(revarg_1_1_2_0, ArrayRef<unsigned>({1}), "im1");
                    Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1_2_1, ArrayRef<unsigned>({0}), "re2");
                    Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1_2_1, ArrayRef<unsigned>({1}), "im2");
                    ({
                      // Computing subroutine CFMul_splat
                      auto revarg_1_1_2_sr_0 = local_re1;
                      auto revarg_1_1_2_sr_1 = local_im1;
                      auto revarg_1_1_2_sr_2 = local_re2;
                      auto revarg_1_1_2_sr_3 = local_im2;
                      ({
                        auto revarg_1_1_2_sr_sr_0 = ({
                          // Computing FSub
                          auto revarg_1_1_2_sr_sr_1_0 = ({
                            // Computing FMul
                            llvm::Value* revarg_1_1_2_sr_sr_1_1_0 = revarg_1_1_2_sr_0;
                            llvm::Value* revarg_1_1_2_sr_sr_1_1_1 = revarg_1_1_2_sr_2;
                            Builder2.CreateFMul(revarg_1_1_2_sr_sr_1_1_0, revarg_1_1_2_sr_sr_1_1_1);
                          });
                          auto revarg_1_1_2_sr_sr_1_1 = ({
                            // Computing FMul
                            llvm::Value* revarg_1_1_2_sr_sr_1_2_0 = revarg_1_1_2_sr_1;
                            llvm::Value* revarg_1_1_2_sr_sr_1_2_1 = revarg_1_1_2_sr_3;
                            Builder2.CreateFMul(revarg_1_1_2_sr_sr_1_2_0, revarg_1_1_2_sr_sr_1_2_1);
                          });
                          Builder2.CreateFSub(revarg_1_1_2_sr_sr_1_0, revarg_1_1_2_sr_sr_1_1);
                        });
                        auto revarg_1_1_2_sr_sr_1 = ({
                          // Computing FAdd
                          auto revarg_1_1_2_sr_sr_2_0 = ({
                            // Computing FMul
                            llvm::Value* revarg_1_1_2_sr_sr_2_1_0 = revarg_1_1_2_sr_0;
                            llvm::Value* revarg_1_1_2_sr_sr_2_1_1 = revarg_1_1_2_sr_3;
                            Builder2.CreateFMul(revarg_1_1_2_sr_sr_2_1_0, revarg_1_1_2_sr_sr_2_1_1);
                          });
                          auto revarg_1_1_2_sr_sr_2_1 = ({
                            // Computing FMul
                            llvm::Value* revarg_1_1_2_sr_sr_2_2_0 = revarg_1_1_2_sr_2;
                            llvm::Value* revarg_1_1_2_sr_sr_2_2_1 = revarg_1_1_2_sr_1;
                            Builder2.CreateFMul(revarg_1_1_2_sr_sr_2_2_0, revarg_1_1_2_sr_sr_2_2_1);
                          });
                          Builder2.CreateFAdd(revarg_1_1_2_sr_sr_2_0, revarg_1_1_2_sr_sr_2_1);
                        });
                        Value *res = UndefValue::get(ArrayType::get(revarg_1_1_2_sr_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, revarg_1_1_2_sr_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, revarg_1_1_2_sr_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                  });
                  Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_0->getType(), 0, 0)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_0->getType(), 0, 1)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto revarg_1_1_sr_0 = local_re1;
                    auto revarg_1_1_sr_1 = local_im1;
                    auto revarg_1_1_sr_2 = local_re2;
                    auto revarg_1_1_sr_3 = local_im2;
                    ({
                      auto revarg_1_1_sr_sr_0 = ({
                        // Computing FSub
                        auto revarg_1_1_sr_sr_1_0 = ({
                          // Computing FMul
                          auto revarg_1_1_sr_sr_1_1_0 = revarg_1_1_sr_0;
                          llvm::Value* revarg_1_1_sr_sr_1_1_1 = revarg_1_1_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_1_0, idx), revarg_1_1_sr_sr_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_1_sr_sr_1_1 = ({
                          // Computing FMul
                          auto revarg_1_1_sr_sr_1_2_0 = revarg_1_1_sr_1;
                          llvm::Value* revarg_1_1_sr_sr_1_2_1 = revarg_1_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_2_0, idx), revarg_1_1_sr_sr_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_1_sr_sr_1 = ({
                        // Computing FAdd
                        auto revarg_1_1_sr_sr_2_0 = ({
                          // Computing FMul
                          auto revarg_1_1_sr_sr_2_1_0 = revarg_1_1_sr_0;
                          llvm::Value* revarg_1_1_sr_sr_2_1_1 = revarg_1_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_1_0, idx), revarg_1_1_sr_sr_2_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_1_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_1_sr_sr_2_2_0 = revarg_1_1_sr_2;
                          auto revarg_1_1_sr_sr_2_2_1 = revarg_1_1_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(revarg_1_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_2_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_0->getType() : getSubType(revarg_1_1_sr_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                ({
                  auto revarg_1_sr_0 = local_re;
                  auto revarg_1_sr_1 = ({
                    // Computing FNeg
                    auto revarg_1_sr_2_0 = local_im;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_1_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_2_0, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_sr_0->getType() : getSubType(revarg_1_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_0 : gutils->extractMeta(Builder2, revarg_1_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_1 : gutils->extractMeta(Builder2, revarg_1_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
              Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(revarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(revarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              ({
                auto revarg_sr_0 = local_re;
                auto revarg_sr_1 = local_im;
                Value *res = UndefValue::get(gutils->getShadowType(call.getType()));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(0);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_sr_0 : gutils->extractMeta(Builder2, revarg_sr_0, idx), idxs);
                  }
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(1);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_sr_1 : gutils->extractMeta(Builder2, revarg_sr_1, idx), idxs);
                  }
                }
                 res;
              });
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "__mulsc3" ||
      funcName == "__muldc3" ||
      funcName == "__multc3" ||
      funcName == "__mulxc3") && call.arg_size() == 4 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
            Value *res = ({
              // Computing subroutine ToStruct2
              auto fwdnsrarg_0 = ({
                // Computing subroutine CFAdd
                auto fwdnsrarg_1_0 = ({
                  // Computing subroutine CFMul_splat
                  auto fwdnsrarg_1_1_0 = ({
                    // Computing SelectIfActive
                    llvm::Value *imVal = call.getOperand(0);
                    bool condition = !gutils->isConstantValue(imVal);
                    bool vectorized = false;
                    (void)vectorized;
                    if (condition) {
                      imVal = gutils->invertPointerM(call.getOperand(0), Builder2, TR.query(&call));
                      vectorized = true;
                    } else {
                      imVal = Constant::getNullValue(call.getOperand(0)->getType());
                    }
                    if (!vectorized) {
                      llvm::Value* vec_imVal = gutils->getWidth() == 1 ? imVal : UndefValue::get(gutils->getShadowType(imVal->getType()));
                      if (gutils->getWidth() != 1)
                        for (size_t i=0; i<gutils->getWidth(); i++)
                          vec_imVal = Builder2.CreateInsertValue(vec_imVal, imVal, std::vector<unsigned>({(unsigned)i}));
                      imVal = vec_imVal;
                    }
                    imVal;
                    });
                  auto fwdnsrarg_1_1_1 = ({
                    // Computing SelectIfActive
                    llvm::Value *imVal = call.getOperand(1);
                    bool condition = !gutils->isConstantValue(imVal);
                    bool vectorized = false;
                    (void)vectorized;
                    if (condition) {
                      imVal = gutils->invertPointerM(call.getOperand(1), Builder2, TR.query(&call));
                      vectorized = true;
                    } else {
                      imVal = Constant::getNullValue(call.getOperand(1)->getType());
                    }
                    if (!vectorized) {
                      llvm::Value* vec_imVal = gutils->getWidth() == 1 ? imVal : UndefValue::get(gutils->getShadowType(imVal->getType()));
                      if (gutils->getWidth() != 1)
                        for (size_t i=0; i<gutils->getWidth(); i++)
                          vec_imVal = Builder2.CreateInsertValue(vec_imVal, imVal, std::vector<unsigned>({(unsigned)i}));
                      imVal = vec_imVal;
                    }
                    imVal;
                    });
                  auto fwdnsrarg_1_1_2 = gutils->getNewFromOriginal(call.getOperand(2));
                  auto fwdnsrarg_1_1_3 = gutils->getNewFromOriginal(call.getOperand(3));
                  ({
                    auto fwdnsrarg_1_1_sr_0 = ({
                      // Computing FSub
                      auto fwdnsrarg_1_1_sr_1_0 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_1_sr_1_1_0 = fwdnsrarg_1_1_0;
                        llvm::Value* fwdnsrarg_1_1_sr_1_1_1 = fwdnsrarg_1_1_2;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_1_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_1_1_0, idx), fwdnsrarg_1_1_sr_1_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_1_1_sr_1_1 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_1_sr_1_2_0 = fwdnsrarg_1_1_1;
                        llvm::Value* fwdnsrarg_1_1_sr_1_2_1 = fwdnsrarg_1_1_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_1_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_1_2_0, idx), fwdnsrarg_1_1_sr_1_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_1_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdnsrarg_1_1_sr_1 = ({
                      // Computing FAdd
                      auto fwdnsrarg_1_1_sr_2_0 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_1_sr_2_1_0 = fwdnsrarg_1_1_0;
                        llvm::Value* fwdnsrarg_1_1_sr_2_1_1 = fwdnsrarg_1_1_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_2_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_2_1_0, idx), fwdnsrarg_1_1_sr_2_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_1_1_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_1_1_sr_2_2_0 = fwdnsrarg_1_1_2;
                        auto fwdnsrarg_1_1_sr_2_2_1 = fwdnsrarg_1_1_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul(fwdnsrarg_1_1_sr_2_2_0, (gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_2_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_2_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_0->getType() : getSubType(fwdnsrarg_1_1_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
                auto fwdnsrarg_1_1 = ({
                  // Computing subroutine CFMul_splat
                  auto fwdnsrarg_1_2_0 = ({
                    // Computing SelectIfActive
                    llvm::Value *imVal = call.getOperand(2);
                    bool condition = !gutils->isConstantValue(imVal);
                    bool vectorized = false;
                    (void)vectorized;
                    if (condition) {
                      imVal = gutils->invertPointerM(call.getOperand(2), Builder2, TR.query(&call));
                      vectorized = true;
                    } else {
                      imVal = Constant::getNullValue(call.getOperand(2)->getType());
                    }
                    if (!vectorized) {
                      llvm::Value* vec_imVal = gutils->getWidth() == 1 ? imVal : UndefValue::get(gutils->getShadowType(imVal->getType()));
                      if (gutils->getWidth() != 1)
                        for (size_t i=0; i<gutils->getWidth(); i++)
                          vec_imVal = Builder2.CreateInsertValue(vec_imVal, imVal, std::vector<unsigned>({(unsigned)i}));
                      imVal = vec_imVal;
                    }
                    imVal;
                    });
                  auto fwdnsrarg_1_2_1 = ({
                    // Computing SelectIfActive
                    llvm::Value *imVal = call.getOperand(3);
                    bool condition = !gutils->isConstantValue(imVal);
                    bool vectorized = false;
                    (void)vectorized;
                    if (condition) {
                      imVal = gutils->invertPointerM(call.getOperand(3), Builder2, TR.query(&call));
                      vectorized = true;
                    } else {
                      imVal = Constant::getNullValue(call.getOperand(3)->getType());
                    }
                    if (!vectorized) {
                      llvm::Value* vec_imVal = gutils->getWidth() == 1 ? imVal : UndefValue::get(gutils->getShadowType(imVal->getType()));
                      if (gutils->getWidth() != 1)
                        for (size_t i=0; i<gutils->getWidth(); i++)
                          vec_imVal = Builder2.CreateInsertValue(vec_imVal, imVal, std::vector<unsigned>({(unsigned)i}));
                      imVal = vec_imVal;
                    }
                    imVal;
                    });
                  auto fwdnsrarg_1_2_2 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto fwdnsrarg_1_2_3 = gutils->getNewFromOriginal(call.getOperand(1));
                  ({
                    auto fwdnsrarg_1_2_sr_0 = ({
                      // Computing FSub
                      auto fwdnsrarg_1_2_sr_1_0 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_2_sr_1_1_0 = fwdnsrarg_1_2_0;
                        llvm::Value* fwdnsrarg_1_2_sr_1_1_1 = fwdnsrarg_1_2_2;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_1_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_1_1_0, idx), fwdnsrarg_1_2_sr_1_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_1_2_sr_1_1 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_2_sr_1_2_0 = fwdnsrarg_1_2_1;
                        llvm::Value* fwdnsrarg_1_2_sr_1_2_1 = fwdnsrarg_1_2_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_1_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_1_2_0, idx), fwdnsrarg_1_2_sr_1_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_1_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdnsrarg_1_2_sr_1 = ({
                      // Computing FAdd
                      auto fwdnsrarg_1_2_sr_2_0 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_2_sr_2_1_0 = fwdnsrarg_1_2_0;
                        llvm::Value* fwdnsrarg_1_2_sr_2_1_1 = fwdnsrarg_1_2_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_2_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_2_1_0, idx), fwdnsrarg_1_2_sr_2_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_1_2_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_1_2_sr_2_2_0 = fwdnsrarg_1_2_2;
                        auto fwdnsrarg_1_2_sr_2_2_1 = fwdnsrarg_1_2_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul(fwdnsrarg_1_2_sr_2_2_0, (gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_2_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_2_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_0->getType() : getSubType(fwdnsrarg_1_2_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_1_1, ArrayRef<unsigned>({0}), "re2") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_1_1->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re2 = Builder2.CreateInsertValue(local_re2, gutils->extractMeta(Builder2, fwdnsrarg_1_1, ArrayRef<unsigned>({(unsigned)i, 0}), "re2."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im2 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_1_1, ArrayRef<unsigned>({1}), "im2") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_1_1->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im2 = Builder2.CreateInsertValue(local_im2, gutils->extractMeta(Builder2, fwdnsrarg_1_1, ArrayRef<unsigned>({(unsigned)i, 1}), "im2."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                ({
                  auto fwdnsrarg_1_sr_0 = ({
                    // Computing FAdd
                    auto fwdnsrarg_1_sr_1_0 = local_re1;
                    auto fwdnsrarg_1_sr_1_1 = local_re2;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_1_1, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  auto fwdnsrarg_1_sr_1 = ({
                    // Computing FAdd
                    auto fwdnsrarg_1_sr_2_0 = local_im1;
                    auto fwdnsrarg_1_sr_2_1 = local_im2;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_2_1, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_0->getType() : getSubType(fwdnsrarg_1_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
              Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              ({
                auto fwdnsrarg_sr_0 = local_re;
                auto fwdnsrarg_sr_1 = local_im;
                Value *res = UndefValue::get(gutils->getShadowType(call.getType()));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(0);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_0, idx), idxs);
                  }
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(1);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_1, idx), idxs);
                  }
                }
                 res;
              });
            });
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_r0 = nullptr;
          {
            Value *itmp = ({
              // Computing ExtractValue
              auto fwdarg_0 = ({
                // Computing subroutine CFMul
                auto fwdarg_1_0 = dif;
                auto fwdarg_1_1 = ({
                  llvm::Value* fwdarg_1_2_0 = gutils->getNewFromOriginal(call.getOperand(2));
                  llvm::Value* fwdarg_1_2_1 = gutils->getNewFromOriginal(call.getOperand(3));
                  Value *res = UndefValue::get(ArrayType::get(fwdarg_1_2_0->getType(), 2));
                  {
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, fwdarg_1_2_0, idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, fwdarg_1_2_1, idxs);
                    }
                  }
                   res;
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFMul_splat
                  auto fwdarg_1_sr_0 = local_re1;
                  auto fwdarg_1_sr_1 = local_im1;
                  auto fwdarg_1_sr_2 = local_re2;
                  auto fwdarg_1_sr_3 = local_im2;
                  ({
                    auto fwdarg_1_sr_sr_0 = ({
                      // Computing FSub
                      auto fwdarg_1_sr_sr_1_0 = ({
                        // Computing FMul
                        auto fwdarg_1_sr_sr_1_1_0 = fwdarg_1_sr_0;
                        llvm::Value* fwdarg_1_sr_sr_1_1_1 = fwdarg_1_sr_2;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_0, idx), fwdarg_1_sr_sr_1_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_sr_sr_1_1 = ({
                        // Computing FMul
                        auto fwdarg_1_sr_sr_1_2_0 = fwdarg_1_sr_1;
                        llvm::Value* fwdarg_1_sr_sr_1_2_1 = fwdarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_2_0, idx), fwdarg_1_sr_sr_1_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdarg_1_sr_sr_1 = ({
                      // Computing FAdd
                      auto fwdarg_1_sr_sr_2_0 = ({
                        // Computing FMul
                        auto fwdarg_1_sr_sr_2_1_0 = fwdarg_1_sr_0;
                        llvm::Value* fwdarg_1_sr_sr_2_1_1 = fwdarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_0, idx), fwdarg_1_sr_sr_2_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_sr_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* fwdarg_1_sr_sr_2_2_0 = fwdarg_1_sr_2;
                        auto fwdarg_1_sr_sr_2_2_1 = fwdarg_1_sr_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul(fwdarg_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_0->getType() : getSubType(fwdarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              local_r0 = fwdarg_0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), ArrayRef<unsigned>({(unsigned)(0)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {0}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          {
            Value *itmp = ({
              // Computing ExtractValue
              auto fwdarg_0 = local_r0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), ArrayRef<unsigned>({(unsigned)(1)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {1}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(1))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_r1 = nullptr;
          {
            Value *itmp = ({
              // Computing ExtractValue
              auto fwdarg_0 = ({
                // Computing subroutine CFMul
                auto fwdarg_1_0 = dif;
                auto fwdarg_1_1 = ({
                  llvm::Value* fwdarg_1_2_0 = gutils->getNewFromOriginal(call.getOperand(3));
                  auto fwdarg_1_2_1 = ({
                    // Computing FNeg
                    llvm::Value* fwdarg_1_2_2_0 = gutils->getNewFromOriginal(call.getOperand(2));
                    Builder2.CreateFNeg(fwdarg_1_2_2_0);
                  });
                  Value *res = UndefValue::get(ArrayType::get(fwdarg_1_2_0->getType(), 2));
                  {
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, fwdarg_1_2_0, idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, fwdarg_1_2_1, idxs);
                    }
                  }
                   res;
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFMul_splat
                  auto fwdarg_1_sr_0 = local_re1;
                  auto fwdarg_1_sr_1 = local_im1;
                  auto fwdarg_1_sr_2 = local_re2;
                  auto fwdarg_1_sr_3 = local_im2;
                  ({
                    auto fwdarg_1_sr_sr_0 = ({
                      // Computing FSub
                      auto fwdarg_1_sr_sr_1_0 = ({
                        // Computing FMul
                        auto fwdarg_1_sr_sr_1_1_0 = fwdarg_1_sr_0;
                        llvm::Value* fwdarg_1_sr_sr_1_1_1 = fwdarg_1_sr_2;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_0, idx), fwdarg_1_sr_sr_1_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_sr_sr_1_1 = ({
                        // Computing FMul
                        auto fwdarg_1_sr_sr_1_2_0 = fwdarg_1_sr_1;
                        llvm::Value* fwdarg_1_sr_sr_1_2_1 = fwdarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_2_0, idx), fwdarg_1_sr_sr_1_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdarg_1_sr_sr_1 = ({
                      // Computing FAdd
                      auto fwdarg_1_sr_sr_2_0 = ({
                        // Computing FMul
                        auto fwdarg_1_sr_sr_2_1_0 = fwdarg_1_sr_0;
                        llvm::Value* fwdarg_1_sr_sr_2_1_1 = fwdarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_0, idx), fwdarg_1_sr_sr_2_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_sr_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* fwdarg_1_sr_sr_2_2_0 = fwdarg_1_sr_2;
                        auto fwdarg_1_sr_sr_2_2_1 = fwdarg_1_sr_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul(fwdarg_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_0->getType() : getSubType(fwdarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              local_r1 = fwdarg_0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), ArrayRef<unsigned>({(unsigned)(0)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {0}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          {
            Value *itmp = ({
              // Computing ExtractValue
              auto fwdarg_0 = local_r1;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), ArrayRef<unsigned>({(unsigned)(1)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {1}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(2))) {
          Value *dif = diffe(call.getOperand(2), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(2))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_r2 = nullptr;
          {
            Value *itmp = ({
              // Computing ExtractValue
              auto fwdarg_0 = ({
                // Computing subroutine CFMul
                auto fwdarg_1_0 = dif;
                auto fwdarg_1_1 = ({
                  llvm::Value* fwdarg_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_1_2_1 = gutils->getNewFromOriginal(call.getOperand(1));
                  Value *res = UndefValue::get(ArrayType::get(fwdarg_1_2_0->getType(), 2));
                  {
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, fwdarg_1_2_0, idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, fwdarg_1_2_1, idxs);
                    }
                  }
                   res;
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFMul_splat
                  auto fwdarg_1_sr_0 = local_re1;
                  auto fwdarg_1_sr_1 = local_im1;
                  auto fwdarg_1_sr_2 = local_re2;
                  auto fwdarg_1_sr_3 = local_im2;
                  ({
                    auto fwdarg_1_sr_sr_0 = ({
                      // Computing FSub
                      auto fwdarg_1_sr_sr_1_0 = ({
                        // Computing FMul
                        auto fwdarg_1_sr_sr_1_1_0 = fwdarg_1_sr_0;
                        llvm::Value* fwdarg_1_sr_sr_1_1_1 = fwdarg_1_sr_2;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_0, idx), fwdarg_1_sr_sr_1_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_sr_sr_1_1 = ({
                        // Computing FMul
                        auto fwdarg_1_sr_sr_1_2_0 = fwdarg_1_sr_1;
                        llvm::Value* fwdarg_1_sr_sr_1_2_1 = fwdarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_2_0, idx), fwdarg_1_sr_sr_1_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdarg_1_sr_sr_1 = ({
                      // Computing FAdd
                      auto fwdarg_1_sr_sr_2_0 = ({
                        // Computing FMul
                        auto fwdarg_1_sr_sr_2_1_0 = fwdarg_1_sr_0;
                        llvm::Value* fwdarg_1_sr_sr_2_1_1 = fwdarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_0, idx), fwdarg_1_sr_sr_2_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_sr_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* fwdarg_1_sr_sr_2_2_0 = fwdarg_1_sr_2;
                        auto fwdarg_1_sr_sr_2_2_1 = fwdarg_1_sr_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul(fwdarg_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_0->getType() : getSubType(fwdarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              local_r2 = fwdarg_0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), ArrayRef<unsigned>({(unsigned)(0)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {0}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          {
            Value *itmp = ({
              // Computing ExtractValue
              auto fwdarg_0 = local_r2;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), ArrayRef<unsigned>({(unsigned)(1)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {1}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(3))) {
          Value *dif = diffe(call.getOperand(3), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(3))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_r3 = nullptr;
          {
            Value *itmp = ({
              // Computing ExtractValue
              auto fwdarg_0 = ({
                // Computing subroutine CFMul
                auto fwdarg_1_0 = dif;
                auto fwdarg_1_1 = ({
                  llvm::Value* fwdarg_1_2_0 = gutils->getNewFromOriginal(call.getOperand(1));
                  auto fwdarg_1_2_1 = ({
                    // Computing FNeg
                    llvm::Value* fwdarg_1_2_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    Builder2.CreateFNeg(fwdarg_1_2_2_0);
                  });
                  Value *res = UndefValue::get(ArrayType::get(fwdarg_1_2_0->getType(), 2));
                  {
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, fwdarg_1_2_0, idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, fwdarg_1_2_1, idxs);
                    }
                  }
                   res;
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFMul_splat
                  auto fwdarg_1_sr_0 = local_re1;
                  auto fwdarg_1_sr_1 = local_im1;
                  auto fwdarg_1_sr_2 = local_re2;
                  auto fwdarg_1_sr_3 = local_im2;
                  ({
                    auto fwdarg_1_sr_sr_0 = ({
                      // Computing FSub
                      auto fwdarg_1_sr_sr_1_0 = ({
                        // Computing FMul
                        auto fwdarg_1_sr_sr_1_1_0 = fwdarg_1_sr_0;
                        llvm::Value* fwdarg_1_sr_sr_1_1_1 = fwdarg_1_sr_2;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_0, idx), fwdarg_1_sr_sr_1_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_sr_sr_1_1 = ({
                        // Computing FMul
                        auto fwdarg_1_sr_sr_1_2_0 = fwdarg_1_sr_1;
                        llvm::Value* fwdarg_1_sr_sr_1_2_1 = fwdarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_2_0, idx), fwdarg_1_sr_sr_1_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdarg_1_sr_sr_1 = ({
                      // Computing FAdd
                      auto fwdarg_1_sr_sr_2_0 = ({
                        // Computing FMul
                        auto fwdarg_1_sr_sr_2_1_0 = fwdarg_1_sr_0;
                        llvm::Value* fwdarg_1_sr_sr_2_1_1 = fwdarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_0, idx), fwdarg_1_sr_sr_2_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_sr_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* fwdarg_1_sr_sr_2_2_0 = fwdarg_1_sr_2;
                        auto fwdarg_1_sr_sr_2_2_1 = fwdarg_1_sr_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul(fwdarg_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_0->getType() : getSubType(fwdarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              local_r3 = fwdarg_0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), ArrayRef<unsigned>({(unsigned)(0)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {0}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          {
            Value *itmp = ({
              // Computing ExtractValue
              auto fwdarg_0 = local_r3;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), ArrayRef<unsigned>({(unsigned)(1)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {1}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        } else if (!dif && !gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        } else if (!dif && !gutils->isConstantValue(call.getOperand(2)) && !isa<PointerType>(call.getOperand(2)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        } else if (!dif && !gutils->isConstantValue(call.getOperand(3)) && !isa<PointerType>(call.getOperand(3)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          llvm::Value *local_r0 = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing ExtractValue
              auto revarg_0 = ({
                // Computing subroutine CFMul
                auto revarg_1_0 = dif;
                auto revarg_1_1 = ({
                  llvm::Value* revarg_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(2)), Builder2);
                  llvm::Value* revarg_1_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(3)), Builder2);
                  Value *res = UndefValue::get(ArrayType::get(revarg_1_2_0->getType(), 2));
                  {
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, revarg_1_2_0, idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, revarg_1_2_1, idxs);
                    }
                  }
                   res;
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFMul_splat
                  auto revarg_1_sr_0 = local_re1;
                  auto revarg_1_sr_1 = local_im1;
                  auto revarg_1_sr_2 = local_re2;
                  auto revarg_1_sr_3 = local_im2;
                  ({
                    auto revarg_1_sr_sr_0 = ({
                      // Computing FSub
                      auto revarg_1_sr_sr_1_0 = ({
                        // Computing FMul
                        auto revarg_1_sr_sr_1_1_0 = revarg_1_sr_0;
                        llvm::Value* revarg_1_sr_sr_1_1_1 = revarg_1_sr_2;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_0, idx), revarg_1_sr_sr_1_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_sr_sr_1_1 = ({
                        // Computing FMul
                        auto revarg_1_sr_sr_1_2_0 = revarg_1_sr_1;
                        llvm::Value* revarg_1_sr_sr_1_2_1 = revarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_2_0, idx), revarg_1_sr_sr_1_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto revarg_1_sr_sr_1 = ({
                      // Computing FAdd
                      auto revarg_1_sr_sr_2_0 = ({
                        // Computing FMul
                        auto revarg_1_sr_sr_2_1_0 = revarg_1_sr_0;
                        llvm::Value* revarg_1_sr_sr_2_1_1 = revarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_0, idx), revarg_1_sr_sr_2_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_sr_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* revarg_1_sr_sr_2_2_0 = revarg_1_sr_2;
                        auto revarg_1_sr_sr_2_2_1 = revarg_1_sr_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul(revarg_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? revarg_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_sr_sr_0->getType() : getSubType(revarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_sr_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_sr_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              local_r0 = revarg_0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), ArrayRef<unsigned>({(unsigned)(0)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          {
            Value *tmp = ({
              // Computing ExtractValue
              auto revarg_0 = local_r0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), ArrayRef<unsigned>({(unsigned)(1)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        if (!gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          llvm::Value *local_r1 = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing ExtractValue
              auto revarg_0 = ({
                // Computing subroutine CFMul
                auto revarg_1_0 = dif;
                auto revarg_1_1 = ({
                  llvm::Value* revarg_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(3)), Builder2);
                  auto revarg_1_2_1 = ({
                    // Computing FNeg
                    llvm::Value* revarg_1_2_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(2)), Builder2);
                    Builder2.CreateFNeg(revarg_1_2_2_0);
                  });
                  Value *res = UndefValue::get(ArrayType::get(revarg_1_2_0->getType(), 2));
                  {
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, revarg_1_2_0, idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, revarg_1_2_1, idxs);
                    }
                  }
                   res;
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFMul_splat
                  auto revarg_1_sr_0 = local_re1;
                  auto revarg_1_sr_1 = local_im1;
                  auto revarg_1_sr_2 = local_re2;
                  auto revarg_1_sr_3 = local_im2;
                  ({
                    auto revarg_1_sr_sr_0 = ({
                      // Computing FSub
                      auto revarg_1_sr_sr_1_0 = ({
                        // Computing FMul
                        auto revarg_1_sr_sr_1_1_0 = revarg_1_sr_0;
                        llvm::Value* revarg_1_sr_sr_1_1_1 = revarg_1_sr_2;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_0, idx), revarg_1_sr_sr_1_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_sr_sr_1_1 = ({
                        // Computing FMul
                        auto revarg_1_sr_sr_1_2_0 = revarg_1_sr_1;
                        llvm::Value* revarg_1_sr_sr_1_2_1 = revarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_2_0, idx), revarg_1_sr_sr_1_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto revarg_1_sr_sr_1 = ({
                      // Computing FAdd
                      auto revarg_1_sr_sr_2_0 = ({
                        // Computing FMul
                        auto revarg_1_sr_sr_2_1_0 = revarg_1_sr_0;
                        llvm::Value* revarg_1_sr_sr_2_1_1 = revarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_0, idx), revarg_1_sr_sr_2_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_sr_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* revarg_1_sr_sr_2_2_0 = revarg_1_sr_2;
                        auto revarg_1_sr_sr_2_2_1 = revarg_1_sr_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul(revarg_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? revarg_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_sr_sr_0->getType() : getSubType(revarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_sr_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_sr_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              local_r1 = revarg_0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), ArrayRef<unsigned>({(unsigned)(0)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(1)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          {
            Value *tmp = ({
              // Computing ExtractValue
              auto revarg_0 = local_r1;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), ArrayRef<unsigned>({(unsigned)(1)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(1)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(1), toadd, Builder2, call.getOperand(1)->getType());
        }
        if (!gutils->isConstantValue(call.getOperand(2)) && !isa<PointerType>(call.getOperand(2)->getType()) ) {
          llvm::Value *local_r2 = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing ExtractValue
              auto revarg_0 = ({
                // Computing subroutine CFMul
                auto revarg_1_0 = dif;
                auto revarg_1_1 = ({
                  llvm::Value* revarg_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  llvm::Value* revarg_1_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                  Value *res = UndefValue::get(ArrayType::get(revarg_1_2_0->getType(), 2));
                  {
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, revarg_1_2_0, idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, revarg_1_2_1, idxs);
                    }
                  }
                   res;
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFMul_splat
                  auto revarg_1_sr_0 = local_re1;
                  auto revarg_1_sr_1 = local_im1;
                  auto revarg_1_sr_2 = local_re2;
                  auto revarg_1_sr_3 = local_im2;
                  ({
                    auto revarg_1_sr_sr_0 = ({
                      // Computing FSub
                      auto revarg_1_sr_sr_1_0 = ({
                        // Computing FMul
                        auto revarg_1_sr_sr_1_1_0 = revarg_1_sr_0;
                        llvm::Value* revarg_1_sr_sr_1_1_1 = revarg_1_sr_2;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_0, idx), revarg_1_sr_sr_1_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_sr_sr_1_1 = ({
                        // Computing FMul
                        auto revarg_1_sr_sr_1_2_0 = revarg_1_sr_1;
                        llvm::Value* revarg_1_sr_sr_1_2_1 = revarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_2_0, idx), revarg_1_sr_sr_1_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto revarg_1_sr_sr_1 = ({
                      // Computing FAdd
                      auto revarg_1_sr_sr_2_0 = ({
                        // Computing FMul
                        auto revarg_1_sr_sr_2_1_0 = revarg_1_sr_0;
                        llvm::Value* revarg_1_sr_sr_2_1_1 = revarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_0, idx), revarg_1_sr_sr_2_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_sr_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* revarg_1_sr_sr_2_2_0 = revarg_1_sr_2;
                        auto revarg_1_sr_sr_2_2_1 = revarg_1_sr_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul(revarg_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? revarg_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_sr_sr_0->getType() : getSubType(revarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_sr_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_sr_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              local_r2 = revarg_0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), ArrayRef<unsigned>({(unsigned)(0)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(2)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          {
            Value *tmp = ({
              // Computing ExtractValue
              auto revarg_0 = local_r2;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), ArrayRef<unsigned>({(unsigned)(1)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(2)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(2), toadd, Builder2, call.getOperand(2)->getType());
        }
        if (!gutils->isConstantValue(call.getOperand(3)) && !isa<PointerType>(call.getOperand(3)->getType()) ) {
          llvm::Value *local_r3 = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing ExtractValue
              auto revarg_0 = ({
                // Computing subroutine CFMul
                auto revarg_1_0 = dif;
                auto revarg_1_1 = ({
                  llvm::Value* revarg_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                  auto revarg_1_2_1 = ({
                    // Computing FNeg
                    llvm::Value* revarg_1_2_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                    Builder2.CreateFNeg(revarg_1_2_2_0);
                  });
                  Value *res = UndefValue::get(ArrayType::get(revarg_1_2_0->getType(), 2));
                  {
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, revarg_1_2_0, idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, revarg_1_2_1, idxs);
                    }
                  }
                   res;
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFMul_splat
                  auto revarg_1_sr_0 = local_re1;
                  auto revarg_1_sr_1 = local_im1;
                  auto revarg_1_sr_2 = local_re2;
                  auto revarg_1_sr_3 = local_im2;
                  ({
                    auto revarg_1_sr_sr_0 = ({
                      // Computing FSub
                      auto revarg_1_sr_sr_1_0 = ({
                        // Computing FMul
                        auto revarg_1_sr_sr_1_1_0 = revarg_1_sr_0;
                        llvm::Value* revarg_1_sr_sr_1_1_1 = revarg_1_sr_2;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_0, idx), revarg_1_sr_sr_1_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_sr_sr_1_1 = ({
                        // Computing FMul
                        auto revarg_1_sr_sr_1_2_0 = revarg_1_sr_1;
                        llvm::Value* revarg_1_sr_sr_1_2_1 = revarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_2_0, idx), revarg_1_sr_sr_1_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto revarg_1_sr_sr_1 = ({
                      // Computing FAdd
                      auto revarg_1_sr_sr_2_0 = ({
                        // Computing FMul
                        auto revarg_1_sr_sr_2_1_0 = revarg_1_sr_0;
                        llvm::Value* revarg_1_sr_sr_2_1_1 = revarg_1_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_0, idx), revarg_1_sr_sr_2_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_sr_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* revarg_1_sr_sr_2_2_0 = revarg_1_sr_2;
                        auto revarg_1_sr_sr_2_2_1 = revarg_1_sr_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul(revarg_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? revarg_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_sr_sr_0->getType() : getSubType(revarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_sr_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_sr_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              local_r3 = revarg_0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), ArrayRef<unsigned>({(unsigned)(0)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(3)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          {
            Value *tmp = ({
              // Computing ExtractValue
              auto revarg_0 = local_r3;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), ArrayRef<unsigned>({(unsigned)(1)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(3)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(3), toadd, Builder2, call.getOperand(3)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "__divsc3" ||
      funcName == "__divdc3" ||
      funcName == "__divtc3" ||
      funcName == "__divxc3") && call.arg_size() == 4 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
            Value *res = ({
              // Computing subroutine ToStruct2
              auto fwdnsrarg_0 = ({
                // Computing subroutine CFAdd
                auto fwdnsrarg_1_0 = ({
                  // Computing subroutine CFDiv_splat
                  auto fwdnsrarg_1_1_0 = ({
                    // Computing SelectIfActive
                    llvm::Value *imVal = call.getOperand(0);
                    bool condition = !gutils->isConstantValue(imVal);
                    bool vectorized = false;
                    (void)vectorized;
                    if (condition) {
                      imVal = gutils->invertPointerM(call.getOperand(0), Builder2, TR.query(&call));
                      vectorized = true;
                    } else {
                      imVal = Constant::getNullValue(call.getOperand(0)->getType());
                    }
                    if (!vectorized) {
                      llvm::Value* vec_imVal = gutils->getWidth() == 1 ? imVal : UndefValue::get(gutils->getShadowType(imVal->getType()));
                      if (gutils->getWidth() != 1)
                        for (size_t i=0; i<gutils->getWidth(); i++)
                          vec_imVal = Builder2.CreateInsertValue(vec_imVal, imVal, std::vector<unsigned>({(unsigned)i}));
                      imVal = vec_imVal;
                    }
                    imVal;
                    });
                  auto fwdnsrarg_1_1_1 = ({
                    // Computing SelectIfActive
                    llvm::Value *imVal = call.getOperand(1);
                    bool condition = !gutils->isConstantValue(imVal);
                    bool vectorized = false;
                    (void)vectorized;
                    if (condition) {
                      imVal = gutils->invertPointerM(call.getOperand(1), Builder2, TR.query(&call));
                      vectorized = true;
                    } else {
                      imVal = Constant::getNullValue(call.getOperand(1)->getType());
                    }
                    if (!vectorized) {
                      llvm::Value* vec_imVal = gutils->getWidth() == 1 ? imVal : UndefValue::get(gutils->getShadowType(imVal->getType()));
                      if (gutils->getWidth() != 1)
                        for (size_t i=0; i<gutils->getWidth(); i++)
                          vec_imVal = Builder2.CreateInsertValue(vec_imVal, imVal, std::vector<unsigned>({(unsigned)i}));
                      imVal = vec_imVal;
                    }
                    imVal;
                    });
                  auto fwdnsrarg_1_1_2 = gutils->getNewFromOriginal(call.getOperand(2));
                  auto fwdnsrarg_1_1_3 = gutils->getNewFromOriginal(call.getOperand(3));
                  llvm::Value *local_denom = nullptr;
                  ({
                    auto fwdnsrarg_1_1_sr_0 = ({
                      // Computing FDiv
                      auto fwdnsrarg_1_1_sr_1_0 = ({
                        // Computing FAdd
                        auto fwdnsrarg_1_1_sr_1_1_0 = ({
                          // Computing FMul
                          auto fwdnsrarg_1_1_sr_1_1_1_0 = fwdnsrarg_1_1_0;
                          llvm::Value* fwdnsrarg_1_1_sr_1_1_1_1 = fwdnsrarg_1_1_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_1_1_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_1_1_1_0, idx), fwdnsrarg_1_1_sr_1_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdnsrarg_1_1_sr_1_1_1 = ({
                          // Computing FMul
                          auto fwdnsrarg_1_1_sr_1_1_2_0 = fwdnsrarg_1_1_1;
                          llvm::Value* fwdnsrarg_1_1_sr_1_1_2_1 = fwdnsrarg_1_1_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_1_1_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_1_1_2_0, idx), fwdnsrarg_1_1_sr_1_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_1_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_1_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_1_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_1_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_1_1_sr_1_1 = ({
                        // Computing FAdd
                        auto fwdnsrarg_1_1_sr_1_2_0 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_1_1_sr_1_2_1_0 = fwdnsrarg_1_1_2;
                          llvm::Value* fwdnsrarg_1_1_sr_1_2_1_1 = fwdnsrarg_1_1_2;
                          Builder2.CreateFMul(fwdnsrarg_1_1_sr_1_2_1_0, fwdnsrarg_1_1_sr_1_2_1_1);
                        });
                        auto fwdnsrarg_1_1_sr_1_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_1_1_sr_1_2_2_0 = fwdnsrarg_1_1_3;
                          llvm::Value* fwdnsrarg_1_1_sr_1_2_2_1 = fwdnsrarg_1_1_3;
                          Builder2.CreateFMul(fwdnsrarg_1_1_sr_1_2_2_0, fwdnsrarg_1_1_sr_1_2_2_1);
                        });
                        Builder2.CreateFAdd(fwdnsrarg_1_1_sr_1_2_0, fwdnsrarg_1_1_sr_1_2_1);
                      });
                      local_denom = fwdnsrarg_1_1_sr_1_1;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_1_0, idx), fwdnsrarg_1_1_sr_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdnsrarg_1_1_sr_1 = ({
                      // Computing FDiv
                      auto fwdnsrarg_1_1_sr_2_0 = ({
                        // Computing FSub
                        auto fwdnsrarg_1_1_sr_2_1_0 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_1_1_sr_2_1_1_0 = fwdnsrarg_1_1_2;
                          auto fwdnsrarg_1_1_sr_2_1_1_1 = fwdnsrarg_1_1_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(fwdnsrarg_1_1_sr_2_1_1_0, (gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_2_1_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_2_1_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdnsrarg_1_1_sr_2_1_1 = ({
                          // Computing FMul
                          auto fwdnsrarg_1_1_sr_2_1_2_0 = fwdnsrarg_1_1_0;
                          llvm::Value* fwdnsrarg_1_1_sr_2_1_2_1 = fwdnsrarg_1_1_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_2_1_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_2_1_2_0, idx), fwdnsrarg_1_1_sr_2_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_2_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_2_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_2_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_2_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_1_1_sr_2_1 = local_denom;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_2_0, idx), fwdnsrarg_1_1_sr_2_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_0->getType() : getSubType(fwdnsrarg_1_1_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_1_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
                auto fwdnsrarg_1_1 = ({
                  // Computing subroutine CFMul_splat
                  auto fwdnsrarg_1_2_0 = ({
                    // Computing SelectIfActive
                    llvm::Value *imVal = call.getOperand(2);
                    bool condition = !gutils->isConstantValue(imVal);
                    bool vectorized = false;
                    (void)vectorized;
                    if (condition) {
                      imVal = gutils->invertPointerM(call.getOperand(2), Builder2, TR.query(&call));
                      vectorized = true;
                    } else {
                      imVal = Constant::getNullValue(call.getOperand(2)->getType());
                    }
                    if (!vectorized) {
                      llvm::Value* vec_imVal = gutils->getWidth() == 1 ? imVal : UndefValue::get(gutils->getShadowType(imVal->getType()));
                      if (gutils->getWidth() != 1)
                        for (size_t i=0; i<gutils->getWidth(); i++)
                          vec_imVal = Builder2.CreateInsertValue(vec_imVal, imVal, std::vector<unsigned>({(unsigned)i}));
                      imVal = vec_imVal;
                    }
                    imVal;
                    });
                  auto fwdnsrarg_1_2_1 = ({
                    // Computing SelectIfActive
                    llvm::Value *imVal = call.getOperand(3);
                    bool condition = !gutils->isConstantValue(imVal);
                    bool vectorized = false;
                    (void)vectorized;
                    if (condition) {
                      imVal = gutils->invertPointerM(call.getOperand(3), Builder2, TR.query(&call));
                      vectorized = true;
                    } else {
                      imVal = Constant::getNullValue(call.getOperand(3)->getType());
                    }
                    if (!vectorized) {
                      llvm::Value* vec_imVal = gutils->getWidth() == 1 ? imVal : UndefValue::get(gutils->getShadowType(imVal->getType()));
                      if (gutils->getWidth() != 1)
                        for (size_t i=0; i<gutils->getWidth(); i++)
                          vec_imVal = Builder2.CreateInsertValue(vec_imVal, imVal, std::vector<unsigned>({(unsigned)i}));
                      imVal = vec_imVal;
                    }
                    imVal;
                    });
                  auto fwdnsrarg_1_2_2 = ({
                    // Computing ExtractValue
                    auto fwdnsrarg_1_2_3_0 = ({
                      // Computing subroutine CFDiv
                      auto fwdnsrarg_1_2_3_1_0 = ({
                        auto fwdnsrarg_1_2_3_1_1_0 = ({
                          // Computing FNeg
                          llvm::Value* fwdnsrarg_1_2_3_1_1_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                          Builder2.CreateFNeg(fwdnsrarg_1_2_3_1_1_1_0);
                        });
                        auto fwdnsrarg_1_2_3_1_1_1 = ({
                          // Computing FNeg
                          llvm::Value* fwdnsrarg_1_2_3_1_1_2_0 = gutils->getNewFromOriginal(call.getOperand(1));
                          Builder2.CreateFNeg(fwdnsrarg_1_2_3_1_1_2_0);
                        });
                        Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_1_2_3_1_1_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_3_1_1_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_3_1_1_1, idxs);
                          }
                        }
                         res;
                      });
                      auto fwdnsrarg_1_2_3_1_1 = ({
                        // Computing subroutine CFMul_splat
                        auto fwdnsrarg_1_2_3_1_2_0 = gutils->getNewFromOriginal(call.getOperand(2));
                        auto fwdnsrarg_1_2_3_1_2_1 = gutils->getNewFromOriginal(call.getOperand(3));
                        auto fwdnsrarg_1_2_3_1_2_2 = gutils->getNewFromOriginal(call.getOperand(2));
                        auto fwdnsrarg_1_2_3_1_2_3 = gutils->getNewFromOriginal(call.getOperand(3));
                        ({
                          auto fwdnsrarg_1_2_3_1_2_sr_0 = ({
                            // Computing FSub
                            auto fwdnsrarg_1_2_3_1_2_sr_1_0 = ({
                              // Computing FMul
                              llvm::Value* fwdnsrarg_1_2_3_1_2_sr_1_1_0 = fwdnsrarg_1_2_3_1_2_0;
                              llvm::Value* fwdnsrarg_1_2_3_1_2_sr_1_1_1 = fwdnsrarg_1_2_3_1_2_2;
                              Builder2.CreateFMul(fwdnsrarg_1_2_3_1_2_sr_1_1_0, fwdnsrarg_1_2_3_1_2_sr_1_1_1);
                            });
                            auto fwdnsrarg_1_2_3_1_2_sr_1_1 = ({
                              // Computing FMul
                              llvm::Value* fwdnsrarg_1_2_3_1_2_sr_1_2_0 = fwdnsrarg_1_2_3_1_2_1;
                              llvm::Value* fwdnsrarg_1_2_3_1_2_sr_1_2_1 = fwdnsrarg_1_2_3_1_2_3;
                              Builder2.CreateFMul(fwdnsrarg_1_2_3_1_2_sr_1_2_0, fwdnsrarg_1_2_3_1_2_sr_1_2_1);
                            });
                            Builder2.CreateFSub(fwdnsrarg_1_2_3_1_2_sr_1_0, fwdnsrarg_1_2_3_1_2_sr_1_1);
                          });
                          auto fwdnsrarg_1_2_3_1_2_sr_1 = ({
                            // Computing FAdd
                            auto fwdnsrarg_1_2_3_1_2_sr_2_0 = ({
                              // Computing FMul
                              llvm::Value* fwdnsrarg_1_2_3_1_2_sr_2_1_0 = fwdnsrarg_1_2_3_1_2_0;
                              llvm::Value* fwdnsrarg_1_2_3_1_2_sr_2_1_1 = fwdnsrarg_1_2_3_1_2_3;
                              Builder2.CreateFMul(fwdnsrarg_1_2_3_1_2_sr_2_1_0, fwdnsrarg_1_2_3_1_2_sr_2_1_1);
                            });
                            auto fwdnsrarg_1_2_3_1_2_sr_2_1 = ({
                              // Computing FMul
                              llvm::Value* fwdnsrarg_1_2_3_1_2_sr_2_2_0 = fwdnsrarg_1_2_3_1_2_2;
                              llvm::Value* fwdnsrarg_1_2_3_1_2_sr_2_2_1 = fwdnsrarg_1_2_3_1_2_1;
                              Builder2.CreateFMul(fwdnsrarg_1_2_3_1_2_sr_2_2_0, fwdnsrarg_1_2_3_1_2_sr_2_2_1);
                            });
                            Builder2.CreateFAdd(fwdnsrarg_1_2_3_1_2_sr_2_0, fwdnsrarg_1_2_3_1_2_sr_2_1);
                          });
                          Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_1_2_3_1_2_sr_0->getType(), 2));
                          {
                            {
                              std::vector<unsigned> idxs;
                              idxs.push_back(0);
                              res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_3_1_2_sr_0, idxs);
                            }
                            {
                              std::vector<unsigned> idxs;
                              idxs.push_back(1);
                              res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_3_1_2_sr_1, idxs);
                            }
                          }
                           res;
                        });
                      });
                      Value* local_re1 = Builder2.CreateExtractValue(fwdnsrarg_1_2_3_1_0, ArrayRef<unsigned>({0}), "re1");
                      Value* local_im1 = Builder2.CreateExtractValue(fwdnsrarg_1_2_3_1_0, ArrayRef<unsigned>({1}), "im1");
                      Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_1_2_3_1_1, ArrayRef<unsigned>({0}), "re2");
                      Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_1_2_3_1_1, ArrayRef<unsigned>({1}), "im2");
                      ({
                        // Computing subroutine CFDiv_splat
                        auto fwdnsrarg_1_2_3_1_sr_0 = local_re1;
                        auto fwdnsrarg_1_2_3_1_sr_1 = local_im1;
                        auto fwdnsrarg_1_2_3_1_sr_2 = local_re2;
                        auto fwdnsrarg_1_2_3_1_sr_3 = local_im2;
                        llvm::Value *local_denom = nullptr;
                        ({
                          auto fwdnsrarg_1_2_3_1_sr_sr_0 = ({
                            // Computing FDiv
                            auto fwdnsrarg_1_2_3_1_sr_sr_1_0 = ({
                              // Computing FAdd
                              auto fwdnsrarg_1_2_3_1_sr_sr_1_1_0 = ({
                                // Computing FMul
                                llvm::Value* fwdnsrarg_1_2_3_1_sr_sr_1_1_1_0 = fwdnsrarg_1_2_3_1_sr_0;
                                llvm::Value* fwdnsrarg_1_2_3_1_sr_sr_1_1_1_1 = fwdnsrarg_1_2_3_1_sr_2;
                                Builder2.CreateFMul(fwdnsrarg_1_2_3_1_sr_sr_1_1_1_0, fwdnsrarg_1_2_3_1_sr_sr_1_1_1_1);
                              });
                              auto fwdnsrarg_1_2_3_1_sr_sr_1_1_1 = ({
                                // Computing FMul
                                llvm::Value* fwdnsrarg_1_2_3_1_sr_sr_1_1_2_0 = fwdnsrarg_1_2_3_1_sr_1;
                                llvm::Value* fwdnsrarg_1_2_3_1_sr_sr_1_1_2_1 = fwdnsrarg_1_2_3_1_sr_3;
                                Builder2.CreateFMul(fwdnsrarg_1_2_3_1_sr_sr_1_1_2_0, fwdnsrarg_1_2_3_1_sr_sr_1_1_2_1);
                              });
                              Builder2.CreateFAdd(fwdnsrarg_1_2_3_1_sr_sr_1_1_0, fwdnsrarg_1_2_3_1_sr_sr_1_1_1);
                            });
                            auto fwdnsrarg_1_2_3_1_sr_sr_1_1 = ({
                              // Computing FAdd
                              auto fwdnsrarg_1_2_3_1_sr_sr_1_2_0 = ({
                                // Computing FMul
                                llvm::Value* fwdnsrarg_1_2_3_1_sr_sr_1_2_1_0 = fwdnsrarg_1_2_3_1_sr_2;
                                llvm::Value* fwdnsrarg_1_2_3_1_sr_sr_1_2_1_1 = fwdnsrarg_1_2_3_1_sr_2;
                                Builder2.CreateFMul(fwdnsrarg_1_2_3_1_sr_sr_1_2_1_0, fwdnsrarg_1_2_3_1_sr_sr_1_2_1_1);
                              });
                              auto fwdnsrarg_1_2_3_1_sr_sr_1_2_1 = ({
                                // Computing FMul
                                llvm::Value* fwdnsrarg_1_2_3_1_sr_sr_1_2_2_0 = fwdnsrarg_1_2_3_1_sr_3;
                                llvm::Value* fwdnsrarg_1_2_3_1_sr_sr_1_2_2_1 = fwdnsrarg_1_2_3_1_sr_3;
                                Builder2.CreateFMul(fwdnsrarg_1_2_3_1_sr_sr_1_2_2_0, fwdnsrarg_1_2_3_1_sr_sr_1_2_2_1);
                              });
                              Builder2.CreateFAdd(fwdnsrarg_1_2_3_1_sr_sr_1_2_0, fwdnsrarg_1_2_3_1_sr_sr_1_2_1);
                            });
                            local_denom = fwdnsrarg_1_2_3_1_sr_sr_1_1;
                            Builder2.CreateFDiv(fwdnsrarg_1_2_3_1_sr_sr_1_0, fwdnsrarg_1_2_3_1_sr_sr_1_1);
                          });
                          auto fwdnsrarg_1_2_3_1_sr_sr_1 = ({
                            // Computing FDiv
                            auto fwdnsrarg_1_2_3_1_sr_sr_2_0 = ({
                              // Computing FSub
                              auto fwdnsrarg_1_2_3_1_sr_sr_2_1_0 = ({
                                // Computing FMul
                                llvm::Value* fwdnsrarg_1_2_3_1_sr_sr_2_1_1_0 = fwdnsrarg_1_2_3_1_sr_2;
                                llvm::Value* fwdnsrarg_1_2_3_1_sr_sr_2_1_1_1 = fwdnsrarg_1_2_3_1_sr_1;
                                Builder2.CreateFMul(fwdnsrarg_1_2_3_1_sr_sr_2_1_1_0, fwdnsrarg_1_2_3_1_sr_sr_2_1_1_1);
                              });
                              auto fwdnsrarg_1_2_3_1_sr_sr_2_1_1 = ({
                                // Computing FMul
                                llvm::Value* fwdnsrarg_1_2_3_1_sr_sr_2_1_2_0 = fwdnsrarg_1_2_3_1_sr_0;
                                llvm::Value* fwdnsrarg_1_2_3_1_sr_sr_2_1_2_1 = fwdnsrarg_1_2_3_1_sr_3;
                                Builder2.CreateFMul(fwdnsrarg_1_2_3_1_sr_sr_2_1_2_0, fwdnsrarg_1_2_3_1_sr_sr_2_1_2_1);
                              });
                              Builder2.CreateFSub(fwdnsrarg_1_2_3_1_sr_sr_2_1_0, fwdnsrarg_1_2_3_1_sr_sr_2_1_1);
                            });
                            auto fwdnsrarg_1_2_3_1_sr_sr_2_1 = local_denom;
                            Builder2.CreateFDiv(fwdnsrarg_1_2_3_1_sr_sr_2_0, fwdnsrarg_1_2_3_1_sr_sr_2_1);
                          });
                          Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_1_2_3_1_sr_sr_0->getType(), 2));
                          {
                            {
                              std::vector<unsigned> idxs;
                              idxs.push_back(0);
                              res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_3_1_sr_sr_0, idxs);
                            }
                            {
                              std::vector<unsigned> idxs;
                              idxs.push_back(1);
                              res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_3_1_sr_sr_1, idxs);
                            }
                          }
                           res;
                        });
                      });
                    });
                    Builder2.CreateExtractValue(fwdnsrarg_1_2_3_0, ArrayRef<unsigned>({(unsigned)(0)}));
                  });
                  auto fwdnsrarg_1_2_3 = ({
                    // Computing ExtractValue
                    auto fwdnsrarg_1_2_4_0 = ({
                      // Computing subroutine CFMul_splat
                      auto fwdnsrarg_1_2_4_1_0 = gutils->getNewFromOriginal(call.getOperand(2));
                      auto fwdnsrarg_1_2_4_1_1 = gutils->getNewFromOriginal(call.getOperand(3));
                      auto fwdnsrarg_1_2_4_1_2 = gutils->getNewFromOriginal(call.getOperand(2));
                      auto fwdnsrarg_1_2_4_1_3 = gutils->getNewFromOriginal(call.getOperand(3));
                      ({
                        auto fwdnsrarg_1_2_4_1_sr_0 = ({
                          // Computing FSub
                          auto fwdnsrarg_1_2_4_1_sr_1_0 = ({
                            // Computing FMul
                            llvm::Value* fwdnsrarg_1_2_4_1_sr_1_1_0 = fwdnsrarg_1_2_4_1_0;
                            llvm::Value* fwdnsrarg_1_2_4_1_sr_1_1_1 = fwdnsrarg_1_2_4_1_2;
                            Builder2.CreateFMul(fwdnsrarg_1_2_4_1_sr_1_1_0, fwdnsrarg_1_2_4_1_sr_1_1_1);
                          });
                          auto fwdnsrarg_1_2_4_1_sr_1_1 = ({
                            // Computing FMul
                            llvm::Value* fwdnsrarg_1_2_4_1_sr_1_2_0 = fwdnsrarg_1_2_4_1_1;
                            llvm::Value* fwdnsrarg_1_2_4_1_sr_1_2_1 = fwdnsrarg_1_2_4_1_3;
                            Builder2.CreateFMul(fwdnsrarg_1_2_4_1_sr_1_2_0, fwdnsrarg_1_2_4_1_sr_1_2_1);
                          });
                          Builder2.CreateFSub(fwdnsrarg_1_2_4_1_sr_1_0, fwdnsrarg_1_2_4_1_sr_1_1);
                        });
                        auto fwdnsrarg_1_2_4_1_sr_1 = ({
                          // Computing FAdd
                          auto fwdnsrarg_1_2_4_1_sr_2_0 = ({
                            // Computing FMul
                            llvm::Value* fwdnsrarg_1_2_4_1_sr_2_1_0 = fwdnsrarg_1_2_4_1_0;
                            llvm::Value* fwdnsrarg_1_2_4_1_sr_2_1_1 = fwdnsrarg_1_2_4_1_3;
                            Builder2.CreateFMul(fwdnsrarg_1_2_4_1_sr_2_1_0, fwdnsrarg_1_2_4_1_sr_2_1_1);
                          });
                          auto fwdnsrarg_1_2_4_1_sr_2_1 = ({
                            // Computing FMul
                            llvm::Value* fwdnsrarg_1_2_4_1_sr_2_2_0 = fwdnsrarg_1_2_4_1_2;
                            llvm::Value* fwdnsrarg_1_2_4_1_sr_2_2_1 = fwdnsrarg_1_2_4_1_1;
                            Builder2.CreateFMul(fwdnsrarg_1_2_4_1_sr_2_2_0, fwdnsrarg_1_2_4_1_sr_2_2_1);
                          });
                          Builder2.CreateFAdd(fwdnsrarg_1_2_4_1_sr_2_0, fwdnsrarg_1_2_4_1_sr_2_1);
                        });
                        Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_1_2_4_1_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_4_1_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, fwdnsrarg_1_2_4_1_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                    Builder2.CreateExtractValue(fwdnsrarg_1_2_4_0, ArrayRef<unsigned>({(unsigned)(1)}));
                  });
                  ({
                    auto fwdnsrarg_1_2_sr_0 = ({
                      // Computing FSub
                      auto fwdnsrarg_1_2_sr_1_0 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_2_sr_1_1_0 = fwdnsrarg_1_2_0;
                        llvm::Value* fwdnsrarg_1_2_sr_1_1_1 = fwdnsrarg_1_2_2;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_1_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_1_1_0, idx), fwdnsrarg_1_2_sr_1_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_1_2_sr_1_1 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_2_sr_1_2_0 = fwdnsrarg_1_2_1;
                        llvm::Value* fwdnsrarg_1_2_sr_1_2_1 = fwdnsrarg_1_2_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_1_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_1_2_0, idx), fwdnsrarg_1_2_sr_1_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_1_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdnsrarg_1_2_sr_1 = ({
                      // Computing FAdd
                      auto fwdnsrarg_1_2_sr_2_0 = ({
                        // Computing FMul
                        auto fwdnsrarg_1_2_sr_2_1_0 = fwdnsrarg_1_2_0;
                        llvm::Value* fwdnsrarg_1_2_sr_2_1_1 = fwdnsrarg_1_2_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_2_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_2_1_0, idx), fwdnsrarg_1_2_sr_2_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_1_2_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_1_2_sr_2_2_0 = fwdnsrarg_1_2_2;
                        auto fwdnsrarg_1_2_sr_2_2_1 = fwdnsrarg_1_2_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul(fwdnsrarg_1_2_sr_2_2_0, (gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_2_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_2_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_2_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_0->getType() : getSubType(fwdnsrarg_1_2_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_2_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_1_1, ArrayRef<unsigned>({0}), "re2") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_1_1->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re2 = Builder2.CreateInsertValue(local_re2, gutils->extractMeta(Builder2, fwdnsrarg_1_1, ArrayRef<unsigned>({(unsigned)i, 0}), "re2."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im2 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_1_1, ArrayRef<unsigned>({1}), "im2") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_1_1->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im2 = Builder2.CreateInsertValue(local_im2, gutils->extractMeta(Builder2, fwdnsrarg_1_1, ArrayRef<unsigned>({(unsigned)i, 1}), "im2."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                ({
                  auto fwdnsrarg_1_sr_0 = ({
                    // Computing FAdd
                    auto fwdnsrarg_1_sr_1_0 = local_re1;
                    auto fwdnsrarg_1_sr_1_1 = local_re2;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_1_1, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  auto fwdnsrarg_1_sr_1 = ({
                    // Computing FAdd
                    auto fwdnsrarg_1_sr_2_0 = local_im1;
                    auto fwdnsrarg_1_sr_2_1 = local_im2;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_2_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_2_1, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_0->getType() : getSubType(fwdnsrarg_1_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
              Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              ({
                auto fwdnsrarg_sr_0 = local_re;
                auto fwdnsrarg_sr_1 = local_im;
                Value *res = UndefValue::get(gutils->getShadowType(call.getType()));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(0);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_0, idx), idxs);
                  }
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(1);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_1, idx), idxs);
                  }
                }
                 res;
              });
            });
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_r0 = nullptr;
          {
            Value *itmp = ({
              // Computing ExtractValue
              auto fwdarg_0 = ({
                // Computing subroutine CFDiv
                auto fwdarg_1_0 = dif;
                auto fwdarg_1_1 = ({
                  llvm::Value* fwdarg_1_2_0 = gutils->getNewFromOriginal(call.getOperand(2));
                  llvm::Value* fwdarg_1_2_1 = gutils->getNewFromOriginal(call.getOperand(3));
                  Value *res = UndefValue::get(ArrayType::get(fwdarg_1_2_0->getType(), 2));
                  {
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, fwdarg_1_2_0, idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, fwdarg_1_2_1, idxs);
                    }
                  }
                   res;
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFDiv_splat
                  auto fwdarg_1_sr_0 = local_re1;
                  auto fwdarg_1_sr_1 = local_im1;
                  auto fwdarg_1_sr_2 = local_re2;
                  auto fwdarg_1_sr_3 = local_im2;
                  llvm::Value *local_denom = nullptr;
                  ({
                    auto fwdarg_1_sr_sr_0 = ({
                      // Computing FDiv
                      auto fwdarg_1_sr_sr_1_0 = ({
                        // Computing FAdd
                        auto fwdarg_1_sr_sr_1_1_0 = ({
                          // Computing FMul
                          auto fwdarg_1_sr_sr_1_1_1_0 = fwdarg_1_sr_0;
                          llvm::Value* fwdarg_1_sr_sr_1_1_1_1 = fwdarg_1_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_1_0, idx), fwdarg_1_sr_sr_1_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_sr_sr_1_1_1 = ({
                          // Computing FMul
                          auto fwdarg_1_sr_sr_1_1_2_0 = fwdarg_1_sr_1;
                          llvm::Value* fwdarg_1_sr_sr_1_1_2_1 = fwdarg_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_2_0, idx), fwdarg_1_sr_sr_1_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_sr_sr_1_1 = ({
                        // Computing FAdd
                        auto fwdarg_1_sr_sr_1_2_0 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_sr_sr_1_2_1_0 = fwdarg_1_sr_2;
                          llvm::Value* fwdarg_1_sr_sr_1_2_1_1 = fwdarg_1_sr_2;
                          Builder2.CreateFMul(fwdarg_1_sr_sr_1_2_1_0, fwdarg_1_sr_sr_1_2_1_1);
                        });
                        auto fwdarg_1_sr_sr_1_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_sr_sr_1_2_2_0 = fwdarg_1_sr_3;
                          llvm::Value* fwdarg_1_sr_sr_1_2_2_1 = fwdarg_1_sr_3;
                          Builder2.CreateFMul(fwdarg_1_sr_sr_1_2_2_0, fwdarg_1_sr_sr_1_2_2_1);
                        });
                        Builder2.CreateFAdd(fwdarg_1_sr_sr_1_2_0, fwdarg_1_sr_sr_1_2_1);
                      });
                      local_denom = fwdarg_1_sr_sr_1_1;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_0, idx), fwdarg_1_sr_sr_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdarg_1_sr_sr_1 = ({
                      // Computing FDiv
                      auto fwdarg_1_sr_sr_2_0 = ({
                        // Computing FSub
                        auto fwdarg_1_sr_sr_2_1_0 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_sr_sr_2_1_1_0 = fwdarg_1_sr_2;
                          auto fwdarg_1_sr_sr_2_1_1_1 = fwdarg_1_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(fwdarg_1_sr_sr_2_1_1_0, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_1_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_sr_sr_2_1_1 = ({
                          // Computing FMul
                          auto fwdarg_1_sr_sr_2_1_2_0 = fwdarg_1_sr_0;
                          llvm::Value* fwdarg_1_sr_sr_2_1_2_1 = fwdarg_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_2_0, idx), fwdarg_1_sr_sr_2_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_sr_sr_2_1 = local_denom;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_0, idx), fwdarg_1_sr_sr_2_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_0->getType() : getSubType(fwdarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              local_r0 = fwdarg_0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), ArrayRef<unsigned>({(unsigned)(0)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {0}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          {
            Value *itmp = ({
              // Computing ExtractValue
              auto fwdarg_0 = local_r0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), ArrayRef<unsigned>({(unsigned)(1)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {1}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(1))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_r1 = nullptr;
          {
            Value *itmp = ({
              // Computing ExtractValue
              auto fwdarg_0 = ({
                // Computing subroutine CFDiv
                auto fwdarg_1_0 = dif;
                auto fwdarg_1_1 = ({
                  llvm::Value* fwdarg_1_2_0 = gutils->getNewFromOriginal(call.getOperand(3));
                  auto fwdarg_1_2_1 = ({
                    // Computing FNeg
                    llvm::Value* fwdarg_1_2_2_0 = gutils->getNewFromOriginal(call.getOperand(2));
                    Builder2.CreateFNeg(fwdarg_1_2_2_0);
                  });
                  Value *res = UndefValue::get(ArrayType::get(fwdarg_1_2_0->getType(), 2));
                  {
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, fwdarg_1_2_0, idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, fwdarg_1_2_1, idxs);
                    }
                  }
                   res;
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFDiv_splat
                  auto fwdarg_1_sr_0 = local_re1;
                  auto fwdarg_1_sr_1 = local_im1;
                  auto fwdarg_1_sr_2 = local_re2;
                  auto fwdarg_1_sr_3 = local_im2;
                  llvm::Value *local_denom = nullptr;
                  ({
                    auto fwdarg_1_sr_sr_0 = ({
                      // Computing FDiv
                      auto fwdarg_1_sr_sr_1_0 = ({
                        // Computing FAdd
                        auto fwdarg_1_sr_sr_1_1_0 = ({
                          // Computing FMul
                          auto fwdarg_1_sr_sr_1_1_1_0 = fwdarg_1_sr_0;
                          llvm::Value* fwdarg_1_sr_sr_1_1_1_1 = fwdarg_1_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_1_0, idx), fwdarg_1_sr_sr_1_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_sr_sr_1_1_1 = ({
                          // Computing FMul
                          auto fwdarg_1_sr_sr_1_1_2_0 = fwdarg_1_sr_1;
                          llvm::Value* fwdarg_1_sr_sr_1_1_2_1 = fwdarg_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_2_0, idx), fwdarg_1_sr_sr_1_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_sr_sr_1_1 = ({
                        // Computing FAdd
                        auto fwdarg_1_sr_sr_1_2_0 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_sr_sr_1_2_1_0 = fwdarg_1_sr_2;
                          llvm::Value* fwdarg_1_sr_sr_1_2_1_1 = fwdarg_1_sr_2;
                          Builder2.CreateFMul(fwdarg_1_sr_sr_1_2_1_0, fwdarg_1_sr_sr_1_2_1_1);
                        });
                        auto fwdarg_1_sr_sr_1_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_sr_sr_1_2_2_0 = fwdarg_1_sr_3;
                          llvm::Value* fwdarg_1_sr_sr_1_2_2_1 = fwdarg_1_sr_3;
                          Builder2.CreateFMul(fwdarg_1_sr_sr_1_2_2_0, fwdarg_1_sr_sr_1_2_2_1);
                        });
                        Builder2.CreateFAdd(fwdarg_1_sr_sr_1_2_0, fwdarg_1_sr_sr_1_2_1);
                      });
                      local_denom = fwdarg_1_sr_sr_1_1;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_0, idx), fwdarg_1_sr_sr_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdarg_1_sr_sr_1 = ({
                      // Computing FDiv
                      auto fwdarg_1_sr_sr_2_0 = ({
                        // Computing FSub
                        auto fwdarg_1_sr_sr_2_1_0 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_sr_sr_2_1_1_0 = fwdarg_1_sr_2;
                          auto fwdarg_1_sr_sr_2_1_1_1 = fwdarg_1_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(fwdarg_1_sr_sr_2_1_1_0, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_1_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_sr_sr_2_1_1 = ({
                          // Computing FMul
                          auto fwdarg_1_sr_sr_2_1_2_0 = fwdarg_1_sr_0;
                          llvm::Value* fwdarg_1_sr_sr_2_1_2_1 = fwdarg_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_2_0, idx), fwdarg_1_sr_sr_2_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_sr_sr_2_1 = local_denom;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_0, idx), fwdarg_1_sr_sr_2_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_0->getType() : getSubType(fwdarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              local_r1 = fwdarg_0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), ArrayRef<unsigned>({(unsigned)(0)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {0}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          {
            Value *itmp = ({
              // Computing ExtractValue
              auto fwdarg_0 = local_r1;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), ArrayRef<unsigned>({(unsigned)(1)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {1}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(2))) {
          Value *dif = diffe(call.getOperand(2), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(2))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_r2 = nullptr;
          {
            Value *itmp = ({
              // Computing ExtractValue
              auto fwdarg_0 = ({
                // Computing subroutine CFNeg
                auto fwdarg_1_0 = ({
                  // Computing subroutine CFDiv
                  auto fwdarg_1_1_0 = ({
                    // Computing subroutine CFMul
                    auto fwdarg_1_1_1_0 = dif;
                    auto fwdarg_1_1_1_1 = ({
                      llvm::Value* fwdarg_1_1_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      llvm::Value* fwdarg_1_1_1_2_1 = gutils->getNewFromOriginal(call.getOperand(1));
                      Value *res = UndefValue::get(ArrayType::get(fwdarg_1_1_1_2_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, fwdarg_1_1_1_2_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, fwdarg_1_1_1_2_1, idxs);
                        }
                      }
                       res;
                    });
                    Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_1_0->getType(), 0, 0)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_1_0->getType(), 0, 1)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1_1_1, ArrayRef<unsigned>({0}), "re2");
                    Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1_1_1, ArrayRef<unsigned>({1}), "im2");
                    ({
                      // Computing subroutine CFMul_splat
                      auto fwdarg_1_1_1_sr_0 = local_re1;
                      auto fwdarg_1_1_1_sr_1 = local_im1;
                      auto fwdarg_1_1_1_sr_2 = local_re2;
                      auto fwdarg_1_1_1_sr_3 = local_im2;
                      ({
                        auto fwdarg_1_1_1_sr_sr_0 = ({
                          // Computing FSub
                          auto fwdarg_1_1_1_sr_sr_1_0 = ({
                            // Computing FMul
                            auto fwdarg_1_1_1_sr_sr_1_1_0 = fwdarg_1_1_1_sr_0;
                            llvm::Value* fwdarg_1_1_1_sr_sr_1_1_1 = fwdarg_1_1_1_sr_2;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_sr_1_1_0, idx), fwdarg_1_1_1_sr_sr_1_1_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          auto fwdarg_1_1_1_sr_sr_1_1 = ({
                            // Computing FMul
                            auto fwdarg_1_1_1_sr_sr_1_2_0 = fwdarg_1_1_1_sr_1;
                            llvm::Value* fwdarg_1_1_1_sr_sr_1_2_1 = fwdarg_1_1_1_sr_3;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_sr_1_2_0, idx), fwdarg_1_1_1_sr_sr_1_2_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_sr_1_1 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_sr_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_1_1_sr_sr_1 = ({
                          // Computing FAdd
                          auto fwdarg_1_1_1_sr_sr_2_0 = ({
                            // Computing FMul
                            auto fwdarg_1_1_1_sr_sr_2_1_0 = fwdarg_1_1_1_sr_0;
                            llvm::Value* fwdarg_1_1_1_sr_sr_2_1_1 = fwdarg_1_1_1_sr_3;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_sr_2_1_0, idx), fwdarg_1_1_1_sr_sr_2_1_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          auto fwdarg_1_1_1_sr_sr_2_1 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_1_1_1_sr_sr_2_2_0 = fwdarg_1_1_1_sr_2;
                            auto fwdarg_1_1_1_sr_sr_2_2_1 = fwdarg_1_1_1_sr_1;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul(fwdarg_1_1_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_sr_2_2_1, idx));
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_sr_2_1 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_sr_2_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_sr_0->getType() : getSubType(fwdarg_1_1_1_sr_sr_0->getType(), -1), 2)));
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          {
                            std::vector<unsigned> idxs;
                            if (gutils->getWidth() != 1) idxs.push_back(idx);
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_sr_0, idx), idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            if (gutils->getWidth() != 1) idxs.push_back(idx);
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_sr_1, idx), idxs);
                          }
                        }
                         res;
                      });
                    });
                  });
                  auto fwdarg_1_1_1 = ({
                    // Computing subroutine CFMul_splat
                    auto fwdarg_1_1_2_0 = gutils->getNewFromOriginal(call.getOperand(2));
                    auto fwdarg_1_1_2_1 = gutils->getNewFromOriginal(call.getOperand(3));
                    auto fwdarg_1_1_2_2 = gutils->getNewFromOriginal(call.getOperand(2));
                    auto fwdarg_1_1_2_3 = gutils->getNewFromOriginal(call.getOperand(3));
                    ({
                      auto fwdarg_1_1_2_sr_0 = ({
                        // Computing FSub
                        auto fwdarg_1_1_2_sr_1_0 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_1_2_sr_1_1_0 = fwdarg_1_1_2_0;
                          llvm::Value* fwdarg_1_1_2_sr_1_1_1 = fwdarg_1_1_2_2;
                          Builder2.CreateFMul(fwdarg_1_1_2_sr_1_1_0, fwdarg_1_1_2_sr_1_1_1);
                        });
                        auto fwdarg_1_1_2_sr_1_1 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_1_2_sr_1_2_0 = fwdarg_1_1_2_1;
                          llvm::Value* fwdarg_1_1_2_sr_1_2_1 = fwdarg_1_1_2_3;
                          Builder2.CreateFMul(fwdarg_1_1_2_sr_1_2_0, fwdarg_1_1_2_sr_1_2_1);
                        });
                        Builder2.CreateFSub(fwdarg_1_1_2_sr_1_0, fwdarg_1_1_2_sr_1_1);
                      });
                      auto fwdarg_1_1_2_sr_1 = ({
                        // Computing FAdd
                        auto fwdarg_1_1_2_sr_2_0 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_1_2_sr_2_1_0 = fwdarg_1_1_2_0;
                          llvm::Value* fwdarg_1_1_2_sr_2_1_1 = fwdarg_1_1_2_3;
                          Builder2.CreateFMul(fwdarg_1_1_2_sr_2_1_0, fwdarg_1_1_2_sr_2_1_1);
                        });
                        auto fwdarg_1_1_2_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_1_2_sr_2_2_0 = fwdarg_1_1_2_2;
                          llvm::Value* fwdarg_1_1_2_sr_2_2_1 = fwdarg_1_1_2_1;
                          Builder2.CreateFMul(fwdarg_1_1_2_sr_2_2_0, fwdarg_1_1_2_sr_2_2_1);
                        });
                        Builder2.CreateFAdd(fwdarg_1_1_2_sr_2_0, fwdarg_1_1_2_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(fwdarg_1_1_2_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                  Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_0->getType(), 0, 0)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_0->getType(), 0, 1)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFDiv_splat
                    auto fwdarg_1_1_sr_0 = local_re1;
                    auto fwdarg_1_1_sr_1 = local_im1;
                    auto fwdarg_1_1_sr_2 = local_re2;
                    auto fwdarg_1_1_sr_3 = local_im2;
                    llvm::Value *local_denom = nullptr;
                    ({
                      auto fwdarg_1_1_sr_sr_0 = ({
                        // Computing FDiv
                        auto fwdarg_1_1_sr_sr_1_0 = ({
                          // Computing FAdd
                          auto fwdarg_1_1_sr_sr_1_1_0 = ({
                            // Computing FMul
                            auto fwdarg_1_1_sr_sr_1_1_1_0 = fwdarg_1_1_sr_0;
                            llvm::Value* fwdarg_1_1_sr_sr_1_1_1_1 = fwdarg_1_1_sr_2;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_1_1_0, idx), fwdarg_1_1_sr_sr_1_1_1_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          auto fwdarg_1_1_sr_sr_1_1_1 = ({
                            // Computing FMul
                            auto fwdarg_1_1_sr_sr_1_1_2_0 = fwdarg_1_1_sr_1;
                            llvm::Value* fwdarg_1_1_sr_sr_1_1_2_1 = fwdarg_1_1_sr_3;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_1_2_0, idx), fwdarg_1_1_sr_sr_1_1_2_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_1_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_1_sr_sr_1_1 = ({
                          // Computing FAdd
                          auto fwdarg_1_1_sr_sr_1_2_0 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_1_1_sr_sr_1_2_1_0 = fwdarg_1_1_sr_2;
                            llvm::Value* fwdarg_1_1_sr_sr_1_2_1_1 = fwdarg_1_1_sr_2;
                            Builder2.CreateFMul(fwdarg_1_1_sr_sr_1_2_1_0, fwdarg_1_1_sr_sr_1_2_1_1);
                          });
                          auto fwdarg_1_1_sr_sr_1_2_1 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_1_1_sr_sr_1_2_2_0 = fwdarg_1_1_sr_3;
                            llvm::Value* fwdarg_1_1_sr_sr_1_2_2_1 = fwdarg_1_1_sr_3;
                            Builder2.CreateFMul(fwdarg_1_1_sr_sr_1_2_2_0, fwdarg_1_1_sr_sr_1_2_2_1);
                          });
                          Builder2.CreateFAdd(fwdarg_1_1_sr_sr_1_2_0, fwdarg_1_1_sr_sr_1_2_1);
                        });
                        local_denom = fwdarg_1_1_sr_sr_1_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_0, idx), fwdarg_1_1_sr_sr_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_1_sr_sr_1 = ({
                        // Computing FDiv
                        auto fwdarg_1_1_sr_sr_2_0 = ({
                          // Computing FSub
                          auto fwdarg_1_1_sr_sr_2_1_0 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_1_1_sr_sr_2_1_1_0 = fwdarg_1_1_sr_2;
                            auto fwdarg_1_1_sr_sr_2_1_1_1 = fwdarg_1_1_sr_1;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul(fwdarg_1_1_sr_sr_2_1_1_0, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_1_1_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_1_1_1, idx));
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          auto fwdarg_1_1_sr_sr_2_1_1 = ({
                            // Computing FMul
                            auto fwdarg_1_1_sr_sr_2_1_2_0 = fwdarg_1_1_sr_0;
                            llvm::Value* fwdarg_1_1_sr_sr_2_1_2_1 = fwdarg_1_1_sr_3;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_1_2_0, idx), fwdarg_1_1_sr_sr_2_1_2_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_1_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_1_sr_sr_2_1 = local_denom;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_0, idx), fwdarg_1_1_sr_sr_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_0->getType() : getSubType(fwdarg_1_1_sr_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                ({
                  auto fwdarg_1_sr_0 = ({
                    // Computing FNeg
                    auto fwdarg_1_sr_1_0 = local_re;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_1_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_1_0, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  auto fwdarg_1_sr_1 = ({
                    // Computing FNeg
                    auto fwdarg_1_sr_2_0 = local_im;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_1_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_2_0, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_sr_0->getType() : getSubType(fwdarg_1_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
              local_r2 = fwdarg_0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), ArrayRef<unsigned>({(unsigned)(0)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {0}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          {
            Value *itmp = ({
              // Computing ExtractValue
              auto fwdarg_0 = local_r2;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), ArrayRef<unsigned>({(unsigned)(1)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {1}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(3))) {
          Value *dif = diffe(call.getOperand(3), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(3))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_r2 = nullptr;
          {
            Value *itmp = ({
              // Computing ExtractValue
              auto fwdarg_0 = ({
                // Computing subroutine CFDiv
                auto fwdarg_1_0 = ({
                  // Computing subroutine CFMul
                  auto fwdarg_1_1_0 = dif;
                  auto fwdarg_1_1_1 = ({
                    llvm::Value* fwdarg_1_1_2_0 = gutils->getNewFromOriginal(call.getOperand(1));
                    auto fwdarg_1_1_2_1 = ({
                      // Computing FNeg
                      llvm::Value* fwdarg_1_1_2_2_0 = gutils->getNewFromOriginal(call.getOperand(1));
                      Builder2.CreateFNeg(fwdarg_1_1_2_2_0);
                    });
                    Value *res = UndefValue::get(ArrayType::get(fwdarg_1_1_2_0->getType(), 2));
                    {
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_0, idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, fwdarg_1_1_2_1, idxs);
                      }
                    }
                     res;
                  });
                  Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_0->getType(), 0, 0)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_0->getType(), 0, 1)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto fwdarg_1_1_sr_0 = local_re1;
                    auto fwdarg_1_1_sr_1 = local_im1;
                    auto fwdarg_1_1_sr_2 = local_re2;
                    auto fwdarg_1_1_sr_3 = local_im2;
                    ({
                      auto fwdarg_1_1_sr_sr_0 = ({
                        // Computing FSub
                        auto fwdarg_1_1_sr_sr_1_0 = ({
                          // Computing FMul
                          auto fwdarg_1_1_sr_sr_1_1_0 = fwdarg_1_1_sr_0;
                          llvm::Value* fwdarg_1_1_sr_sr_1_1_1 = fwdarg_1_1_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_1_0, idx), fwdarg_1_1_sr_sr_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_1_sr_sr_1_1 = ({
                          // Computing FMul
                          auto fwdarg_1_1_sr_sr_1_2_0 = fwdarg_1_1_sr_1;
                          llvm::Value* fwdarg_1_1_sr_sr_1_2_1 = fwdarg_1_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_2_0, idx), fwdarg_1_1_sr_sr_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_1_sr_sr_1 = ({
                        // Computing FAdd
                        auto fwdarg_1_1_sr_sr_2_0 = ({
                          // Computing FMul
                          auto fwdarg_1_1_sr_sr_2_1_0 = fwdarg_1_1_sr_0;
                          llvm::Value* fwdarg_1_1_sr_sr_2_1_1 = fwdarg_1_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_1_0, idx), fwdarg_1_1_sr_sr_2_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_1_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_1_sr_sr_2_2_0 = fwdarg_1_1_sr_2;
                          auto fwdarg_1_1_sr_sr_2_2_1 = fwdarg_1_1_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(fwdarg_1_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_2_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_2_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_0->getType() : getSubType(fwdarg_1_1_sr_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                auto fwdarg_1_1 = ({
                  // Computing subroutine CFMul_splat
                  auto fwdarg_1_2_0 = gutils->getNewFromOriginal(call.getOperand(2));
                  auto fwdarg_1_2_1 = gutils->getNewFromOriginal(call.getOperand(3));
                  auto fwdarg_1_2_2 = gutils->getNewFromOriginal(call.getOperand(2));
                  auto fwdarg_1_2_3 = gutils->getNewFromOriginal(call.getOperand(3));
                  ({
                    auto fwdarg_1_2_sr_0 = ({
                      // Computing FSub
                      auto fwdarg_1_2_sr_1_0 = ({
                        // Computing FMul
                        llvm::Value* fwdarg_1_2_sr_1_1_0 = fwdarg_1_2_0;
                        llvm::Value* fwdarg_1_2_sr_1_1_1 = fwdarg_1_2_2;
                        Builder2.CreateFMul(fwdarg_1_2_sr_1_1_0, fwdarg_1_2_sr_1_1_1);
                      });
                      auto fwdarg_1_2_sr_1_1 = ({
                        // Computing FMul
                        llvm::Value* fwdarg_1_2_sr_1_2_0 = fwdarg_1_2_1;
                        llvm::Value* fwdarg_1_2_sr_1_2_1 = fwdarg_1_2_3;
                        Builder2.CreateFMul(fwdarg_1_2_sr_1_2_0, fwdarg_1_2_sr_1_2_1);
                      });
                      Builder2.CreateFSub(fwdarg_1_2_sr_1_0, fwdarg_1_2_sr_1_1);
                    });
                    auto fwdarg_1_2_sr_1 = ({
                      // Computing FAdd
                      auto fwdarg_1_2_sr_2_0 = ({
                        // Computing FMul
                        llvm::Value* fwdarg_1_2_sr_2_1_0 = fwdarg_1_2_0;
                        llvm::Value* fwdarg_1_2_sr_2_1_1 = fwdarg_1_2_3;
                        Builder2.CreateFMul(fwdarg_1_2_sr_2_1_0, fwdarg_1_2_sr_2_1_1);
                      });
                      auto fwdarg_1_2_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* fwdarg_1_2_sr_2_2_0 = fwdarg_1_2_2;
                        llvm::Value* fwdarg_1_2_sr_2_2_1 = fwdarg_1_2_1;
                        Builder2.CreateFMul(fwdarg_1_2_sr_2_2_0, fwdarg_1_2_sr_2_2_1);
                      });
                      Builder2.CreateFAdd(fwdarg_1_2_sr_2_0, fwdarg_1_2_sr_2_1);
                    });
                    Value *res = UndefValue::get(ArrayType::get(fwdarg_1_2_sr_0->getType(), 2));
                    {
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, fwdarg_1_2_sr_0, idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, fwdarg_1_2_sr_1, idxs);
                      }
                    }
                     res;
                  });
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFDiv_splat
                  auto fwdarg_1_sr_0 = local_re1;
                  auto fwdarg_1_sr_1 = local_im1;
                  auto fwdarg_1_sr_2 = local_re2;
                  auto fwdarg_1_sr_3 = local_im2;
                  llvm::Value *local_denom = nullptr;
                  ({
                    auto fwdarg_1_sr_sr_0 = ({
                      // Computing FDiv
                      auto fwdarg_1_sr_sr_1_0 = ({
                        // Computing FAdd
                        auto fwdarg_1_sr_sr_1_1_0 = ({
                          // Computing FMul
                          auto fwdarg_1_sr_sr_1_1_1_0 = fwdarg_1_sr_0;
                          llvm::Value* fwdarg_1_sr_sr_1_1_1_1 = fwdarg_1_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_1_0, idx), fwdarg_1_sr_sr_1_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_sr_sr_1_1_1 = ({
                          // Computing FMul
                          auto fwdarg_1_sr_sr_1_1_2_0 = fwdarg_1_sr_1;
                          llvm::Value* fwdarg_1_sr_sr_1_1_2_1 = fwdarg_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_2_0, idx), fwdarg_1_sr_sr_1_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_sr_sr_1_1 = ({
                        // Computing FAdd
                        auto fwdarg_1_sr_sr_1_2_0 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_sr_sr_1_2_1_0 = fwdarg_1_sr_2;
                          llvm::Value* fwdarg_1_sr_sr_1_2_1_1 = fwdarg_1_sr_2;
                          Builder2.CreateFMul(fwdarg_1_sr_sr_1_2_1_0, fwdarg_1_sr_sr_1_2_1_1);
                        });
                        auto fwdarg_1_sr_sr_1_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_sr_sr_1_2_2_0 = fwdarg_1_sr_3;
                          llvm::Value* fwdarg_1_sr_sr_1_2_2_1 = fwdarg_1_sr_3;
                          Builder2.CreateFMul(fwdarg_1_sr_sr_1_2_2_0, fwdarg_1_sr_sr_1_2_2_1);
                        });
                        Builder2.CreateFAdd(fwdarg_1_sr_sr_1_2_0, fwdarg_1_sr_sr_1_2_1);
                      });
                      local_denom = fwdarg_1_sr_sr_1_1;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_0, idx), fwdarg_1_sr_sr_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdarg_1_sr_sr_1 = ({
                      // Computing FDiv
                      auto fwdarg_1_sr_sr_2_0 = ({
                        // Computing FSub
                        auto fwdarg_1_sr_sr_2_1_0 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_sr_sr_2_1_1_0 = fwdarg_1_sr_2;
                          auto fwdarg_1_sr_sr_2_1_1_1 = fwdarg_1_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(fwdarg_1_sr_sr_2_1_1_0, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_1_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_sr_sr_2_1_1 = ({
                          // Computing FMul
                          auto fwdarg_1_sr_sr_2_1_2_0 = fwdarg_1_sr_0;
                          llvm::Value* fwdarg_1_sr_sr_2_1_2_1 = fwdarg_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_2_0, idx), fwdarg_1_sr_sr_2_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_sr_sr_2_1 = local_denom;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_0, idx), fwdarg_1_sr_sr_2_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_0->getType() : getSubType(fwdarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              local_r2 = fwdarg_0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), ArrayRef<unsigned>({(unsigned)(0)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {0}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          {
            Value *itmp = ({
              // Computing ExtractValue
              auto fwdarg_0 = local_r2;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), ArrayRef<unsigned>({(unsigned)(1)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {1}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        } else if (!dif && !gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        } else if (!dif && !gutils->isConstantValue(call.getOperand(2)) && !isa<PointerType>(call.getOperand(2)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        } else if (!dif && !gutils->isConstantValue(call.getOperand(3)) && !isa<PointerType>(call.getOperand(3)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          llvm::Value *local_r0 = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing ExtractValue
              auto revarg_0 = ({
                // Computing subroutine CFDiv
                auto revarg_1_0 = dif;
                auto revarg_1_1 = ({
                  llvm::Value* revarg_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(2)), Builder2);
                  llvm::Value* revarg_1_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(3)), Builder2);
                  Value *res = UndefValue::get(ArrayType::get(revarg_1_2_0->getType(), 2));
                  {
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, revarg_1_2_0, idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, revarg_1_2_1, idxs);
                    }
                  }
                   res;
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFDiv_splat
                  auto revarg_1_sr_0 = local_re1;
                  auto revarg_1_sr_1 = local_im1;
                  auto revarg_1_sr_2 = local_re2;
                  auto revarg_1_sr_3 = local_im2;
                  llvm::Value *local_denom = nullptr;
                  ({
                    auto revarg_1_sr_sr_0 = ({
                      // Computing FDiv
                      auto revarg_1_sr_sr_1_0 = ({
                        // Computing FAdd
                        auto revarg_1_sr_sr_1_1_0 = ({
                          // Computing FMul
                          auto revarg_1_sr_sr_1_1_1_0 = revarg_1_sr_0;
                          llvm::Value* revarg_1_sr_sr_1_1_1_1 = revarg_1_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_1_0, idx), revarg_1_sr_sr_1_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_sr_sr_1_1_1 = ({
                          // Computing FMul
                          auto revarg_1_sr_sr_1_1_2_0 = revarg_1_sr_1;
                          llvm::Value* revarg_1_sr_sr_1_1_2_1 = revarg_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_2_0, idx), revarg_1_sr_sr_1_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_sr_sr_1_1 = ({
                        // Computing FAdd
                        auto revarg_1_sr_sr_1_2_0 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_sr_sr_1_2_1_0 = revarg_1_sr_2;
                          llvm::Value* revarg_1_sr_sr_1_2_1_1 = revarg_1_sr_2;
                          Builder2.CreateFMul(revarg_1_sr_sr_1_2_1_0, revarg_1_sr_sr_1_2_1_1);
                        });
                        auto revarg_1_sr_sr_1_2_1 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_sr_sr_1_2_2_0 = revarg_1_sr_3;
                          llvm::Value* revarg_1_sr_sr_1_2_2_1 = revarg_1_sr_3;
                          Builder2.CreateFMul(revarg_1_sr_sr_1_2_2_0, revarg_1_sr_sr_1_2_2_1);
                        });
                        Builder2.CreateFAdd(revarg_1_sr_sr_1_2_0, revarg_1_sr_sr_1_2_1);
                      });
                      local_denom = revarg_1_sr_sr_1_1;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_0, idx), revarg_1_sr_sr_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto revarg_1_sr_sr_1 = ({
                      // Computing FDiv
                      auto revarg_1_sr_sr_2_0 = ({
                        // Computing FSub
                        auto revarg_1_sr_sr_2_1_0 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_sr_sr_2_1_1_0 = revarg_1_sr_2;
                          auto revarg_1_sr_sr_2_1_1_1 = revarg_1_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(revarg_1_sr_sr_2_1_1_0, (gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_1_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_sr_sr_2_1_1 = ({
                          // Computing FMul
                          auto revarg_1_sr_sr_2_1_2_0 = revarg_1_sr_0;
                          llvm::Value* revarg_1_sr_sr_2_1_2_1 = revarg_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_2_0, idx), revarg_1_sr_sr_2_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_sr_sr_2_1 = local_denom;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_0, idx), revarg_1_sr_sr_2_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_sr_sr_0->getType() : getSubType(revarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_sr_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_sr_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              local_r0 = revarg_0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), ArrayRef<unsigned>({(unsigned)(0)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          {
            Value *tmp = ({
              // Computing ExtractValue
              auto revarg_0 = local_r0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), ArrayRef<unsigned>({(unsigned)(1)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        if (!gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          llvm::Value *local_r1 = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing ExtractValue
              auto revarg_0 = ({
                // Computing subroutine CFDiv
                auto revarg_1_0 = dif;
                auto revarg_1_1 = ({
                  llvm::Value* revarg_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(3)), Builder2);
                  auto revarg_1_2_1 = ({
                    // Computing FNeg
                    llvm::Value* revarg_1_2_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(2)), Builder2);
                    Builder2.CreateFNeg(revarg_1_2_2_0);
                  });
                  Value *res = UndefValue::get(ArrayType::get(revarg_1_2_0->getType(), 2));
                  {
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, revarg_1_2_0, idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, revarg_1_2_1, idxs);
                    }
                  }
                   res;
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFDiv_splat
                  auto revarg_1_sr_0 = local_re1;
                  auto revarg_1_sr_1 = local_im1;
                  auto revarg_1_sr_2 = local_re2;
                  auto revarg_1_sr_3 = local_im2;
                  llvm::Value *local_denom = nullptr;
                  ({
                    auto revarg_1_sr_sr_0 = ({
                      // Computing FDiv
                      auto revarg_1_sr_sr_1_0 = ({
                        // Computing FAdd
                        auto revarg_1_sr_sr_1_1_0 = ({
                          // Computing FMul
                          auto revarg_1_sr_sr_1_1_1_0 = revarg_1_sr_0;
                          llvm::Value* revarg_1_sr_sr_1_1_1_1 = revarg_1_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_1_0, idx), revarg_1_sr_sr_1_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_sr_sr_1_1_1 = ({
                          // Computing FMul
                          auto revarg_1_sr_sr_1_1_2_0 = revarg_1_sr_1;
                          llvm::Value* revarg_1_sr_sr_1_1_2_1 = revarg_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_2_0, idx), revarg_1_sr_sr_1_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_sr_sr_1_1 = ({
                        // Computing FAdd
                        auto revarg_1_sr_sr_1_2_0 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_sr_sr_1_2_1_0 = revarg_1_sr_2;
                          llvm::Value* revarg_1_sr_sr_1_2_1_1 = revarg_1_sr_2;
                          Builder2.CreateFMul(revarg_1_sr_sr_1_2_1_0, revarg_1_sr_sr_1_2_1_1);
                        });
                        auto revarg_1_sr_sr_1_2_1 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_sr_sr_1_2_2_0 = revarg_1_sr_3;
                          llvm::Value* revarg_1_sr_sr_1_2_2_1 = revarg_1_sr_3;
                          Builder2.CreateFMul(revarg_1_sr_sr_1_2_2_0, revarg_1_sr_sr_1_2_2_1);
                        });
                        Builder2.CreateFAdd(revarg_1_sr_sr_1_2_0, revarg_1_sr_sr_1_2_1);
                      });
                      local_denom = revarg_1_sr_sr_1_1;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_0, idx), revarg_1_sr_sr_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto revarg_1_sr_sr_1 = ({
                      // Computing FDiv
                      auto revarg_1_sr_sr_2_0 = ({
                        // Computing FSub
                        auto revarg_1_sr_sr_2_1_0 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_sr_sr_2_1_1_0 = revarg_1_sr_2;
                          auto revarg_1_sr_sr_2_1_1_1 = revarg_1_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(revarg_1_sr_sr_2_1_1_0, (gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_1_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_sr_sr_2_1_1 = ({
                          // Computing FMul
                          auto revarg_1_sr_sr_2_1_2_0 = revarg_1_sr_0;
                          llvm::Value* revarg_1_sr_sr_2_1_2_1 = revarg_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_2_0, idx), revarg_1_sr_sr_2_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_sr_sr_2_1 = local_denom;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_0, idx), revarg_1_sr_sr_2_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_sr_sr_0->getType() : getSubType(revarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_sr_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_sr_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              local_r1 = revarg_0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), ArrayRef<unsigned>({(unsigned)(0)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(1)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          {
            Value *tmp = ({
              // Computing ExtractValue
              auto revarg_0 = local_r1;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), ArrayRef<unsigned>({(unsigned)(1)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(1)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(1), toadd, Builder2, call.getOperand(1)->getType());
        }
        if (!gutils->isConstantValue(call.getOperand(2)) && !isa<PointerType>(call.getOperand(2)->getType()) ) {
          llvm::Value *local_r2 = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing ExtractValue
              auto revarg_0 = ({
                // Computing subroutine CFNeg
                auto revarg_1_0 = ({
                  // Computing subroutine CFDiv
                  auto revarg_1_1_0 = ({
                    // Computing subroutine CFMul
                    auto revarg_1_1_1_0 = dif;
                    auto revarg_1_1_1_1 = ({
                      llvm::Value* revarg_1_1_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                      llvm::Value* revarg_1_1_1_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                      Value *res = UndefValue::get(ArrayType::get(revarg_1_1_1_2_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, revarg_1_1_1_2_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, revarg_1_1_1_2_1, idxs);
                        }
                      }
                       res;
                    });
                    Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_1_0->getType(), 0, 0)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_1_0->getType(), 0, 1)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1_1_1, ArrayRef<unsigned>({0}), "re2");
                    Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1_1_1, ArrayRef<unsigned>({1}), "im2");
                    ({
                      // Computing subroutine CFMul_splat
                      auto revarg_1_1_1_sr_0 = local_re1;
                      auto revarg_1_1_1_sr_1 = local_im1;
                      auto revarg_1_1_1_sr_2 = local_re2;
                      auto revarg_1_1_1_sr_3 = local_im2;
                      ({
                        auto revarg_1_1_1_sr_sr_0 = ({
                          // Computing FSub
                          auto revarg_1_1_1_sr_sr_1_0 = ({
                            // Computing FMul
                            auto revarg_1_1_1_sr_sr_1_1_0 = revarg_1_1_1_sr_0;
                            llvm::Value* revarg_1_1_1_sr_sr_1_1_1 = revarg_1_1_1_sr_2;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_sr_1_1_0, idx), revarg_1_1_1_sr_sr_1_1_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          auto revarg_1_1_1_sr_sr_1_1 = ({
                            // Computing FMul
                            auto revarg_1_1_1_sr_sr_1_2_0 = revarg_1_1_1_sr_1;
                            llvm::Value* revarg_1_1_1_sr_sr_1_2_1 = revarg_1_1_1_sr_3;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_sr_1_2_0, idx), revarg_1_1_1_sr_sr_1_2_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_1_1_1_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_1_1_sr_sr_1_1 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_sr_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_1_1_sr_sr_1 = ({
                          // Computing FAdd
                          auto revarg_1_1_1_sr_sr_2_0 = ({
                            // Computing FMul
                            auto revarg_1_1_1_sr_sr_2_1_0 = revarg_1_1_1_sr_0;
                            llvm::Value* revarg_1_1_1_sr_sr_2_1_1 = revarg_1_1_1_sr_3;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_sr_2_1_0, idx), revarg_1_1_1_sr_sr_2_1_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          auto revarg_1_1_1_sr_sr_2_1 = ({
                            // Computing FMul
                            llvm::Value* revarg_1_1_1_sr_sr_2_2_0 = revarg_1_1_1_sr_2;
                            auto revarg_1_1_1_sr_sr_2_2_1 = revarg_1_1_1_sr_1;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul(revarg_1_1_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? revarg_1_1_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_sr_2_2_1, idx));
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_1_1_1_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? revarg_1_1_1_sr_sr_2_1 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_sr_2_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_1_1_sr_sr_0->getType() : getSubType(revarg_1_1_1_sr_sr_0->getType(), -1), 2)));
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          {
                            std::vector<unsigned> idxs;
                            if (gutils->getWidth() != 1) idxs.push_back(idx);
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_1_sr_sr_0 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_sr_0, idx), idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            if (gutils->getWidth() != 1) idxs.push_back(idx);
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_1_sr_sr_1 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_sr_1, idx), idxs);
                          }
                        }
                         res;
                      });
                    });
                  });
                  auto revarg_1_1_1 = ({
                    // Computing subroutine CFMul_splat
                    auto revarg_1_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(2)), Builder2);
                    auto revarg_1_1_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(3)), Builder2);
                    auto revarg_1_1_2_2 = lookup(gutils->getNewFromOriginal(call.getOperand(2)), Builder2);
                    auto revarg_1_1_2_3 = lookup(gutils->getNewFromOriginal(call.getOperand(3)), Builder2);
                    ({
                      auto revarg_1_1_2_sr_0 = ({
                        // Computing FSub
                        auto revarg_1_1_2_sr_1_0 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_1_2_sr_1_1_0 = revarg_1_1_2_0;
                          llvm::Value* revarg_1_1_2_sr_1_1_1 = revarg_1_1_2_2;
                          Builder2.CreateFMul(revarg_1_1_2_sr_1_1_0, revarg_1_1_2_sr_1_1_1);
                        });
                        auto revarg_1_1_2_sr_1_1 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_1_2_sr_1_2_0 = revarg_1_1_2_1;
                          llvm::Value* revarg_1_1_2_sr_1_2_1 = revarg_1_1_2_3;
                          Builder2.CreateFMul(revarg_1_1_2_sr_1_2_0, revarg_1_1_2_sr_1_2_1);
                        });
                        Builder2.CreateFSub(revarg_1_1_2_sr_1_0, revarg_1_1_2_sr_1_1);
                      });
                      auto revarg_1_1_2_sr_1 = ({
                        // Computing FAdd
                        auto revarg_1_1_2_sr_2_0 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_1_2_sr_2_1_0 = revarg_1_1_2_0;
                          llvm::Value* revarg_1_1_2_sr_2_1_1 = revarg_1_1_2_3;
                          Builder2.CreateFMul(revarg_1_1_2_sr_2_1_0, revarg_1_1_2_sr_2_1_1);
                        });
                        auto revarg_1_1_2_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_1_2_sr_2_2_0 = revarg_1_1_2_2;
                          llvm::Value* revarg_1_1_2_sr_2_2_1 = revarg_1_1_2_1;
                          Builder2.CreateFMul(revarg_1_1_2_sr_2_2_0, revarg_1_1_2_sr_2_2_1);
                        });
                        Builder2.CreateFAdd(revarg_1_1_2_sr_2_0, revarg_1_1_2_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(revarg_1_1_2_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, revarg_1_1_2_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, revarg_1_1_2_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                  Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_0->getType(), 0, 0)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_0->getType(), 0, 1)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFDiv_splat
                    auto revarg_1_1_sr_0 = local_re1;
                    auto revarg_1_1_sr_1 = local_im1;
                    auto revarg_1_1_sr_2 = local_re2;
                    auto revarg_1_1_sr_3 = local_im2;
                    llvm::Value *local_denom = nullptr;
                    ({
                      auto revarg_1_1_sr_sr_0 = ({
                        // Computing FDiv
                        auto revarg_1_1_sr_sr_1_0 = ({
                          // Computing FAdd
                          auto revarg_1_1_sr_sr_1_1_0 = ({
                            // Computing FMul
                            auto revarg_1_1_sr_sr_1_1_1_0 = revarg_1_1_sr_0;
                            llvm::Value* revarg_1_1_sr_sr_1_1_1_1 = revarg_1_1_sr_2;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_1_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_1_1_0, idx), revarg_1_1_sr_sr_1_1_1_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          auto revarg_1_1_sr_sr_1_1_1 = ({
                            // Computing FMul
                            auto revarg_1_1_sr_sr_1_1_2_0 = revarg_1_1_sr_1;
                            llvm::Value* revarg_1_1_sr_sr_1_1_2_1 = revarg_1_1_sr_3;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_1_2_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_1_2_0, idx), revarg_1_1_sr_sr_1_1_2_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_1_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_1_sr_sr_1_1 = ({
                          // Computing FAdd
                          auto revarg_1_1_sr_sr_1_2_0 = ({
                            // Computing FMul
                            llvm::Value* revarg_1_1_sr_sr_1_2_1_0 = revarg_1_1_sr_2;
                            llvm::Value* revarg_1_1_sr_sr_1_2_1_1 = revarg_1_1_sr_2;
                            Builder2.CreateFMul(revarg_1_1_sr_sr_1_2_1_0, revarg_1_1_sr_sr_1_2_1_1);
                          });
                          auto revarg_1_1_sr_sr_1_2_1 = ({
                            // Computing FMul
                            llvm::Value* revarg_1_1_sr_sr_1_2_2_0 = revarg_1_1_sr_3;
                            llvm::Value* revarg_1_1_sr_sr_1_2_2_1 = revarg_1_1_sr_3;
                            Builder2.CreateFMul(revarg_1_1_sr_sr_1_2_2_0, revarg_1_1_sr_sr_1_2_2_1);
                          });
                          Builder2.CreateFAdd(revarg_1_1_sr_sr_1_2_0, revarg_1_1_sr_sr_1_2_1);
                        });
                        local_denom = revarg_1_1_sr_sr_1_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_0, idx), revarg_1_1_sr_sr_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_1_sr_sr_1 = ({
                        // Computing FDiv
                        auto revarg_1_1_sr_sr_2_0 = ({
                          // Computing FSub
                          auto revarg_1_1_sr_sr_2_1_0 = ({
                            // Computing FMul
                            llvm::Value* revarg_1_1_sr_sr_2_1_1_0 = revarg_1_1_sr_2;
                            auto revarg_1_1_sr_sr_2_1_1_1 = revarg_1_1_sr_1;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul(revarg_1_1_sr_sr_2_1_1_0, (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_1_1_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_1_1_1, idx));
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          auto revarg_1_1_sr_sr_2_1_1 = ({
                            // Computing FMul
                            auto revarg_1_1_sr_sr_2_1_2_0 = revarg_1_1_sr_0;
                            llvm::Value* revarg_1_1_sr_sr_2_1_2_1 = revarg_1_1_sr_3;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_1_2_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_1_2_0, idx), revarg_1_1_sr_sr_2_1_2_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_1_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_1_sr_sr_2_1 = local_denom;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_0, idx), revarg_1_1_sr_sr_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_0->getType() : getSubType(revarg_1_1_sr_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                ({
                  auto revarg_1_sr_0 = ({
                    // Computing FNeg
                    auto revarg_1_sr_1_0 = local_re;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_1_sr_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_1_0, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  auto revarg_1_sr_1 = ({
                    // Computing FNeg
                    auto revarg_1_sr_2_0 = local_im;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_1_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_2_0, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_sr_0->getType() : getSubType(revarg_1_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_0 : gutils->extractMeta(Builder2, revarg_1_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_1 : gutils->extractMeta(Builder2, revarg_1_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
              local_r2 = revarg_0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), ArrayRef<unsigned>({(unsigned)(0)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(2)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          {
            Value *tmp = ({
              // Computing ExtractValue
              auto revarg_0 = local_r2;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), ArrayRef<unsigned>({(unsigned)(1)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(2)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(2), toadd, Builder2, call.getOperand(2)->getType());
        }
        if (!gutils->isConstantValue(call.getOperand(3)) && !isa<PointerType>(call.getOperand(3)->getType()) ) {
          llvm::Value *local_r2 = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing ExtractValue
              auto revarg_0 = ({
                // Computing subroutine CFDiv
                auto revarg_1_0 = ({
                  // Computing subroutine CFMul
                  auto revarg_1_1_0 = dif;
                  auto revarg_1_1_1 = ({
                    llvm::Value* revarg_1_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                    auto revarg_1_1_2_1 = ({
                      // Computing FNeg
                      llvm::Value* revarg_1_1_2_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                      Builder2.CreateFNeg(revarg_1_1_2_2_0);
                    });
                    Value *res = UndefValue::get(ArrayType::get(revarg_1_1_2_0->getType(), 2));
                    {
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, revarg_1_1_2_0, idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, revarg_1_1_2_1, idxs);
                      }
                    }
                     res;
                  });
                  Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_0->getType(), 0, 0)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_0->getType(), 0, 1)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto revarg_1_1_sr_0 = local_re1;
                    auto revarg_1_1_sr_1 = local_im1;
                    auto revarg_1_1_sr_2 = local_re2;
                    auto revarg_1_1_sr_3 = local_im2;
                    ({
                      auto revarg_1_1_sr_sr_0 = ({
                        // Computing FSub
                        auto revarg_1_1_sr_sr_1_0 = ({
                          // Computing FMul
                          auto revarg_1_1_sr_sr_1_1_0 = revarg_1_1_sr_0;
                          llvm::Value* revarg_1_1_sr_sr_1_1_1 = revarg_1_1_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_1_0, idx), revarg_1_1_sr_sr_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_1_sr_sr_1_1 = ({
                          // Computing FMul
                          auto revarg_1_1_sr_sr_1_2_0 = revarg_1_1_sr_1;
                          llvm::Value* revarg_1_1_sr_sr_1_2_1 = revarg_1_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_2_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_2_0, idx), revarg_1_1_sr_sr_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_1_sr_sr_1 = ({
                        // Computing FAdd
                        auto revarg_1_1_sr_sr_2_0 = ({
                          // Computing FMul
                          auto revarg_1_1_sr_sr_2_1_0 = revarg_1_1_sr_0;
                          llvm::Value* revarg_1_1_sr_sr_2_1_1 = revarg_1_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_1_0, idx), revarg_1_1_sr_sr_2_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_1_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_1_sr_sr_2_2_0 = revarg_1_1_sr_2;
                          auto revarg_1_1_sr_sr_2_2_1 = revarg_1_1_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(revarg_1_1_sr_sr_2_2_0, (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_2_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_2_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_0, idx), (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_2_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_2_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_1_sr_sr_0->getType() : getSubType(revarg_1_1_sr_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_sr_sr_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                auto revarg_1_1 = ({
                  // Computing subroutine CFMul_splat
                  auto revarg_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(2)), Builder2);
                  auto revarg_1_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(3)), Builder2);
                  auto revarg_1_2_2 = lookup(gutils->getNewFromOriginal(call.getOperand(2)), Builder2);
                  auto revarg_1_2_3 = lookup(gutils->getNewFromOriginal(call.getOperand(3)), Builder2);
                  ({
                    auto revarg_1_2_sr_0 = ({
                      // Computing FSub
                      auto revarg_1_2_sr_1_0 = ({
                        // Computing FMul
                        llvm::Value* revarg_1_2_sr_1_1_0 = revarg_1_2_0;
                        llvm::Value* revarg_1_2_sr_1_1_1 = revarg_1_2_2;
                        Builder2.CreateFMul(revarg_1_2_sr_1_1_0, revarg_1_2_sr_1_1_1);
                      });
                      auto revarg_1_2_sr_1_1 = ({
                        // Computing FMul
                        llvm::Value* revarg_1_2_sr_1_2_0 = revarg_1_2_1;
                        llvm::Value* revarg_1_2_sr_1_2_1 = revarg_1_2_3;
                        Builder2.CreateFMul(revarg_1_2_sr_1_2_0, revarg_1_2_sr_1_2_1);
                      });
                      Builder2.CreateFSub(revarg_1_2_sr_1_0, revarg_1_2_sr_1_1);
                    });
                    auto revarg_1_2_sr_1 = ({
                      // Computing FAdd
                      auto revarg_1_2_sr_2_0 = ({
                        // Computing FMul
                        llvm::Value* revarg_1_2_sr_2_1_0 = revarg_1_2_0;
                        llvm::Value* revarg_1_2_sr_2_1_1 = revarg_1_2_3;
                        Builder2.CreateFMul(revarg_1_2_sr_2_1_0, revarg_1_2_sr_2_1_1);
                      });
                      auto revarg_1_2_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* revarg_1_2_sr_2_2_0 = revarg_1_2_2;
                        llvm::Value* revarg_1_2_sr_2_2_1 = revarg_1_2_1;
                        Builder2.CreateFMul(revarg_1_2_sr_2_2_0, revarg_1_2_sr_2_2_1);
                      });
                      Builder2.CreateFAdd(revarg_1_2_sr_2_0, revarg_1_2_sr_2_1);
                    });
                    Value *res = UndefValue::get(ArrayType::get(revarg_1_2_sr_0->getType(), 2));
                    {
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, revarg_1_2_sr_0, idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, revarg_1_2_sr_1, idxs);
                      }
                    }
                     res;
                  });
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFDiv_splat
                  auto revarg_1_sr_0 = local_re1;
                  auto revarg_1_sr_1 = local_im1;
                  auto revarg_1_sr_2 = local_re2;
                  auto revarg_1_sr_3 = local_im2;
                  llvm::Value *local_denom = nullptr;
                  ({
                    auto revarg_1_sr_sr_0 = ({
                      // Computing FDiv
                      auto revarg_1_sr_sr_1_0 = ({
                        // Computing FAdd
                        auto revarg_1_sr_sr_1_1_0 = ({
                          // Computing FMul
                          auto revarg_1_sr_sr_1_1_1_0 = revarg_1_sr_0;
                          llvm::Value* revarg_1_sr_sr_1_1_1_1 = revarg_1_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_1_0, idx), revarg_1_sr_sr_1_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_sr_sr_1_1_1 = ({
                          // Computing FMul
                          auto revarg_1_sr_sr_1_1_2_0 = revarg_1_sr_1;
                          llvm::Value* revarg_1_sr_sr_1_1_2_1 = revarg_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_2_0, idx), revarg_1_sr_sr_1_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_sr_sr_1_1 = ({
                        // Computing FAdd
                        auto revarg_1_sr_sr_1_2_0 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_sr_sr_1_2_1_0 = revarg_1_sr_2;
                          llvm::Value* revarg_1_sr_sr_1_2_1_1 = revarg_1_sr_2;
                          Builder2.CreateFMul(revarg_1_sr_sr_1_2_1_0, revarg_1_sr_sr_1_2_1_1);
                        });
                        auto revarg_1_sr_sr_1_2_1 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_sr_sr_1_2_2_0 = revarg_1_sr_3;
                          llvm::Value* revarg_1_sr_sr_1_2_2_1 = revarg_1_sr_3;
                          Builder2.CreateFMul(revarg_1_sr_sr_1_2_2_0, revarg_1_sr_sr_1_2_2_1);
                        });
                        Builder2.CreateFAdd(revarg_1_sr_sr_1_2_0, revarg_1_sr_sr_1_2_1);
                      });
                      local_denom = revarg_1_sr_sr_1_1;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_0, idx), revarg_1_sr_sr_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto revarg_1_sr_sr_1 = ({
                      // Computing FDiv
                      auto revarg_1_sr_sr_2_0 = ({
                        // Computing FSub
                        auto revarg_1_sr_sr_2_1_0 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_sr_sr_2_1_1_0 = revarg_1_sr_2;
                          auto revarg_1_sr_sr_2_1_1_1 = revarg_1_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(revarg_1_sr_sr_2_1_1_0, (gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_1_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_sr_sr_2_1_1 = ({
                          // Computing FMul
                          auto revarg_1_sr_sr_2_1_2_0 = revarg_1_sr_0;
                          llvm::Value* revarg_1_sr_sr_2_1_2_1 = revarg_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_2_0, idx), revarg_1_sr_sr_2_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_sr_sr_2_1 = local_denom;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_0, idx), revarg_1_sr_sr_2_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_sr_sr_0->getType() : getSubType(revarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_sr_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_sr_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              local_r2 = revarg_0;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), ArrayRef<unsigned>({(unsigned)(0)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(3)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          {
            Value *tmp = ({
              // Computing ExtractValue
              auto revarg_0 = local_r2;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateExtractValue((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), ArrayRef<unsigned>({(unsigned)(1)}));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(3)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(3), toadd, Builder2, call.getOperand(3)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "scalbn" ||
      funcName == "scalbnf" ||
      funcName == "scalbnl" ||
      funcName == "scalbnln" ||
      funcName == "scalbnlnf" ||
      funcName == "scalbnlnl") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing Call
              auto fwdarg_0 = dif;
              llvm::Value* fwdarg_1 = gutils->getNewFromOriginal(call.getOperand(1));
              auto callval = cast<CallInst>(&call)->getCalledOperand();
              auto FT = cast<CallInst>(&call)->getFunctionType();
              auto cconv = cast<CallInst>(&call)->getCallingConv();
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({(gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing Call
              auto fwdarg_0 = dif;
              llvm::Value* fwdarg_1 = gutils->getNewFromOriginal(call.getOperand(1));
              auto callval = cast<CallInst>(&call)->getCalledOperand();
              auto FT = cast<CallInst>(&call)->getFunctionType();
              auto cconv = cast<CallInst>(&call)->getCallingConv();
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({(gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing Call
              auto revarg_0 = dif;
              llvm::Value* revarg_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
              auto callval = cast<CallInst>(&call)->getCalledOperand();
              auto FT = cast<CallInst>(&call)->getFunctionType();
              auto cconv = cast<CallInst>(&call)->getCallingConv();
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({(gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "ldexp" ||
      funcName == "ldexpf" ||
      funcName == "ldexpl") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing Call
              auto fwdarg_0 = dif;
              llvm::Value* fwdarg_1 = gutils->getNewFromOriginal(call.getOperand(1));
              auto callval = cast<CallInst>(&call)->getCalledOperand();
              auto FT = cast<CallInst>(&call)->getFunctionType();
              auto cconv = cast<CallInst>(&call)->getCallingConv();
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({(gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing Call
              auto fwdarg_0 = dif;
              llvm::Value* fwdarg_1 = gutils->getNewFromOriginal(call.getOperand(1));
              auto callval = cast<CallInst>(&call)->getCalledOperand();
              auto FT = cast<CallInst>(&call)->getFunctionType();
              auto cconv = cast<CallInst>(&call)->getCallingConv();
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({(gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing Call
              auto revarg_0 = dif;
              llvm::Value* revarg_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
              auto callval = cast<CallInst>(&call)->getCalledOperand();
              auto FT = cast<CallInst>(&call)->getFunctionType();
              auto cconv = cast<CallInst>(&call)->getCallingConv();
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({(gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "frexp" ||
      funcName == "frexpf" ||
      funcName == "frexpl") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_mask = nullptr;
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ({
                  // Computing BitCast
                  auto fwdarg_2_1_0 = ({
                    // Computing And
                    auto fwdarg_2_1_1_0 = ({ 
  auto ty = call.getType();
  unsigned low = 0;
  unsigned high = 0;
  unsigned tsize = 0;
  if (ty->isHalfTy()) {
    tsize = 16;
    high = tsize - 1;
    low = high - 5;
  } else if (ty->isFloatTy()) {
    tsize = 32;
    high = tsize - 1;
    low = high - 8;
  } else if (ty->isDoubleTy()) {
    tsize = 64;
    high = tsize - 1;
    low = high - 11;
  } else if (ty->isX86_FP80Ty()) {
    tsize = 80;
    high = tsize - 1;
    low = high - 16;
    // x86_fp80 has only 15 exponent bits, but we must also
    // retain the most-significant bit of the mantissa as
    // there is no implicit leading
  } else if (ty->isBFloatTy()) {
    tsize = 16;
    high = tsize - 1;
    low = high - 8;
  } else if (ty->isFP128Ty()) {
    tsize = 128;
    high = tsize - 1;
    low = high - 15;
  } else {
    llvm_unreachable("Unknown type within frexp");
  }
  APInt eval = APInt::getBitsSet(tsize, low, high);
  auto ity = IntegerType::get(ty->getContext(), tsize);
  ConstantInt::get(ity, eval);
 });
                    local_mask = fwdarg_2_1_1_0;
                    auto fwdarg_2_1_1_1 = ({
                      // Computing BitCast
                      llvm::Value* fwdarg_2_1_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto fwdarg_2_1_1_2_1 = local_mask->getType();
                      Builder2.CreateBitCast(fwdarg_2_1_1_2_0, fwdarg_2_1_1_2_1);
                    });
                    Builder2.CreateAnd(fwdarg_2_1_1_0, fwdarg_2_1_1_1);
                  });
                  auto fwdarg_2_1_1 = call.getOperand(0)->getType();
                  Builder2.CreateBitCast(fwdarg_2_1_0, fwdarg_2_1_1);
                });
                auto fwdarg_2_1 = ConstantFP::get(call.getOperand(0)->getType(), "2");
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          llvm::Value *local_mask = nullptr;
          {
            Value *itmp = ({
              // Computing FDiv
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FMul
                auto fwdarg_2_0 = ({
                  // Computing BitCast
                  auto fwdarg_2_1_0 = ({
                    // Computing And
                    auto fwdarg_2_1_1_0 = ({ 
  auto ty = call.getType();
  unsigned low = 0;
  unsigned high = 0;
  unsigned tsize = 0;
  if (ty->isHalfTy()) {
    tsize = 16;
    high = tsize - 1;
    low = high - 5;
  } else if (ty->isFloatTy()) {
    tsize = 32;
    high = tsize - 1;
    low = high - 8;
  } else if (ty->isDoubleTy()) {
    tsize = 64;
    high = tsize - 1;
    low = high - 11;
  } else if (ty->isX86_FP80Ty()) {
    tsize = 80;
    high = tsize - 1;
    low = high - 16;
    // x86_fp80 has only 15 exponent bits, but we must also
    // retain the most-significant bit of the mantissa as
    // there is no implicit leading
  } else if (ty->isBFloatTy()) {
    tsize = 16;
    high = tsize - 1;
    low = high - 8;
  } else if (ty->isFP128Ty()) {
    tsize = 128;
    high = tsize - 1;
    low = high - 15;
  } else {
    llvm_unreachable("Unknown type within frexp");
  }
  APInt eval = APInt::getBitsSet(tsize, low, high);
  auto ity = IntegerType::get(ty->getContext(), tsize);
  ConstantInt::get(ity, eval);
 });
                    local_mask = fwdarg_2_1_1_0;
                    auto fwdarg_2_1_1_1 = ({
                      // Computing BitCast
                      llvm::Value* fwdarg_2_1_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto fwdarg_2_1_1_2_1 = local_mask->getType();
                      Builder2.CreateBitCast(fwdarg_2_1_1_2_0, fwdarg_2_1_1_2_1);
                    });
                    Builder2.CreateAnd(fwdarg_2_1_1_0, fwdarg_2_1_1_1);
                  });
                  auto fwdarg_2_1_1 = call.getOperand(0)->getType();
                  Builder2.CreateBitCast(fwdarg_2_1_0, fwdarg_2_1_1);
                });
                auto fwdarg_2_1 = ConstantFP::get(call.getOperand(0)->getType(), "2");
                Builder2.CreateFMul(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          llvm::Value *local_mask = nullptr;
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FDiv
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FMul
                auto revarg_2_0 = ({
                  // Computing BitCast
                  auto revarg_2_1_0 = ({
                    // Computing And
                    auto revarg_2_1_1_0 = ({ 
  auto ty = call.getType();
  unsigned low = 0;
  unsigned high = 0;
  unsigned tsize = 0;
  if (ty->isHalfTy()) {
    tsize = 16;
    high = tsize - 1;
    low = high - 5;
  } else if (ty->isFloatTy()) {
    tsize = 32;
    high = tsize - 1;
    low = high - 8;
  } else if (ty->isDoubleTy()) {
    tsize = 64;
    high = tsize - 1;
    low = high - 11;
  } else if (ty->isX86_FP80Ty()) {
    tsize = 80;
    high = tsize - 1;
    low = high - 16;
    // x86_fp80 has only 15 exponent bits, but we must also
    // retain the most-significant bit of the mantissa as
    // there is no implicit leading
  } else if (ty->isBFloatTy()) {
    tsize = 16;
    high = tsize - 1;
    low = high - 8;
  } else if (ty->isFP128Ty()) {
    tsize = 128;
    high = tsize - 1;
    low = high - 15;
  } else {
    llvm_unreachable("Unknown type within frexp");
  }
  APInt eval = APInt::getBitsSet(tsize, low, high);
  auto ity = IntegerType::get(ty->getContext(), tsize);
  ConstantInt::get(ity, eval);
 });
                    local_mask = revarg_2_1_1_0;
                    auto revarg_2_1_1_1 = ({
                      // Computing BitCast
                      llvm::Value* revarg_2_1_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                      auto revarg_2_1_1_2_1 = local_mask->getType();
                      Builder2.CreateBitCast(revarg_2_1_1_2_0, revarg_2_1_1_2_1);
                    });
                    Builder2.CreateAnd(revarg_2_1_1_0, revarg_2_1_1_1);
                  });
                  auto revarg_2_1_1 = call.getOperand(0)->getType();
                  Builder2.CreateBitCast(revarg_2_1_0, revarg_2_1_1);
                });
                auto revarg_2_1 = ConstantFP::get(call.getOperand(0)->getType(), "2");
                Builder2.CreateFMul(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "cabs" ||
      funcName == "cabsf" ||
      funcName == "cabsl") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FDiv
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto fwdarg_2_1 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_2_2_1 = gutils->getNewFromOriginal(call.getOperand(1));
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_0, fwdarg_2_2_1})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFDiv(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FDiv
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(1));
                auto fwdarg_2_1 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_2_2_1 = gutils->getNewFromOriginal(call.getOperand(1));
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_0, fwdarg_2_2_1})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFDiv(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FDiv
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto fwdarg_2_1 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_2_2_1 = gutils->getNewFromOriginal(call.getOperand(1));
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_0, fwdarg_2_2_1})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFDiv(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(1))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing FMul
              auto fwdarg_0 = dif;
              auto fwdarg_1 = ({
                // Computing FDiv
                llvm::Value* fwdarg_2_0 = gutils->getNewFromOriginal(call.getOperand(1));
                auto fwdarg_2_1 = ({
                  // Computing Call
                  llvm::Value* fwdarg_2_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  llvm::Value* fwdarg_2_2_1 = gutils->getNewFromOriginal(call.getOperand(1));
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_2_2_0, fwdarg_2_2_1})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFDiv(fwdarg_2_0, fwdarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_0 : gutils->extractMeta(Builder2, fwdarg_0, idx), fwdarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        } else if (!dif && !gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FDiv
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto revarg_2_1 = ({
                  // Computing Call
                  llvm::Value* revarg_2_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  llvm::Value* revarg_2_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_0, revarg_2_2_1})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFDiv(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        if (!gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing FMul
              auto revarg_0 = dif;
              auto revarg_1 = ({
                // Computing FDiv
                llvm::Value* revarg_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                auto revarg_2_1 = ({
                  // Computing Call
                  llvm::Value* revarg_2_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  llvm::Value* revarg_2_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                  auto callval = cast<CallInst>(&call)->getCalledOperand();
                  auto FT = cast<CallInst>(&call)->getFunctionType();
                  auto cconv = cast<CallInst>(&call)->getCallingConv();
                  CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_2_2_0, revarg_2_2_1})));
                  V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                  V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                  V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                  V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                  V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                  V;
                });
                Builder2.CreateFDiv(revarg_2_0, revarg_2_1);
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_0 : gutils->extractMeta(Builder2, revarg_0, idx), revarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(1)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(1), toadd, Builder2, call.getOperand(1)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "cabs" ||
      funcName == "cabsf" ||
      funcName == "cabsl") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
            Value *res = ({
              // Computing FDiv
              auto fwdnsrarg_0 = ({
                // Computing FAdd
                auto fwdnsrarg_1_0 = ({
                  // Computing SelectIfActive
                  llvm::Value *imVal = call.getOperand(0);
                  bool condition = !gutils->isConstantValue(imVal);
                  bool vectorized = false;
                  (void)vectorized;
                  if (condition) {
                    imVal = ({
                      // Computing FMul
                      auto fwdnsrarg_1_1_cic_0 = gutils->invertPointerM(gutils->extractMeta(Builder2,call.getOperand(0), ArrayRef<unsigned>({0})), Builder2, TR.query(&call));
                      llvm::Value* fwdnsrarg_1_1_cic_1 = gutils->extractMeta(Builder2, gutils->getNewFromOriginal(call.getOperand(0)), ArrayRef<unsigned>({0}));
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_1_cic_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_1_cic_0, idx), fwdnsrarg_1_1_cic_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    vectorized = true;
                  } else {
                    imVal = Constant::getNullValue(gutils->extractMeta(call.getOperand(0)->getType(), ArrayRef<unsigned>({0})));
                  }
                  if (!vectorized) {
                    llvm::Value* vec_imVal = gutils->getWidth() == 1 ? imVal : UndefValue::get(gutils->getShadowType(imVal->getType()));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        vec_imVal = Builder2.CreateInsertValue(vec_imVal, imVal, std::vector<unsigned>({(unsigned)i}));
                    imVal = vec_imVal;
                  }
                  imVal;
                  });
                auto fwdnsrarg_1_1 = ({
                  // Computing SelectIfActive
                  llvm::Value *imVal = call.getOperand(0);
                  bool condition = !gutils->isConstantValue(imVal);
                  bool vectorized = false;
                  (void)vectorized;
                  if (condition) {
                    imVal = ({
                      // Computing FMul
                      auto fwdnsrarg_1_2_cic_0 = gutils->invertPointerM(gutils->extractMeta(Builder2,call.getOperand(0), ArrayRef<unsigned>({1})), Builder2, TR.query(&call));
                      llvm::Value* fwdnsrarg_1_2_cic_1 = gutils->extractMeta(Builder2, gutils->getNewFromOriginal(call.getOperand(0)), ArrayRef<unsigned>({1}));
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_1_2_cic_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_2_cic_0, idx), fwdnsrarg_1_2_cic_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    vectorized = true;
                  } else {
                    imVal = Constant::getNullValue(gutils->extractMeta(call.getOperand(0)->getType(), ArrayRef<unsigned>({1})));
                  }
                  if (!vectorized) {
                    llvm::Value* vec_imVal = gutils->getWidth() == 1 ? imVal : UndefValue::get(gutils->getShadowType(imVal->getType()));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        vec_imVal = Builder2.CreateInsertValue(vec_imVal, imVal, std::vector<unsigned>({(unsigned)i}));
                    imVal = vec_imVal;
                  }
                  imVal;
                  });
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdnsrarg_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_1, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              auto fwdnsrarg_1 = ({
                // Computing Call
                llvm::Value* fwdnsrarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto callval = cast<CallInst>(&call)->getCalledOperand();
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdnsrarg_2_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdnsrarg_0 : gutils->extractMeta(Builder2, fwdnsrarg_0, idx), fwdnsrarg_1);
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing subroutine Cabs_helper
              auto fwdarg_0 = gutils->getNewFromOriginal(call.getOperand(0));
              auto fwdarg_1 = dif;
              auto fwdarg_2 = ({
                // Computing Call
                llvm::Value* fwdarg_3_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto callval = cast<CallInst>(&call)->getCalledOperand();
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_3_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value* local_re = Builder2.CreateExtractValue(fwdarg_0, ArrayRef<unsigned>({0}), "re");
              Value* local_im = Builder2.CreateExtractValue(fwdarg_0, ArrayRef<unsigned>({1}), "im");
              ({
                auto fwdarg_sr_0 = ({
                  // Computing FMul
                  auto fwdarg_sr_1_0 = fwdarg_1;
                  auto fwdarg_sr_1_1 = ({
                    // Computing FDiv
                    auto fwdarg_sr_1_2_0 = local_re;
                    llvm::Value* fwdarg_sr_1_2_1 = fwdarg_2;
                    Builder2.CreateFDiv(fwdarg_sr_1_2_0, fwdarg_sr_1_2_1);
                  });
                  Value *res = nullptr;
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_sr_1_0, idx), fwdarg_sr_1_1);
                    if (gutils->getWidth() == 1) res = V;
                    else {
                      if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                      res = Builder2.CreateInsertValue(res, V, {idx});
                    }
                  }
                  res;
                });
                auto fwdarg_sr_1 = ({
                  // Computing FMul
                  auto fwdarg_sr_2_0 = fwdarg_1;
                  auto fwdarg_sr_2_1 = ({
                    // Computing FDiv
                    auto fwdarg_sr_2_2_0 = local_im;
                    llvm::Value* fwdarg_sr_2_2_1 = fwdarg_2;
                    Builder2.CreateFDiv(fwdarg_sr_2_2_0, fwdarg_sr_2_2_1);
                  });
                  Value *res = nullptr;
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_sr_2_0, idx), fwdarg_sr_2_1);
                    if (gutils->getWidth() == 1) res = V;
                    else {
                      if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                      res = Builder2.CreateInsertValue(res, V, {idx});
                    }
                  }
                  res;
                });
                Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_sr_0->getType() : getSubType(fwdarg_sr_0->getType(), -1), 2)));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(0);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_sr_0 : gutils->extractMeta(Builder2, fwdarg_sr_0, idx), idxs);
                  }
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(1);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_sr_1 : gutils->extractMeta(Builder2, fwdarg_sr_1, idx), idxs);
                  }
                }
                 res;
              });
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing subroutine Cabs_helper
              auto revarg_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
              auto revarg_1 = dif;
              auto revarg_2 = ({
                // Computing Call
                llvm::Value* revarg_3_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                auto callval = cast<CallInst>(&call)->getCalledOperand();
                auto FT = cast<CallInst>(&call)->getFunctionType();
                auto cconv = cast<CallInst>(&call)->getCallingConv();
                CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_3_0})));
                V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                V;
              });
              Value* local_re = Builder2.CreateExtractValue(revarg_0, ArrayRef<unsigned>({0}), "re");
              Value* local_im = Builder2.CreateExtractValue(revarg_0, ArrayRef<unsigned>({1}), "im");
              ({
                auto revarg_sr_0 = ({
                  // Computing FMul
                  auto revarg_sr_1_0 = revarg_1;
                  auto revarg_sr_1_1 = ({
                    // Computing FDiv
                    auto revarg_sr_1_2_0 = local_re;
                    llvm::Value* revarg_sr_1_2_1 = revarg_2;
                    Builder2.CreateFDiv(revarg_sr_1_2_0, revarg_sr_1_2_1);
                  });
                  Value *res = nullptr;
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_sr_1_0 : gutils->extractMeta(Builder2, revarg_sr_1_0, idx), revarg_sr_1_1);
                    if (gutils->getWidth() == 1) res = V;
                    else {
                      if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                      res = Builder2.CreateInsertValue(res, V, {idx});
                    }
                  }
                  res;
                });
                auto revarg_sr_1 = ({
                  // Computing FMul
                  auto revarg_sr_2_0 = revarg_1;
                  auto revarg_sr_2_1 = ({
                    // Computing FDiv
                    auto revarg_sr_2_2_0 = local_im;
                    llvm::Value* revarg_sr_2_2_1 = revarg_2;
                    Builder2.CreateFDiv(revarg_sr_2_2_0, revarg_sr_2_2_1);
                  });
                  Value *res = nullptr;
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_sr_2_0 : gutils->extractMeta(Builder2, revarg_sr_2_0, idx), revarg_sr_2_1);
                    if (gutils->getWidth() == 1) res = V;
                    else {
                      if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                      res = Builder2.CreateInsertValue(res, V, {idx});
                    }
                  }
                  res;
                });
                Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_sr_0->getType() : getSubType(revarg_sr_0->getType(), -1), 2)));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(0);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_sr_0 : gutils->extractMeta(Builder2, revarg_sr_0, idx), idxs);
                  }
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(1);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_sr_1 : gutils->extractMeta(Builder2, revarg_sr_1, idx), idxs);
                  }
                }
                 res;
              });
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "cmplx_inv") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
            Value *res = ({
              // Computing subroutine CFDiv
              auto fwdnsrarg_0 = ({
                // Computing subroutine CFNeg
                auto fwdnsrarg_1_0 = gutils->invertPointerM(call.getOperand(0), Builder2, TR.query(&call));
                Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdnsrarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                ({
                  auto fwdnsrarg_1_sr_0 = ({
                    // Computing FNeg
                    auto fwdnsrarg_1_sr_1_0 = local_re;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_1_0, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  auto fwdnsrarg_1_sr_1 = ({
                    // Computing FNeg
                    auto fwdnsrarg_1_sr_2_0 = local_im;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_2_0, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdnsrarg_1_sr_0->getType() : getSubType(fwdnsrarg_1_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_1_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_1_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
              auto fwdnsrarg_1 = ({
                // Computing subroutine CFMul
                auto fwdnsrarg_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                auto fwdnsrarg_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                Value* local_re1 = Builder2.CreateExtractValue(fwdnsrarg_2_0, ArrayRef<unsigned>({0}), "re1");
                Value* local_im1 = Builder2.CreateExtractValue(fwdnsrarg_2_0, ArrayRef<unsigned>({1}), "im1");
                Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_2_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_2_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFMul_splat
                  auto fwdnsrarg_2_sr_0 = local_re1;
                  auto fwdnsrarg_2_sr_1 = local_im1;
                  auto fwdnsrarg_2_sr_2 = local_re2;
                  auto fwdnsrarg_2_sr_3 = local_im2;
                  ({
                    auto fwdnsrarg_2_sr_sr_0 = ({
                      // Computing FSub
                      auto fwdnsrarg_2_sr_sr_1_0 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_2_sr_sr_1_1_0 = fwdnsrarg_2_sr_0;
                        llvm::Value* fwdnsrarg_2_sr_sr_1_1_1 = fwdnsrarg_2_sr_2;
                        Builder2.CreateFMul(fwdnsrarg_2_sr_sr_1_1_0, fwdnsrarg_2_sr_sr_1_1_1);
                      });
                      auto fwdnsrarg_2_sr_sr_1_1 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_2_sr_sr_1_2_0 = fwdnsrarg_2_sr_1;
                        llvm::Value* fwdnsrarg_2_sr_sr_1_2_1 = fwdnsrarg_2_sr_3;
                        Builder2.CreateFMul(fwdnsrarg_2_sr_sr_1_2_0, fwdnsrarg_2_sr_sr_1_2_1);
                      });
                      Builder2.CreateFSub(fwdnsrarg_2_sr_sr_1_0, fwdnsrarg_2_sr_sr_1_1);
                    });
                    auto fwdnsrarg_2_sr_sr_1 = ({
                      // Computing FAdd
                      auto fwdnsrarg_2_sr_sr_2_0 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_2_sr_sr_2_1_0 = fwdnsrarg_2_sr_0;
                        llvm::Value* fwdnsrarg_2_sr_sr_2_1_1 = fwdnsrarg_2_sr_3;
                        Builder2.CreateFMul(fwdnsrarg_2_sr_sr_2_1_0, fwdnsrarg_2_sr_sr_2_1_1);
                      });
                      auto fwdnsrarg_2_sr_sr_2_1 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_2_sr_sr_2_2_0 = fwdnsrarg_2_sr_2;
                        llvm::Value* fwdnsrarg_2_sr_sr_2_2_1 = fwdnsrarg_2_sr_1;
                        Builder2.CreateFMul(fwdnsrarg_2_sr_sr_2_2_0, fwdnsrarg_2_sr_sr_2_2_1);
                      });
                      Builder2.CreateFAdd(fwdnsrarg_2_sr_sr_2_0, fwdnsrarg_2_sr_sr_2_1);
                    });
                    Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_2_sr_sr_0->getType(), 2));
                    {
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, fwdnsrarg_2_sr_sr_0, idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, fwdnsrarg_2_sr_sr_1, idxs);
                      }
                    }
                     res;
                  });
                });
              });
              Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdnsrarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_1, ArrayRef<unsigned>({0}), "re2");
              Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_1, ArrayRef<unsigned>({1}), "im2");
              ({
                // Computing subroutine CFDiv_splat
                auto fwdnsrarg_sr_0 = local_re1;
                auto fwdnsrarg_sr_1 = local_im1;
                auto fwdnsrarg_sr_2 = local_re2;
                auto fwdnsrarg_sr_3 = local_im2;
                llvm::Value *local_denom = nullptr;
                ({
                  auto fwdnsrarg_sr_sr_0 = ({
                    // Computing FDiv
                    auto fwdnsrarg_sr_sr_1_0 = ({
                      // Computing FAdd
                      auto fwdnsrarg_sr_sr_1_1_0 = ({
                        // Computing FMul
                        auto fwdnsrarg_sr_sr_1_1_1_0 = fwdnsrarg_sr_0;
                        llvm::Value* fwdnsrarg_sr_sr_1_1_1_1 = fwdnsrarg_sr_2;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_1_1_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_1_1_1_0, idx), fwdnsrarg_sr_sr_1_1_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_sr_sr_1_1_1 = ({
                        // Computing FMul
                        auto fwdnsrarg_sr_sr_1_1_2_0 = fwdnsrarg_sr_1;
                        llvm::Value* fwdnsrarg_sr_sr_1_1_2_1 = fwdnsrarg_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_1_1_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_1_1_2_0, idx), fwdnsrarg_sr_sr_1_1_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_1_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_1_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_1_1_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdnsrarg_sr_sr_1_1 = ({
                      // Computing FAdd
                      auto fwdnsrarg_sr_sr_1_2_0 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_sr_sr_1_2_1_0 = fwdnsrarg_sr_2;
                        llvm::Value* fwdnsrarg_sr_sr_1_2_1_1 = fwdnsrarg_sr_2;
                        Builder2.CreateFMul(fwdnsrarg_sr_sr_1_2_1_0, fwdnsrarg_sr_sr_1_2_1_1);
                      });
                      auto fwdnsrarg_sr_sr_1_2_1 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_sr_sr_1_2_2_0 = fwdnsrarg_sr_3;
                        llvm::Value* fwdnsrarg_sr_sr_1_2_2_1 = fwdnsrarg_sr_3;
                        Builder2.CreateFMul(fwdnsrarg_sr_sr_1_2_2_0, fwdnsrarg_sr_sr_1_2_2_1);
                      });
                      Builder2.CreateFAdd(fwdnsrarg_sr_sr_1_2_0, fwdnsrarg_sr_sr_1_2_1);
                    });
                    local_denom = fwdnsrarg_sr_sr_1_1;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_1_0, idx), fwdnsrarg_sr_sr_1_1);
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  auto fwdnsrarg_sr_sr_1 = ({
                    // Computing FDiv
                    auto fwdnsrarg_sr_sr_2_0 = ({
                      // Computing FSub
                      auto fwdnsrarg_sr_sr_2_1_0 = ({
                        // Computing FMul
                        llvm::Value* fwdnsrarg_sr_sr_2_1_1_0 = fwdnsrarg_sr_2;
                        auto fwdnsrarg_sr_sr_2_1_1_1 = fwdnsrarg_sr_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul(fwdnsrarg_sr_sr_2_1_1_0, (gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_2_1_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_2_1_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_sr_sr_2_1_1 = ({
                        // Computing FMul
                        auto fwdnsrarg_sr_sr_2_1_2_0 = fwdnsrarg_sr_0;
                        llvm::Value* fwdnsrarg_sr_sr_2_1_2_1 = fwdnsrarg_sr_3;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_2_1_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_2_1_2_0, idx), fwdnsrarg_sr_sr_2_1_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_2_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_2_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_2_1_1, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdnsrarg_sr_sr_2_1 = local_denom;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_2_0, idx), fwdnsrarg_sr_sr_2_1);
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_0->getType() : getSubType(fwdnsrarg_sr_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_sr_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_sr_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
            });
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing subroutine Conj
              auto fwdarg_0 = ({
                // Computing subroutine CFDiv
                auto fwdarg_1_0 = ({
                  // Computing subroutine CFNeg
                  auto fwdarg_1_1_0 = ({
                    // Computing subroutine Conj
                    auto fwdarg_1_1_1_0 = dif;
                    Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_1_0->getType(), 0, 0)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_1_0->getType(), 0, 1)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    ({
                      auto fwdarg_1_1_1_sr_0 = local_re;
                      auto fwdarg_1_1_1_sr_1 = ({
                        // Computing FNeg
                        auto fwdarg_1_1_1_sr_2_0 = local_im;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_2_0, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_0->getType() : getSubType(fwdarg_1_1_1_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_1_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_1_1_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                  Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_0->getType(), 0, 0)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_1_0->getType(), 0, 1)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  ({
                    auto fwdarg_1_1_sr_0 = ({
                      // Computing FNeg
                      auto fwdarg_1_1_sr_1_0 = local_re;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_1_1_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_1_0, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdarg_1_1_sr_1 = ({
                      // Computing FNeg
                      auto fwdarg_1_1_sr_2_0 = local_im;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_1_1_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_2_0, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_1_sr_0->getType() : getSubType(fwdarg_1_1_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_1_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_1_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
                auto fwdarg_1_1 = ({
                  // Computing subroutine CFMul
                  auto fwdarg_1_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                  auto fwdarg_1_2_1 = gutils->getNewFromOriginal(call.getOperand(0));
                  Value* local_re1 = Builder2.CreateExtractValue(fwdarg_1_2_0, ArrayRef<unsigned>({0}), "re1");
                  Value* local_im1 = Builder2.CreateExtractValue(fwdarg_1_2_0, ArrayRef<unsigned>({1}), "im1");
                  Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_2_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_2_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto fwdarg_1_2_sr_0 = local_re1;
                    auto fwdarg_1_2_sr_1 = local_im1;
                    auto fwdarg_1_2_sr_2 = local_re2;
                    auto fwdarg_1_2_sr_3 = local_im2;
                    ({
                      auto fwdarg_1_2_sr_sr_0 = ({
                        // Computing FSub
                        auto fwdarg_1_2_sr_sr_1_0 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_2_sr_sr_1_1_0 = fwdarg_1_2_sr_0;
                          llvm::Value* fwdarg_1_2_sr_sr_1_1_1 = fwdarg_1_2_sr_2;
                          Builder2.CreateFMul(fwdarg_1_2_sr_sr_1_1_0, fwdarg_1_2_sr_sr_1_1_1);
                        });
                        auto fwdarg_1_2_sr_sr_1_1 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_2_sr_sr_1_2_0 = fwdarg_1_2_sr_1;
                          llvm::Value* fwdarg_1_2_sr_sr_1_2_1 = fwdarg_1_2_sr_3;
                          Builder2.CreateFMul(fwdarg_1_2_sr_sr_1_2_0, fwdarg_1_2_sr_sr_1_2_1);
                        });
                        Builder2.CreateFSub(fwdarg_1_2_sr_sr_1_0, fwdarg_1_2_sr_sr_1_1);
                      });
                      auto fwdarg_1_2_sr_sr_1 = ({
                        // Computing FAdd
                        auto fwdarg_1_2_sr_sr_2_0 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_2_sr_sr_2_1_0 = fwdarg_1_2_sr_0;
                          llvm::Value* fwdarg_1_2_sr_sr_2_1_1 = fwdarg_1_2_sr_3;
                          Builder2.CreateFMul(fwdarg_1_2_sr_sr_2_1_0, fwdarg_1_2_sr_sr_2_1_1);
                        });
                        auto fwdarg_1_2_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_2_sr_sr_2_2_0 = fwdarg_1_2_sr_2;
                          llvm::Value* fwdarg_1_2_sr_sr_2_2_1 = fwdarg_1_2_sr_1;
                          Builder2.CreateFMul(fwdarg_1_2_sr_sr_2_2_0, fwdarg_1_2_sr_sr_2_2_1);
                        });
                        Builder2.CreateFAdd(fwdarg_1_2_sr_sr_2_0, fwdarg_1_2_sr_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(fwdarg_1_2_sr_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, fwdarg_1_2_sr_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, fwdarg_1_2_sr_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(fwdarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(fwdarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFDiv_splat
                  auto fwdarg_1_sr_0 = local_re1;
                  auto fwdarg_1_sr_1 = local_im1;
                  auto fwdarg_1_sr_2 = local_re2;
                  auto fwdarg_1_sr_3 = local_im2;
                  llvm::Value *local_denom = nullptr;
                  ({
                    auto fwdarg_1_sr_sr_0 = ({
                      // Computing FDiv
                      auto fwdarg_1_sr_sr_1_0 = ({
                        // Computing FAdd
                        auto fwdarg_1_sr_sr_1_1_0 = ({
                          // Computing FMul
                          auto fwdarg_1_sr_sr_1_1_1_0 = fwdarg_1_sr_0;
                          llvm::Value* fwdarg_1_sr_sr_1_1_1_1 = fwdarg_1_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_1_0, idx), fwdarg_1_sr_sr_1_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_sr_sr_1_1_1 = ({
                          // Computing FMul
                          auto fwdarg_1_sr_sr_1_1_2_0 = fwdarg_1_sr_1;
                          llvm::Value* fwdarg_1_sr_sr_1_1_2_1 = fwdarg_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_2_0, idx), fwdarg_1_sr_sr_1_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_1_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_sr_sr_1_1 = ({
                        // Computing FAdd
                        auto fwdarg_1_sr_sr_1_2_0 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_sr_sr_1_2_1_0 = fwdarg_1_sr_2;
                          llvm::Value* fwdarg_1_sr_sr_1_2_1_1 = fwdarg_1_sr_2;
                          Builder2.CreateFMul(fwdarg_1_sr_sr_1_2_1_0, fwdarg_1_sr_sr_1_2_1_1);
                        });
                        auto fwdarg_1_sr_sr_1_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_sr_sr_1_2_2_0 = fwdarg_1_sr_3;
                          llvm::Value* fwdarg_1_sr_sr_1_2_2_1 = fwdarg_1_sr_3;
                          Builder2.CreateFMul(fwdarg_1_sr_sr_1_2_2_0, fwdarg_1_sr_sr_1_2_2_1);
                        });
                        Builder2.CreateFAdd(fwdarg_1_sr_sr_1_2_0, fwdarg_1_sr_sr_1_2_1);
                      });
                      local_denom = fwdarg_1_sr_sr_1_1;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1_0, idx), fwdarg_1_sr_sr_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdarg_1_sr_sr_1 = ({
                      // Computing FDiv
                      auto fwdarg_1_sr_sr_2_0 = ({
                        // Computing FSub
                        auto fwdarg_1_sr_sr_2_1_0 = ({
                          // Computing FMul
                          llvm::Value* fwdarg_1_sr_sr_2_1_1_0 = fwdarg_1_sr_2;
                          auto fwdarg_1_sr_sr_2_1_1_1 = fwdarg_1_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(fwdarg_1_sr_sr_2_1_1_0, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_1_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_1_sr_sr_2_1_1 = ({
                          // Computing FMul
                          auto fwdarg_1_sr_sr_2_1_2_0 = fwdarg_1_sr_0;
                          llvm::Value* fwdarg_1_sr_sr_2_1_2_1 = fwdarg_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_2_0, idx), fwdarg_1_sr_sr_2_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_1_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_1_sr_sr_2_1 = local_denom;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_2_0, idx), fwdarg_1_sr_sr_2_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_1_sr_sr_0->getType() : getSubType(fwdarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              ({
                auto fwdarg_sr_0 = local_re;
                auto fwdarg_sr_1 = ({
                  // Computing FNeg
                  auto fwdarg_sr_2_0 = local_im;
                  Value *res = nullptr;
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_sr_2_0, idx));
                    if (gutils->getWidth() == 1) res = V;
                    else {
                      if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                      res = Builder2.CreateInsertValue(res, V, {idx});
                    }
                  }
                  res;
                });
                Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_sr_0->getType() : getSubType(fwdarg_sr_0->getType(), -1), 2)));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(0);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_sr_0 : gutils->extractMeta(Builder2, fwdarg_sr_0, idx), idxs);
                  }
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(1);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_sr_1 : gutils->extractMeta(Builder2, fwdarg_sr_1, idx), idxs);
                  }
                }
                 res;
              });
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing subroutine Conj
              auto revarg_0 = ({
                // Computing subroutine CFDiv
                auto revarg_1_0 = ({
                  // Computing subroutine CFNeg
                  auto revarg_1_1_0 = ({
                    // Computing subroutine Conj
                    auto revarg_1_1_1_0 = dif;
                    Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_1_0->getType(), 0, 0)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_1_0->getType(), 0, 1)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, revarg_1_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    ({
                      auto revarg_1_1_1_sr_0 = local_re;
                      auto revarg_1_1_1_sr_1 = ({
                        // Computing FNeg
                        auto revarg_1_1_1_sr_2_0 = local_im;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_1_1_1_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_2_0, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_1_1_sr_0->getType() : getSubType(revarg_1_1_1_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_1_sr_0 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_1_sr_1 : gutils->extractMeta(Builder2, revarg_1_1_1_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                  Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_0->getType(), 0, 0)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_1_0->getType(), 0, 1)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, revarg_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  ({
                    auto revarg_1_1_sr_0 = ({
                      // Computing FNeg
                      auto revarg_1_1_sr_1_0 = local_re;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_1_1_sr_1_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_1_0, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto revarg_1_1_sr_1 = ({
                      // Computing FNeg
                      auto revarg_1_1_sr_2_0 = local_im;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_1_1_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_2_0, idx));
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_1_sr_0->getType() : getSubType(revarg_1_1_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_sr_0 : gutils->extractMeta(Builder2, revarg_1_1_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_1_sr_1 : gutils->extractMeta(Builder2, revarg_1_1_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
                auto revarg_1_1 = ({
                  // Computing subroutine CFMul
                  auto revarg_1_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  auto revarg_1_2_1 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                  Value* local_re1 = Builder2.CreateExtractValue(revarg_1_2_0, ArrayRef<unsigned>({0}), "re1");
                  Value* local_im1 = Builder2.CreateExtractValue(revarg_1_2_0, ArrayRef<unsigned>({1}), "im1");
                  Value* local_re2 = Builder2.CreateExtractValue(revarg_1_2_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(revarg_1_2_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto revarg_1_2_sr_0 = local_re1;
                    auto revarg_1_2_sr_1 = local_im1;
                    auto revarg_1_2_sr_2 = local_re2;
                    auto revarg_1_2_sr_3 = local_im2;
                    ({
                      auto revarg_1_2_sr_sr_0 = ({
                        // Computing FSub
                        auto revarg_1_2_sr_sr_1_0 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_2_sr_sr_1_1_0 = revarg_1_2_sr_0;
                          llvm::Value* revarg_1_2_sr_sr_1_1_1 = revarg_1_2_sr_2;
                          Builder2.CreateFMul(revarg_1_2_sr_sr_1_1_0, revarg_1_2_sr_sr_1_1_1);
                        });
                        auto revarg_1_2_sr_sr_1_1 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_2_sr_sr_1_2_0 = revarg_1_2_sr_1;
                          llvm::Value* revarg_1_2_sr_sr_1_2_1 = revarg_1_2_sr_3;
                          Builder2.CreateFMul(revarg_1_2_sr_sr_1_2_0, revarg_1_2_sr_sr_1_2_1);
                        });
                        Builder2.CreateFSub(revarg_1_2_sr_sr_1_0, revarg_1_2_sr_sr_1_1);
                      });
                      auto revarg_1_2_sr_sr_1 = ({
                        // Computing FAdd
                        auto revarg_1_2_sr_sr_2_0 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_2_sr_sr_2_1_0 = revarg_1_2_sr_0;
                          llvm::Value* revarg_1_2_sr_sr_2_1_1 = revarg_1_2_sr_3;
                          Builder2.CreateFMul(revarg_1_2_sr_sr_2_1_0, revarg_1_2_sr_sr_2_1_1);
                        });
                        auto revarg_1_2_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_2_sr_sr_2_2_0 = revarg_1_2_sr_2;
                          llvm::Value* revarg_1_2_sr_sr_2_2_1 = revarg_1_2_sr_1;
                          Builder2.CreateFMul(revarg_1_2_sr_sr_2_2_0, revarg_1_2_sr_sr_2_2_1);
                        });
                        Builder2.CreateFAdd(revarg_1_2_sr_sr_2_0, revarg_1_2_sr_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(revarg_1_2_sr_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, revarg_1_2_sr_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, revarg_1_2_sr_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_1_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(revarg_1_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(revarg_1_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFDiv_splat
                  auto revarg_1_sr_0 = local_re1;
                  auto revarg_1_sr_1 = local_im1;
                  auto revarg_1_sr_2 = local_re2;
                  auto revarg_1_sr_3 = local_im2;
                  llvm::Value *local_denom = nullptr;
                  ({
                    auto revarg_1_sr_sr_0 = ({
                      // Computing FDiv
                      auto revarg_1_sr_sr_1_0 = ({
                        // Computing FAdd
                        auto revarg_1_sr_sr_1_1_0 = ({
                          // Computing FMul
                          auto revarg_1_sr_sr_1_1_1_0 = revarg_1_sr_0;
                          llvm::Value* revarg_1_sr_sr_1_1_1_1 = revarg_1_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_1_0, idx), revarg_1_sr_sr_1_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_sr_sr_1_1_1 = ({
                          // Computing FMul
                          auto revarg_1_sr_sr_1_1_2_0 = revarg_1_sr_1;
                          llvm::Value* revarg_1_sr_sr_1_1_2_1 = revarg_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_2_0, idx), revarg_1_sr_sr_1_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_sr_sr_1_1_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_sr_sr_1_1 = ({
                        // Computing FAdd
                        auto revarg_1_sr_sr_1_2_0 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_sr_sr_1_2_1_0 = revarg_1_sr_2;
                          llvm::Value* revarg_1_sr_sr_1_2_1_1 = revarg_1_sr_2;
                          Builder2.CreateFMul(revarg_1_sr_sr_1_2_1_0, revarg_1_sr_sr_1_2_1_1);
                        });
                        auto revarg_1_sr_sr_1_2_1 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_sr_sr_1_2_2_0 = revarg_1_sr_3;
                          llvm::Value* revarg_1_sr_sr_1_2_2_1 = revarg_1_sr_3;
                          Builder2.CreateFMul(revarg_1_sr_sr_1_2_2_0, revarg_1_sr_sr_1_2_2_1);
                        });
                        Builder2.CreateFAdd(revarg_1_sr_sr_1_2_0, revarg_1_sr_sr_1_2_1);
                      });
                      local_denom = revarg_1_sr_sr_1_1;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_1_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1_0, idx), revarg_1_sr_sr_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto revarg_1_sr_sr_1 = ({
                      // Computing FDiv
                      auto revarg_1_sr_sr_2_0 = ({
                        // Computing FSub
                        auto revarg_1_sr_sr_2_1_0 = ({
                          // Computing FMul
                          llvm::Value* revarg_1_sr_sr_2_1_1_0 = revarg_1_sr_2;
                          auto revarg_1_sr_sr_2_1_1_1 = revarg_1_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(revarg_1_sr_sr_2_1_1_0, (gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_1_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_1_sr_sr_2_1_1 = ({
                          // Computing FMul
                          auto revarg_1_sr_sr_2_1_2_0 = revarg_1_sr_0;
                          llvm::Value* revarg_1_sr_sr_2_1_2_1 = revarg_1_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_2_0, idx), revarg_1_sr_sr_2_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_0, idx), (gutils->getWidth() == 1) ? revarg_1_sr_sr_2_1_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_1_sr_sr_2_1 = local_denom;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_1_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_2_0, idx), revarg_1_sr_sr_2_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_1_sr_sr_0->getType() : getSubType(revarg_1_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_sr_0 : gutils->extractMeta(Builder2, revarg_1_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_1_sr_sr_1 : gutils->extractMeta(Builder2, revarg_1_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(revarg_0->getType(), 0, 0)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(revarg_0->getType(), 0, 1)));
              if (gutils->getWidth() != 1)
                for (size_t i=0; i<gutils->getWidth(); i++)
                  local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, revarg_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
              ({
                auto revarg_sr_0 = local_re;
                auto revarg_sr_1 = ({
                  // Computing FNeg
                  auto revarg_sr_2_0 = local_im;
                  Value *res = nullptr;
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_sr_2_0 : gutils->extractMeta(Builder2, revarg_sr_2_0, idx));
                    if (gutils->getWidth() == 1) res = V;
                    else {
                      if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                      res = Builder2.CreateInsertValue(res, V, {idx});
                    }
                  }
                  res;
                });
                Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_sr_0->getType() : getSubType(revarg_sr_0->getType(), -1), 2)));
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(0);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_sr_0 : gutils->extractMeta(Builder2, revarg_sr_0, idx), idxs);
                  }
                  {
                    std::vector<unsigned> idxs;
                    if (gutils->getWidth() != 1) idxs.push_back(idx);
                    idxs.push_back(1);
                    res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_sr_1 : gutils->extractMeta(Builder2, revarg_sr_1, idx), idxs);
                  }
                }
                 res;
              });
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "fdim" ||
      funcName == "fdimf" ||
      funcName == "fdiml") && call.arg_size() == 2 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing Select
              auto fwdarg_0 = ({
                // Computing FCmpOLT
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                llvm::Value* fwdarg_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                Builder2.CreateFCmpOLT(fwdarg_1_0, fwdarg_1_1);
              });
              auto fwdarg_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
              auto fwdarg_2 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(fwdarg_0, fwdarg_1, (gutils->getWidth() == 1) ? fwdarg_2 : gutils->extractMeta(Builder2, fwdarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing Select
              auto fwdarg_0 = ({
                // Computing FCmpOLT
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                llvm::Value* fwdarg_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                Builder2.CreateFCmpOLT(fwdarg_1_0, fwdarg_1_1);
              });
              auto fwdarg_1 = ConstantFP::get(call.getOperand(1)->getType(), "0");
              auto fwdarg_2 = ({
                // Computing FNeg
                auto fwdarg_3_0 = dif;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_3_0 : gutils->extractMeta(Builder2, fwdarg_3_0, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(fwdarg_0, fwdarg_1, (gutils->getWidth() == 1) ? fwdarg_2 : gutils->extractMeta(Builder2, fwdarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing Select
              auto fwdarg_0 = ({
                // Computing FCmpOLT
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                llvm::Value* fwdarg_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                Builder2.CreateFCmpOLT(fwdarg_1_0, fwdarg_1_1);
              });
              auto fwdarg_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
              auto fwdarg_2 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(fwdarg_0, fwdarg_1, (gutils->getWidth() == 1) ? fwdarg_2 : gutils->extractMeta(Builder2, fwdarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        if (!gutils->isConstantValue(call.getOperand(1))) {
          Value *dif = diffe(call.getOperand(1), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(1))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing Select
              auto fwdarg_0 = ({
                // Computing FCmpOLT
                llvm::Value* fwdarg_1_0 = gutils->getNewFromOriginal(call.getOperand(0));
                llvm::Value* fwdarg_1_1 = gutils->getNewFromOriginal(call.getOperand(1));
                Builder2.CreateFCmpOLT(fwdarg_1_0, fwdarg_1_1);
              });
              auto fwdarg_1 = ConstantFP::get(call.getOperand(1)->getType(), "0");
              auto fwdarg_2 = ({
                // Computing FNeg
                auto fwdarg_3_0 = dif;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_3_0 : gutils->extractMeta(Builder2, fwdarg_3_0, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(fwdarg_0, fwdarg_1, (gutils->getWidth() == 1) ? fwdarg_2 : gutils->extractMeta(Builder2, fwdarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        } else if (!dif && !gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing Select
              auto revarg_0 = ({
                // Computing FCmpOLT
                llvm::Value* revarg_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                llvm::Value* revarg_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                Builder2.CreateFCmpOLT(revarg_1_0, revarg_1_1);
              });
              auto revarg_1 = ConstantFP::get(call.getOperand(0)->getType(), "0");
              auto revarg_2 = dif;
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(revarg_0, revarg_1, (gutils->getWidth() == 1) ? revarg_2 : gutils->extractMeta(Builder2, revarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        if (!gutils->isConstantValue(call.getOperand(1)) && !isa<PointerType>(call.getOperand(1)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing Select
              auto revarg_0 = ({
                // Computing FCmpOLT
                llvm::Value* revarg_1_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                llvm::Value* revarg_1_1 = lookup(gutils->getNewFromOriginal(call.getOperand(1)), Builder2);
                Builder2.CreateFCmpOLT(revarg_1_0, revarg_1_1);
              });
              auto revarg_1 = ConstantFP::get(call.getOperand(1)->getType(), "0");
              auto revarg_2 = ({
                // Computing FNeg
                auto revarg_3_0 = dif;
                Value *res = nullptr;
                for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                  Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_3_0 : gutils->extractMeta(Builder2, revarg_3_0, idx));
                  if (gutils->getWidth() == 1) res = V;
                  else {
                    if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                    res = Builder2.CreateInsertValue(res, V, {idx});
                  }
                }
                res;
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(revarg_0, revarg_1, (gutils->getWidth() == 1) ? revarg_2 : gutils->extractMeta(Builder2, revarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(1)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(1), toadd, Builder2, call.getOperand(1)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
  if ((funcName == "cmplx_sqrt") && call.arg_size() == 1 ){
    if (gutils->knownRecomputeHeuristic.find(&call) !=
        gutils->knownRecomputeHeuristic.end()) {
        if (!gutils->knownRecomputeHeuristic[&call]) {
          gutils->cacheForReverse(BuilderZ, newCall,
                                  getIndex(&call, CacheType::Self, BuilderZ));
        }
    }
    eraseIfUnused(call);
    if (gutils->isConstantInstruction(&call) && gutils->isConstantValue(&call))
      return true;
    switch (Mode) {
      case DerivativeMode::ForwardModeSplit:
      case DerivativeMode::ForwardMode:{
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
            Value *res = ({
              // Computing Select
              auto fwdnsrarg_0 = ({
                // Computing And
                auto fwdnsrarg_1_0 = ({
                  // Computing FCmpUEQ
                  llvm::Value* fwdnsrarg_1_1_0 = gutils->extractMeta(Builder2, gutils->getNewFromOriginal(call.getOperand(0)), ArrayRef<unsigned>({0}));
                  auto fwdnsrarg_1_1_1 = ConstantFP::get(gutils->extractMeta(call.getOperand(0)->getType(), ArrayRef<unsigned>({0})), "0");
                  Builder2.CreateFCmpUEQ(fwdnsrarg_1_1_0, fwdnsrarg_1_1_1);
                });
                auto fwdnsrarg_1_1 = ({
                  // Computing FCmpUEQ
                  llvm::Value* fwdnsrarg_1_2_0 = gutils->extractMeta(Builder2, gutils->getNewFromOriginal(call.getOperand(0)), ArrayRef<unsigned>({1}));
                  auto fwdnsrarg_1_2_1 = ConstantFP::get(gutils->extractMeta(call.getOperand(0)->getType(), ArrayRef<unsigned>({1})), "0");
                  Builder2.CreateFCmpUEQ(fwdnsrarg_1_2_0, fwdnsrarg_1_2_1);
                });
                Builder2.CreateAnd(fwdnsrarg_1_0, fwdnsrarg_1_1);
              });
              auto fwdnsrarg_1 = ({
                auto ty = call.getOperand(0)->getType();
                Value *ret = nullptr;
                if (auto ST = dyn_cast<StructType>(ty)) {
                  ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "0"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                  ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "0"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                } else {
                  llvm::errs() << *ty << "\n";
                  assert(0 && "unhandled cfp");
                }
                ret;
              })
;
              auto fwdnsrarg_2 = ({
                // Computing subroutine CFDiv
                auto fwdnsrarg_3_0 = gutils->invertPointerM(call.getOperand(0), Builder2, TR.query(&call));
                auto fwdnsrarg_3_1 = ({
                  // Computing subroutine CFMul
                  auto fwdnsrarg_3_2_0 = ({
                    auto ty = call.getOperand(0)->getType();
                    Value *ret = nullptr;
                    if (auto ST = dyn_cast<StructType>(ty)) {
                      ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "2"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                      ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "2"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                    } else {
                      llvm::errs() << *ty << "\n";
                      assert(0 && "unhandled cfp");
                    }
                    ret;
                  })
;
                  auto fwdnsrarg_3_2_1 = ({
                    // Computing Call
                    llvm::Value* fwdnsrarg_3_2_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                    auto callval = cast<CallInst>(&call)->getCalledOperand();
                    auto FT = cast<CallInst>(&call)->getFunctionType();
                    auto cconv = cast<CallInst>(&call)->getCallingConv();
                    CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdnsrarg_3_2_2_0})));
                    V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                    V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                    V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                    V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                    V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                    V;
                  });
                  Value* local_re1 = Builder2.CreateExtractValue(fwdnsrarg_3_2_0, ArrayRef<unsigned>({0}), "re1");
                  Value* local_im1 = Builder2.CreateExtractValue(fwdnsrarg_3_2_0, ArrayRef<unsigned>({1}), "im1");
                  Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_3_2_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_3_2_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFMul_splat
                    auto fwdnsrarg_3_2_sr_0 = local_re1;
                    auto fwdnsrarg_3_2_sr_1 = local_im1;
                    auto fwdnsrarg_3_2_sr_2 = local_re2;
                    auto fwdnsrarg_3_2_sr_3 = local_im2;
                    ({
                      auto fwdnsrarg_3_2_sr_sr_0 = ({
                        // Computing FSub
                        auto fwdnsrarg_3_2_sr_sr_1_0 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_3_2_sr_sr_1_1_0 = fwdnsrarg_3_2_sr_0;
                          llvm::Value* fwdnsrarg_3_2_sr_sr_1_1_1 = fwdnsrarg_3_2_sr_2;
                          Builder2.CreateFMul(fwdnsrarg_3_2_sr_sr_1_1_0, fwdnsrarg_3_2_sr_sr_1_1_1);
                        });
                        auto fwdnsrarg_3_2_sr_sr_1_1 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_3_2_sr_sr_1_2_0 = fwdnsrarg_3_2_sr_1;
                          llvm::Value* fwdnsrarg_3_2_sr_sr_1_2_1 = fwdnsrarg_3_2_sr_3;
                          Builder2.CreateFMul(fwdnsrarg_3_2_sr_sr_1_2_0, fwdnsrarg_3_2_sr_sr_1_2_1);
                        });
                        Builder2.CreateFSub(fwdnsrarg_3_2_sr_sr_1_0, fwdnsrarg_3_2_sr_sr_1_1);
                      });
                      auto fwdnsrarg_3_2_sr_sr_1 = ({
                        // Computing FAdd
                        auto fwdnsrarg_3_2_sr_sr_2_0 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_3_2_sr_sr_2_1_0 = fwdnsrarg_3_2_sr_0;
                          llvm::Value* fwdnsrarg_3_2_sr_sr_2_1_1 = fwdnsrarg_3_2_sr_3;
                          Builder2.CreateFMul(fwdnsrarg_3_2_sr_sr_2_1_0, fwdnsrarg_3_2_sr_sr_2_1_1);
                        });
                        auto fwdnsrarg_3_2_sr_sr_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_3_2_sr_sr_2_2_0 = fwdnsrarg_3_2_sr_2;
                          llvm::Value* fwdnsrarg_3_2_sr_sr_2_2_1 = fwdnsrarg_3_2_sr_1;
                          Builder2.CreateFMul(fwdnsrarg_3_2_sr_sr_2_2_0, fwdnsrarg_3_2_sr_sr_2_2_1);
                        });
                        Builder2.CreateFAdd(fwdnsrarg_3_2_sr_sr_2_0, fwdnsrarg_3_2_sr_sr_2_1);
                      });
                      Value *res = UndefValue::get(ArrayType::get(fwdnsrarg_3_2_sr_sr_0->getType(), 2));
                      {
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, fwdnsrarg_3_2_sr_sr_0, idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, fwdnsrarg_3_2_sr_sr_1, idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_3_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_3_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdnsrarg_3_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdnsrarg_3_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdnsrarg_3_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdnsrarg_3_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_re2 = Builder2.CreateExtractValue(fwdnsrarg_3_1, ArrayRef<unsigned>({0}), "re2");
                Value* local_im2 = Builder2.CreateExtractValue(fwdnsrarg_3_1, ArrayRef<unsigned>({1}), "im2");
                ({
                  // Computing subroutine CFDiv_splat
                  auto fwdnsrarg_3_sr_0 = local_re1;
                  auto fwdnsrarg_3_sr_1 = local_im1;
                  auto fwdnsrarg_3_sr_2 = local_re2;
                  auto fwdnsrarg_3_sr_3 = local_im2;
                  llvm::Value *local_denom = nullptr;
                  ({
                    auto fwdnsrarg_3_sr_sr_0 = ({
                      // Computing FDiv
                      auto fwdnsrarg_3_sr_sr_1_0 = ({
                        // Computing FAdd
                        auto fwdnsrarg_3_sr_sr_1_1_0 = ({
                          // Computing FMul
                          auto fwdnsrarg_3_sr_sr_1_1_1_0 = fwdnsrarg_3_sr_0;
                          llvm::Value* fwdnsrarg_3_sr_sr_1_1_1_1 = fwdnsrarg_3_sr_2;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_3_sr_sr_1_1_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_3_sr_sr_1_1_1_0, idx), fwdnsrarg_3_sr_sr_1_1_1_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdnsrarg_3_sr_sr_1_1_1 = ({
                          // Computing FMul
                          auto fwdnsrarg_3_sr_sr_1_1_2_0 = fwdnsrarg_3_sr_1;
                          llvm::Value* fwdnsrarg_3_sr_sr_1_1_2_1 = fwdnsrarg_3_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_3_sr_sr_1_1_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_3_sr_sr_1_1_2_0, idx), fwdnsrarg_3_sr_sr_1_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdnsrarg_3_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_3_sr_sr_1_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_3_sr_sr_1_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_3_sr_sr_1_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_3_sr_sr_1_1 = ({
                        // Computing FAdd
                        auto fwdnsrarg_3_sr_sr_1_2_0 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_3_sr_sr_1_2_1_0 = fwdnsrarg_3_sr_2;
                          llvm::Value* fwdnsrarg_3_sr_sr_1_2_1_1 = fwdnsrarg_3_sr_2;
                          Builder2.CreateFMul(fwdnsrarg_3_sr_sr_1_2_1_0, fwdnsrarg_3_sr_sr_1_2_1_1);
                        });
                        auto fwdnsrarg_3_sr_sr_1_2_1 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_3_sr_sr_1_2_2_0 = fwdnsrarg_3_sr_3;
                          llvm::Value* fwdnsrarg_3_sr_sr_1_2_2_1 = fwdnsrarg_3_sr_3;
                          Builder2.CreateFMul(fwdnsrarg_3_sr_sr_1_2_2_0, fwdnsrarg_3_sr_sr_1_2_2_1);
                        });
                        Builder2.CreateFAdd(fwdnsrarg_3_sr_sr_1_2_0, fwdnsrarg_3_sr_sr_1_2_1);
                      });
                      local_denom = fwdnsrarg_3_sr_sr_1_1;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdnsrarg_3_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_3_sr_sr_1_0, idx), fwdnsrarg_3_sr_sr_1_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    auto fwdnsrarg_3_sr_sr_1 = ({
                      // Computing FDiv
                      auto fwdnsrarg_3_sr_sr_2_0 = ({
                        // Computing FSub
                        auto fwdnsrarg_3_sr_sr_2_1_0 = ({
                          // Computing FMul
                          llvm::Value* fwdnsrarg_3_sr_sr_2_1_1_0 = fwdnsrarg_3_sr_2;
                          auto fwdnsrarg_3_sr_sr_2_1_1_1 = fwdnsrarg_3_sr_1;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul(fwdnsrarg_3_sr_sr_2_1_1_0, (gutils->getWidth() == 1) ? fwdnsrarg_3_sr_sr_2_1_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_3_sr_sr_2_1_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdnsrarg_3_sr_sr_2_1_1 = ({
                          // Computing FMul
                          auto fwdnsrarg_3_sr_sr_2_1_2_0 = fwdnsrarg_3_sr_0;
                          llvm::Value* fwdnsrarg_3_sr_sr_2_1_2_1 = fwdnsrarg_3_sr_3;
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdnsrarg_3_sr_sr_2_1_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_3_sr_sr_2_1_2_0, idx), fwdnsrarg_3_sr_sr_2_1_2_1);
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdnsrarg_3_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdnsrarg_3_sr_sr_2_1_0, idx), (gutils->getWidth() == 1) ? fwdnsrarg_3_sr_sr_2_1_1 : gutils->extractMeta(Builder2, fwdnsrarg_3_sr_sr_2_1_1, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdnsrarg_3_sr_sr_2_1 = local_denom;
                      Value *res = nullptr;
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdnsrarg_3_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdnsrarg_3_sr_sr_2_0, idx), fwdnsrarg_3_sr_sr_2_1);
                        if (gutils->getWidth() == 1) res = V;
                        else {
                          if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                          res = Builder2.CreateInsertValue(res, V, {idx});
                        }
                      }
                      res;
                    });
                    Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdnsrarg_3_sr_sr_0->getType() : getSubType(fwdnsrarg_3_sr_sr_0->getType(), -1), 2)));
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(0);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_3_sr_sr_0 : gutils->extractMeta(Builder2, fwdnsrarg_3_sr_sr_0, idx), idxs);
                      }
                      {
                        std::vector<unsigned> idxs;
                        if (gutils->getWidth() != 1) idxs.push_back(idx);
                        idxs.push_back(1);
                        res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdnsrarg_3_sr_sr_1 : gutils->extractMeta(Builder2, fwdnsrarg_3_sr_sr_1, idx), idxs);
                      }
                    }
                     res;
                  });
                });
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(fwdnsrarg_0, fwdnsrarg_1, (gutils->getWidth() == 1) ? fwdnsrarg_2 : gutils->extractMeta(Builder2, fwdnsrarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
        assert(res);
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ForwardModeError: {
        IRBuilder<> Builder2(&call);
        getForwardBuilder(Builder2);
        Value *res = Constant::getNullValue(gutils->getShadowType(call.getType()));
        if (!gutils->isConstantValue(call.getOperand(0))) {
          Value *dif = diffe(call.getOperand(0), Builder2);
          dif = Builder2.CreateFDiv(Builder2.CreateFMul(dif, gutils->getNewFromOriginal(call.getOperand(0))), gutils->getNewFromOriginal(&call));
          Value *arg_diff_tmp = UndefValue::get(res->getType());
          {
            Value *itmp = ({
              // Computing Select
              auto fwdarg_0 = ({
                // Computing And
                auto fwdarg_1_0 = ({
                  // Computing FCmpUEQ
                  llvm::Value* fwdarg_1_1_0 = gutils->extractMeta(Builder2, gutils->getNewFromOriginal(call.getOperand(0)), ArrayRef<unsigned>({0}));
                  auto fwdarg_1_1_1 = ConstantFP::get(gutils->extractMeta(call.getOperand(0)->getType(), ArrayRef<unsigned>({0})), "0");
                  Builder2.CreateFCmpUEQ(fwdarg_1_1_0, fwdarg_1_1_1);
                });
                auto fwdarg_1_1 = ({
                  // Computing FCmpUEQ
                  llvm::Value* fwdarg_1_2_0 = gutils->extractMeta(Builder2, gutils->getNewFromOriginal(call.getOperand(0)), ArrayRef<unsigned>({1}));
                  auto fwdarg_1_2_1 = ConstantFP::get(gutils->extractMeta(call.getOperand(0)->getType(), ArrayRef<unsigned>({1})), "0");
                  Builder2.CreateFCmpUEQ(fwdarg_1_2_0, fwdarg_1_2_1);
                });
                Builder2.CreateAnd(fwdarg_1_0, fwdarg_1_1);
              });
              auto fwdarg_1 = ({
                auto ty = call.getOperand(0)->getType();
                Value *ret = nullptr;
                if (auto ST = dyn_cast<StructType>(ty)) {
                  ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "0"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                  ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "0"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                } else {
                  llvm::errs() << *ty << "\n";
                  assert(0 && "unhandled cfp");
                }
                ret;
              })
;
              auto fwdarg_2 = ({
                // Computing subroutine Conj
                auto fwdarg_3_0 = ({
                  // Computing subroutine CFDiv
                  auto fwdarg_3_1_0 = ({
                    // Computing subroutine Conj
                    auto fwdarg_3_1_1_0 = dif;
                    Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_3_1_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_3_1_1_0->getType(), 0, 0)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdarg_3_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_3_1_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_3_1_1_0->getType(), 0, 1)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdarg_3_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    ({
                      auto fwdarg_3_1_1_sr_0 = local_re;
                      auto fwdarg_3_1_1_sr_1 = ({
                        // Computing FNeg
                        auto fwdarg_3_1_1_sr_2_0 = local_im;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_3_1_1_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_3_1_1_sr_2_0, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_3_1_1_sr_0->getType() : getSubType(fwdarg_3_1_1_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_3_1_1_sr_0 : gutils->extractMeta(Builder2, fwdarg_3_1_1_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_3_1_1_sr_1 : gutils->extractMeta(Builder2, fwdarg_3_1_1_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                  auto fwdarg_3_1_1 = ({
                    // Computing subroutine CFMul
                    auto fwdarg_3_1_2_0 = ({
                      auto ty = call.getOperand(0)->getType();
                      Value *ret = nullptr;
                      if (auto ST = dyn_cast<StructType>(ty)) {
                        ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "2"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                        ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "2"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                      } else {
                        llvm::errs() << *ty << "\n";
                        assert(0 && "unhandled cfp");
                      }
                      ret;
                    })
;
                    auto fwdarg_3_1_2_1 = ({
                      // Computing Call
                      llvm::Value* fwdarg_3_1_2_2_0 = gutils->getNewFromOriginal(call.getOperand(0));
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({fwdarg_3_1_2_2_0})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    Value* local_re1 = Builder2.CreateExtractValue(fwdarg_3_1_2_0, ArrayRef<unsigned>({0}), "re1");
                    Value* local_im1 = Builder2.CreateExtractValue(fwdarg_3_1_2_0, ArrayRef<unsigned>({1}), "im1");
                    Value* local_re2 = Builder2.CreateExtractValue(fwdarg_3_1_2_1, ArrayRef<unsigned>({0}), "re2");
                    Value* local_im2 = Builder2.CreateExtractValue(fwdarg_3_1_2_1, ArrayRef<unsigned>({1}), "im2");
                    ({
                      // Computing subroutine CFMul_splat
                      auto fwdarg_3_1_2_sr_0 = local_re1;
                      auto fwdarg_3_1_2_sr_1 = local_im1;
                      auto fwdarg_3_1_2_sr_2 = local_re2;
                      auto fwdarg_3_1_2_sr_3 = local_im2;
                      ({
                        auto fwdarg_3_1_2_sr_sr_0 = ({
                          // Computing FSub
                          auto fwdarg_3_1_2_sr_sr_1_0 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_3_1_2_sr_sr_1_1_0 = fwdarg_3_1_2_sr_0;
                            llvm::Value* fwdarg_3_1_2_sr_sr_1_1_1 = fwdarg_3_1_2_sr_2;
                            Builder2.CreateFMul(fwdarg_3_1_2_sr_sr_1_1_0, fwdarg_3_1_2_sr_sr_1_1_1);
                          });
                          auto fwdarg_3_1_2_sr_sr_1_1 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_3_1_2_sr_sr_1_2_0 = fwdarg_3_1_2_sr_1;
                            llvm::Value* fwdarg_3_1_2_sr_sr_1_2_1 = fwdarg_3_1_2_sr_3;
                            Builder2.CreateFMul(fwdarg_3_1_2_sr_sr_1_2_0, fwdarg_3_1_2_sr_sr_1_2_1);
                          });
                          Builder2.CreateFSub(fwdarg_3_1_2_sr_sr_1_0, fwdarg_3_1_2_sr_sr_1_1);
                        });
                        auto fwdarg_3_1_2_sr_sr_1 = ({
                          // Computing FAdd
                          auto fwdarg_3_1_2_sr_sr_2_0 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_3_1_2_sr_sr_2_1_0 = fwdarg_3_1_2_sr_0;
                            llvm::Value* fwdarg_3_1_2_sr_sr_2_1_1 = fwdarg_3_1_2_sr_3;
                            Builder2.CreateFMul(fwdarg_3_1_2_sr_sr_2_1_0, fwdarg_3_1_2_sr_sr_2_1_1);
                          });
                          auto fwdarg_3_1_2_sr_sr_2_1 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_3_1_2_sr_sr_2_2_0 = fwdarg_3_1_2_sr_2;
                            llvm::Value* fwdarg_3_1_2_sr_sr_2_2_1 = fwdarg_3_1_2_sr_1;
                            Builder2.CreateFMul(fwdarg_3_1_2_sr_sr_2_2_0, fwdarg_3_1_2_sr_sr_2_2_1);
                          });
                          Builder2.CreateFAdd(fwdarg_3_1_2_sr_sr_2_0, fwdarg_3_1_2_sr_sr_2_1);
                        });
                        Value *res = UndefValue::get(ArrayType::get(fwdarg_3_1_2_sr_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, fwdarg_3_1_2_sr_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, fwdarg_3_1_2_sr_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                  });
                  Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_3_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_3_1_0->getType(), 0, 0)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, fwdarg_3_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_3_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_3_1_0->getType(), 0, 1)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, fwdarg_3_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_re2 = Builder2.CreateExtractValue(fwdarg_3_1_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(fwdarg_3_1_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFDiv_splat
                    auto fwdarg_3_1_sr_0 = local_re1;
                    auto fwdarg_3_1_sr_1 = local_im1;
                    auto fwdarg_3_1_sr_2 = local_re2;
                    auto fwdarg_3_1_sr_3 = local_im2;
                    llvm::Value *local_denom = nullptr;
                    ({
                      auto fwdarg_3_1_sr_sr_0 = ({
                        // Computing FDiv
                        auto fwdarg_3_1_sr_sr_1_0 = ({
                          // Computing FAdd
                          auto fwdarg_3_1_sr_sr_1_1_0 = ({
                            // Computing FMul
                            auto fwdarg_3_1_sr_sr_1_1_1_0 = fwdarg_3_1_sr_0;
                            llvm::Value* fwdarg_3_1_sr_sr_1_1_1_1 = fwdarg_3_1_sr_2;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_3_1_sr_sr_1_1_1_0 : gutils->extractMeta(Builder2, fwdarg_3_1_sr_sr_1_1_1_0, idx), fwdarg_3_1_sr_sr_1_1_1_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          auto fwdarg_3_1_sr_sr_1_1_1 = ({
                            // Computing FMul
                            auto fwdarg_3_1_sr_sr_1_1_2_0 = fwdarg_3_1_sr_1;
                            llvm::Value* fwdarg_3_1_sr_sr_1_1_2_1 = fwdarg_3_1_sr_3;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_3_1_sr_sr_1_1_2_0 : gutils->extractMeta(Builder2, fwdarg_3_1_sr_sr_1_1_2_0, idx), fwdarg_3_1_sr_sr_1_1_2_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? fwdarg_3_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, fwdarg_3_1_sr_sr_1_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_3_1_sr_sr_1_1_1 : gutils->extractMeta(Builder2, fwdarg_3_1_sr_sr_1_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_3_1_sr_sr_1_1 = ({
                          // Computing FAdd
                          auto fwdarg_3_1_sr_sr_1_2_0 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_3_1_sr_sr_1_2_1_0 = fwdarg_3_1_sr_2;
                            llvm::Value* fwdarg_3_1_sr_sr_1_2_1_1 = fwdarg_3_1_sr_2;
                            Builder2.CreateFMul(fwdarg_3_1_sr_sr_1_2_1_0, fwdarg_3_1_sr_sr_1_2_1_1);
                          });
                          auto fwdarg_3_1_sr_sr_1_2_1 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_3_1_sr_sr_1_2_2_0 = fwdarg_3_1_sr_3;
                            llvm::Value* fwdarg_3_1_sr_sr_1_2_2_1 = fwdarg_3_1_sr_3;
                            Builder2.CreateFMul(fwdarg_3_1_sr_sr_1_2_2_0, fwdarg_3_1_sr_sr_1_2_2_1);
                          });
                          Builder2.CreateFAdd(fwdarg_3_1_sr_sr_1_2_0, fwdarg_3_1_sr_sr_1_2_1);
                        });
                        local_denom = fwdarg_3_1_sr_sr_1_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_3_1_sr_sr_1_0 : gutils->extractMeta(Builder2, fwdarg_3_1_sr_sr_1_0, idx), fwdarg_3_1_sr_sr_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto fwdarg_3_1_sr_sr_1 = ({
                        // Computing FDiv
                        auto fwdarg_3_1_sr_sr_2_0 = ({
                          // Computing FSub
                          auto fwdarg_3_1_sr_sr_2_1_0 = ({
                            // Computing FMul
                            llvm::Value* fwdarg_3_1_sr_sr_2_1_1_0 = fwdarg_3_1_sr_2;
                            auto fwdarg_3_1_sr_sr_2_1_1_1 = fwdarg_3_1_sr_1;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul(fwdarg_3_1_sr_sr_2_1_1_0, (gutils->getWidth() == 1) ? fwdarg_3_1_sr_sr_2_1_1_1 : gutils->extractMeta(Builder2, fwdarg_3_1_sr_sr_2_1_1_1, idx));
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          auto fwdarg_3_1_sr_sr_2_1_1 = ({
                            // Computing FMul
                            auto fwdarg_3_1_sr_sr_2_1_2_0 = fwdarg_3_1_sr_0;
                            llvm::Value* fwdarg_3_1_sr_sr_2_1_2_1 = fwdarg_3_1_sr_3;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? fwdarg_3_1_sr_sr_2_1_2_0 : gutils->extractMeta(Builder2, fwdarg_3_1_sr_sr_2_1_2_0, idx), fwdarg_3_1_sr_sr_2_1_2_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? fwdarg_3_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, fwdarg_3_1_sr_sr_2_1_0, idx), (gutils->getWidth() == 1) ? fwdarg_3_1_sr_sr_2_1_1 : gutils->extractMeta(Builder2, fwdarg_3_1_sr_sr_2_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto fwdarg_3_1_sr_sr_2_1 = local_denom;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? fwdarg_3_1_sr_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_3_1_sr_sr_2_0, idx), fwdarg_3_1_sr_sr_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_3_1_sr_sr_0->getType() : getSubType(fwdarg_3_1_sr_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_3_1_sr_sr_0 : gutils->extractMeta(Builder2, fwdarg_3_1_sr_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_3_1_sr_sr_1 : gutils->extractMeta(Builder2, fwdarg_3_1_sr_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_3_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_3_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, fwdarg_3_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, fwdarg_3_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(fwdarg_3_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, fwdarg_3_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                ({
                  auto fwdarg_3_sr_0 = local_re;
                  auto fwdarg_3_sr_1 = ({
                    // Computing FNeg
                    auto fwdarg_3_sr_2_0 = local_im;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? fwdarg_3_sr_2_0 : gutils->extractMeta(Builder2, fwdarg_3_sr_2_0, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? fwdarg_3_sr_0->getType() : getSubType(fwdarg_3_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_3_sr_0 : gutils->extractMeta(Builder2, fwdarg_3_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? fwdarg_3_sr_1 : gutils->extractMeta(Builder2, fwdarg_3_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(fwdarg_0, fwdarg_1, (gutils->getWidth() == 1) ? fwdarg_2 : gutils->extractMeta(Builder2, fwdarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            itmp = Builder2.CreateIntrinsic(Intrinsic::fabs, ArrayRef<Type*>(itmp->getType()), ArrayRef<Value*>(itmp));
            arg_diff_tmp = GradientUtils::recursiveFAdd(Builder2,res, itmp, {}, {}, arg_diff_tmp, gutils->getWidth() != 1);
          }
          res = arg_diff_tmp;
        }
        res = Builder2.CreateMaxNum(get1ULP(Builder2, gutils->getNewFromOriginal(&call)), res);
        assert(res);
        Function *logFunc = call.getModule()->getFunction("enzymeLogError");
        if (logFunc) {
            std::string moduleName = call.getModule()->getModuleIdentifier() ;
            std::string functionName = call.getFunction()->getName().str();
            std::string blockName = call.getParent()->getName().str();
            int funcIdx = -1, blockIdx = -1, instIdx = -1;
            auto funcIt = std::find_if(call.getModule()->begin(), call.getModule()->end(),
              [&](const auto& func) { return &func == call.getFunction(); });
            if (funcIt != call.getModule()->end()) {
              funcIdx = std::distance(call.getModule()->begin(), funcIt);
            }
            auto blockIt = std::find_if(call.getFunction()->begin(), call.getFunction()->end(),
              [&](const auto& block) { return &block == call.getParent(); });
            if (blockIt != call.getFunction()->end()) {
              blockIdx = std::distance(call.getFunction()->begin(), blockIt);
            }
            auto instIt = std::find_if(call.getParent()->begin(), call.getParent()->end(),
              [&](const auto& curr) { return &curr == &call; });
            if (instIt != call.getParent()->end()) {
              instIdx = std::distance(call.getParent()->begin(), instIt);
            }
            Value *origValue = Builder2.CreateFPExt(gutils->getNewFromOriginal(&call), Type::getDoubleTy(call.getContext()));
            Value *errValue = Builder2.CreateFPExt(res, Type::getDoubleTy(call.getContext()));
            std::string opcodeName = call.getOpcodeName();
            std::string calleeName = "<N/A>";
            if (auto CI = dyn_cast<CallInst>(&call)) {
                if (Function *fn = CI->getCalledFunction()) {
                    calleeName = fn->getName();
                } else {
                    calleeName = "<Unknown>";
                }
            }
#if LLVM_VERSION_MAJOR >= 17
            Value *moduleNameValue = Builder2.CreateGlobalString(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalString(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalString(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalString(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalString(calleeName);
#else
            Value *moduleNameValue = Builder2.CreateGlobalStringPtr(moduleName);
            Value *functionNameValue = Builder2.CreateGlobalStringPtr(functionName + " (" +std::to_string(funcIdx) + ")");
            Value *blockNameValue = Builder2.CreateGlobalStringPtr(blockName + " (" +std::to_string(blockIdx) + ")");
            Value *opcodeNameValue = Builder2.CreateGlobalStringPtr(opcodeName + " (" +std::to_string(instIdx) + ")");
            Value *calleeNameValue = Builder2.CreateGlobalStringPtr(calleeName);
#endif
            Builder2.CreateCall(logFunc, {origValue, errValue, opcodeNameValue, calleeNameValue, moduleNameValue, functionNameValue, blockNameValue});
        }
        setDiffe(&call, res, Builder2);
        break;
      }
      case DerivativeMode::ReverseModeGradient:
      case DerivativeMode::ReverseModeCombined:{
        IRBuilder<> Builder2(&call);
        getReverseBuilder(Builder2);
        Value *dif = nullptr;
        if (!dif && !gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          dif = diffe(&call, Builder2);
          setDiffe(&call, Constant::getNullValue(gutils->getShadowType(call.getType())), Builder2);
        }
        if (!gutils->isConstantValue(call.getOperand(0)) && !isa<PointerType>(call.getOperand(0)->getType()) ) {
          Value *toadd = nullptr;
          {
            Value *tmp = ({
              // Computing Select
              auto revarg_0 = ({
                // Computing And
                auto revarg_1_0 = ({
                  // Computing FCmpUEQ
                  llvm::Value* revarg_1_1_0 = Builder2.CreateExtractValue(lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2), ArrayRef<unsigned>({0}));
                  auto revarg_1_1_1 = ConstantFP::get(gutils->extractMeta(call.getOperand(0)->getType(), ArrayRef<unsigned>({0})), "0");
                  Builder2.CreateFCmpUEQ(revarg_1_1_0, revarg_1_1_1);
                });
                auto revarg_1_1 = ({
                  // Computing FCmpUEQ
                  llvm::Value* revarg_1_2_0 = Builder2.CreateExtractValue(lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2), ArrayRef<unsigned>({1}));
                  auto revarg_1_2_1 = ConstantFP::get(gutils->extractMeta(call.getOperand(0)->getType(), ArrayRef<unsigned>({1})), "0");
                  Builder2.CreateFCmpUEQ(revarg_1_2_0, revarg_1_2_1);
                });
                Builder2.CreateAnd(revarg_1_0, revarg_1_1);
              });
              auto revarg_1 = ({
                auto ty = call.getOperand(0)->getType();
                Value *ret = nullptr;
                if (auto ST = dyn_cast<StructType>(ty)) {
                  ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "0"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                  ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "0"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                } else {
                  llvm::errs() << *ty << "\n";
                  assert(0 && "unhandled cfp");
                }
                ret;
              })
;
              auto revarg_2 = ({
                // Computing subroutine Conj
                auto revarg_3_0 = ({
                  // Computing subroutine CFDiv
                  auto revarg_3_1_0 = ({
                    // Computing subroutine Conj
                    auto revarg_3_1_1_0 = dif;
                    Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_3_1_1_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(revarg_3_1_1_0->getType(), 0, 0)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, revarg_3_1_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_3_1_1_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(revarg_3_1_1_0->getType(), 0, 1)));
                    if (gutils->getWidth() != 1)
                      for (size_t i=0; i<gutils->getWidth(); i++)
                        local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, revarg_3_1_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                    ({
                      auto revarg_3_1_1_sr_0 = local_re;
                      auto revarg_3_1_1_sr_1 = ({
                        // Computing FNeg
                        auto revarg_3_1_1_sr_2_0 = local_im;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_3_1_1_sr_2_0 : gutils->extractMeta(Builder2, revarg_3_1_1_sr_2_0, idx));
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_3_1_1_sr_0->getType() : getSubType(revarg_3_1_1_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_3_1_1_sr_0 : gutils->extractMeta(Builder2, revarg_3_1_1_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_3_1_1_sr_1 : gutils->extractMeta(Builder2, revarg_3_1_1_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                  auto revarg_3_1_1 = ({
                    // Computing subroutine CFMul
                    auto revarg_3_1_2_0 = ({
                      auto ty = call.getOperand(0)->getType();
                      Value *ret = nullptr;
                      if (auto ST = dyn_cast<StructType>(ty)) {
                        ret = ConstantStruct::get(ST, {(llvm::Constant*)ConstantFP::get(ST->getElementType(0), "2"), (llvm::Constant*)ConstantFP::get(ST->getElementType(1), "0")});
} else if (auto AT = dyn_cast<ArrayType>(ty)) {
                        ret = ConstantArray::get(AT, {(llvm::Constant*)ConstantFP::get(AT->getElementType(), "2"), (llvm::Constant*)ConstantFP::get(AT->getElementType(), "0")});
                      } else {
                        llvm::errs() << *ty << "\n";
                        assert(0 && "unhandled cfp");
                      }
                      ret;
                    })
;
                    auto revarg_3_1_2_1 = ({
                      // Computing Call
                      llvm::Value* revarg_3_1_2_2_0 = lookup(gutils->getNewFromOriginal(call.getOperand(0)), Builder2);
                      auto callval = cast<CallInst>(&call)->getCalledOperand();
                      auto FT = cast<CallInst>(&call)->getFunctionType();
                      auto cconv = cast<CallInst>(&call)->getCallingConv();
                      CallInst *V = cast<CallInst>(Builder2.CreateCall(FT, callval, ArrayRef<Value*>({revarg_3_1_2_2_0})));
                      V->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
                      V->setCallingConv(cconv);
#if LLVM_VERSION_MAJOR >= 16
                      V->setOnlyReadsMemory();
V->setOnlyWritesMemory();
#elif LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::ReadNone);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
#endif 
#if LLVM_VERSION_MAJOR >= 14
                      V->addAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoUnwind);
#else 
                      V->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
#endif 
                      V;
                    });
                    Value* local_re1 = Builder2.CreateExtractValue(revarg_3_1_2_0, ArrayRef<unsigned>({0}), "re1");
                    Value* local_im1 = Builder2.CreateExtractValue(revarg_3_1_2_0, ArrayRef<unsigned>({1}), "im1");
                    Value* local_re2 = Builder2.CreateExtractValue(revarg_3_1_2_1, ArrayRef<unsigned>({0}), "re2");
                    Value* local_im2 = Builder2.CreateExtractValue(revarg_3_1_2_1, ArrayRef<unsigned>({1}), "im2");
                    ({
                      // Computing subroutine CFMul_splat
                      auto revarg_3_1_2_sr_0 = local_re1;
                      auto revarg_3_1_2_sr_1 = local_im1;
                      auto revarg_3_1_2_sr_2 = local_re2;
                      auto revarg_3_1_2_sr_3 = local_im2;
                      ({
                        auto revarg_3_1_2_sr_sr_0 = ({
                          // Computing FSub
                          auto revarg_3_1_2_sr_sr_1_0 = ({
                            // Computing FMul
                            llvm::Value* revarg_3_1_2_sr_sr_1_1_0 = revarg_3_1_2_sr_0;
                            llvm::Value* revarg_3_1_2_sr_sr_1_1_1 = revarg_3_1_2_sr_2;
                            Builder2.CreateFMul(revarg_3_1_2_sr_sr_1_1_0, revarg_3_1_2_sr_sr_1_1_1);
                          });
                          auto revarg_3_1_2_sr_sr_1_1 = ({
                            // Computing FMul
                            llvm::Value* revarg_3_1_2_sr_sr_1_2_0 = revarg_3_1_2_sr_1;
                            llvm::Value* revarg_3_1_2_sr_sr_1_2_1 = revarg_3_1_2_sr_3;
                            Builder2.CreateFMul(revarg_3_1_2_sr_sr_1_2_0, revarg_3_1_2_sr_sr_1_2_1);
                          });
                          Builder2.CreateFSub(revarg_3_1_2_sr_sr_1_0, revarg_3_1_2_sr_sr_1_1);
                        });
                        auto revarg_3_1_2_sr_sr_1 = ({
                          // Computing FAdd
                          auto revarg_3_1_2_sr_sr_2_0 = ({
                            // Computing FMul
                            llvm::Value* revarg_3_1_2_sr_sr_2_1_0 = revarg_3_1_2_sr_0;
                            llvm::Value* revarg_3_1_2_sr_sr_2_1_1 = revarg_3_1_2_sr_3;
                            Builder2.CreateFMul(revarg_3_1_2_sr_sr_2_1_0, revarg_3_1_2_sr_sr_2_1_1);
                          });
                          auto revarg_3_1_2_sr_sr_2_1 = ({
                            // Computing FMul
                            llvm::Value* revarg_3_1_2_sr_sr_2_2_0 = revarg_3_1_2_sr_2;
                            llvm::Value* revarg_3_1_2_sr_sr_2_2_1 = revarg_3_1_2_sr_1;
                            Builder2.CreateFMul(revarg_3_1_2_sr_sr_2_2_0, revarg_3_1_2_sr_sr_2_2_1);
                          });
                          Builder2.CreateFAdd(revarg_3_1_2_sr_sr_2_0, revarg_3_1_2_sr_sr_2_1);
                        });
                        Value *res = UndefValue::get(ArrayType::get(revarg_3_1_2_sr_sr_0->getType(), 2));
                        {
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(0);
                            res = Builder2.CreateInsertValue(res, revarg_3_1_2_sr_sr_0, idxs);
                          }
                          {
                            std::vector<unsigned> idxs;
                            idxs.push_back(1);
                            res = Builder2.CreateInsertValue(res, revarg_3_1_2_sr_sr_1, idxs);
                          }
                        }
                         res;
                      });
                    });
                  });
                  Value* local_re1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_3_1_0, ArrayRef<unsigned>({0}), "re1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_3_1_0->getType(), 0, 0)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_re1 = Builder2.CreateInsertValue(local_re1, gutils->extractMeta(Builder2, revarg_3_1_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_im1 = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_3_1_0, ArrayRef<unsigned>({1}), "im1") : UndefValue::get(gutils->getShadowType(getSubType(revarg_3_1_0->getType(), 0, 1)));
                  if (gutils->getWidth() != 1)
                    for (size_t i=0; i<gutils->getWidth(); i++)
                      local_im1 = Builder2.CreateInsertValue(local_im1, gutils->extractMeta(Builder2, revarg_3_1_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im1."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                  Value* local_re2 = Builder2.CreateExtractValue(revarg_3_1_1, ArrayRef<unsigned>({0}), "re2");
                  Value* local_im2 = Builder2.CreateExtractValue(revarg_3_1_1, ArrayRef<unsigned>({1}), "im2");
                  ({
                    // Computing subroutine CFDiv_splat
                    auto revarg_3_1_sr_0 = local_re1;
                    auto revarg_3_1_sr_1 = local_im1;
                    auto revarg_3_1_sr_2 = local_re2;
                    auto revarg_3_1_sr_3 = local_im2;
                    llvm::Value *local_denom = nullptr;
                    ({
                      auto revarg_3_1_sr_sr_0 = ({
                        // Computing FDiv
                        auto revarg_3_1_sr_sr_1_0 = ({
                          // Computing FAdd
                          auto revarg_3_1_sr_sr_1_1_0 = ({
                            // Computing FMul
                            auto revarg_3_1_sr_sr_1_1_1_0 = revarg_3_1_sr_0;
                            llvm::Value* revarg_3_1_sr_sr_1_1_1_1 = revarg_3_1_sr_2;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_3_1_sr_sr_1_1_1_0 : gutils->extractMeta(Builder2, revarg_3_1_sr_sr_1_1_1_0, idx), revarg_3_1_sr_sr_1_1_1_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          auto revarg_3_1_sr_sr_1_1_1 = ({
                            // Computing FMul
                            auto revarg_3_1_sr_sr_1_1_2_0 = revarg_3_1_sr_1;
                            llvm::Value* revarg_3_1_sr_sr_1_1_2_1 = revarg_3_1_sr_3;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_3_1_sr_sr_1_1_2_0 : gutils->extractMeta(Builder2, revarg_3_1_sr_sr_1_1_2_0, idx), revarg_3_1_sr_sr_1_1_2_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFAdd((gutils->getWidth() == 1) ? revarg_3_1_sr_sr_1_1_0 : gutils->extractMeta(Builder2, revarg_3_1_sr_sr_1_1_0, idx), (gutils->getWidth() == 1) ? revarg_3_1_sr_sr_1_1_1 : gutils->extractMeta(Builder2, revarg_3_1_sr_sr_1_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_3_1_sr_sr_1_1 = ({
                          // Computing FAdd
                          auto revarg_3_1_sr_sr_1_2_0 = ({
                            // Computing FMul
                            llvm::Value* revarg_3_1_sr_sr_1_2_1_0 = revarg_3_1_sr_2;
                            llvm::Value* revarg_3_1_sr_sr_1_2_1_1 = revarg_3_1_sr_2;
                            Builder2.CreateFMul(revarg_3_1_sr_sr_1_2_1_0, revarg_3_1_sr_sr_1_2_1_1);
                          });
                          auto revarg_3_1_sr_sr_1_2_1 = ({
                            // Computing FMul
                            llvm::Value* revarg_3_1_sr_sr_1_2_2_0 = revarg_3_1_sr_3;
                            llvm::Value* revarg_3_1_sr_sr_1_2_2_1 = revarg_3_1_sr_3;
                            Builder2.CreateFMul(revarg_3_1_sr_sr_1_2_2_0, revarg_3_1_sr_sr_1_2_2_1);
                          });
                          Builder2.CreateFAdd(revarg_3_1_sr_sr_1_2_0, revarg_3_1_sr_sr_1_2_1);
                        });
                        local_denom = revarg_3_1_sr_sr_1_1;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_3_1_sr_sr_1_0 : gutils->extractMeta(Builder2, revarg_3_1_sr_sr_1_0, idx), revarg_3_1_sr_sr_1_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      auto revarg_3_1_sr_sr_1 = ({
                        // Computing FDiv
                        auto revarg_3_1_sr_sr_2_0 = ({
                          // Computing FSub
                          auto revarg_3_1_sr_sr_2_1_0 = ({
                            // Computing FMul
                            llvm::Value* revarg_3_1_sr_sr_2_1_1_0 = revarg_3_1_sr_2;
                            auto revarg_3_1_sr_sr_2_1_1_1 = revarg_3_1_sr_1;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul(revarg_3_1_sr_sr_2_1_1_0, (gutils->getWidth() == 1) ? revarg_3_1_sr_sr_2_1_1_1 : gutils->extractMeta(Builder2, revarg_3_1_sr_sr_2_1_1_1, idx));
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          auto revarg_3_1_sr_sr_2_1_1 = ({
                            // Computing FMul
                            auto revarg_3_1_sr_sr_2_1_2_0 = revarg_3_1_sr_0;
                            llvm::Value* revarg_3_1_sr_sr_2_1_2_1 = revarg_3_1_sr_3;
                            Value *res = nullptr;
                            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                              Value *V = Builder2.CreateFMul((gutils->getWidth() == 1) ? revarg_3_1_sr_sr_2_1_2_0 : gutils->extractMeta(Builder2, revarg_3_1_sr_sr_2_1_2_0, idx), revarg_3_1_sr_sr_2_1_2_1);
                              if (gutils->getWidth() == 1) res = V;
                              else {
                                if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                                res = Builder2.CreateInsertValue(res, V, {idx});
                              }
                            }
                            res;
                          });
                          Value *res = nullptr;
                          for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                            Value *V = Builder2.CreateFSub((gutils->getWidth() == 1) ? revarg_3_1_sr_sr_2_1_0 : gutils->extractMeta(Builder2, revarg_3_1_sr_sr_2_1_0, idx), (gutils->getWidth() == 1) ? revarg_3_1_sr_sr_2_1_1 : gutils->extractMeta(Builder2, revarg_3_1_sr_sr_2_1_1, idx));
                            if (gutils->getWidth() == 1) res = V;
                            else {
                              if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                              res = Builder2.CreateInsertValue(res, V, {idx});
                            }
                          }
                          res;
                        });
                        auto revarg_3_1_sr_sr_2_1 = local_denom;
                        Value *res = nullptr;
                        for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                          Value *V = Builder2.CreateFDiv((gutils->getWidth() == 1) ? revarg_3_1_sr_sr_2_0 : gutils->extractMeta(Builder2, revarg_3_1_sr_sr_2_0, idx), revarg_3_1_sr_sr_2_1);
                          if (gutils->getWidth() == 1) res = V;
                          else {
                            if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                            res = Builder2.CreateInsertValue(res, V, {idx});
                          }
                        }
                        res;
                      });
                      Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_3_1_sr_sr_0->getType() : getSubType(revarg_3_1_sr_sr_0->getType(), -1), 2)));
                      for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(0);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_3_1_sr_sr_0 : gutils->extractMeta(Builder2, revarg_3_1_sr_sr_0, idx), idxs);
                        }
                        {
                          std::vector<unsigned> idxs;
                          if (gutils->getWidth() != 1) idxs.push_back(idx);
                          idxs.push_back(1);
                          res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_3_1_sr_sr_1 : gutils->extractMeta(Builder2, revarg_3_1_sr_sr_1, idx), idxs);
                        }
                      }
                       res;
                    });
                  });
                });
                Value* local_re = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_3_0, ArrayRef<unsigned>({0}), "re") : UndefValue::get(gutils->getShadowType(getSubType(revarg_3_0->getType(), 0, 0)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_re = Builder2.CreateInsertValue(local_re, gutils->extractMeta(Builder2, revarg_3_0, ArrayRef<unsigned>({(unsigned)i, 0}), "re."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                Value* local_im = gutils->getWidth() == 1 ? gutils->extractMeta(Builder2, revarg_3_0, ArrayRef<unsigned>({1}), "im") : UndefValue::get(gutils->getShadowType(getSubType(revarg_3_0->getType(), 0, 1)));
                if (gutils->getWidth() != 1)
                  for (size_t i=0; i<gutils->getWidth(); i++)
                    local_im = Builder2.CreateInsertValue(local_im, gutils->extractMeta(Builder2, revarg_3_0, ArrayRef<unsigned>({(unsigned)i, 1}), "im."+Twine(i)), ArrayRef<unsigned>({(unsigned)i}));
                ({
                  auto revarg_3_sr_0 = local_re;
                  auto revarg_3_sr_1 = ({
                    // Computing FNeg
                    auto revarg_3_sr_2_0 = local_im;
                    Value *res = nullptr;
                    for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                      Value *V = Builder2.CreateFNeg((gutils->getWidth() == 1) ? revarg_3_sr_2_0 : gutils->extractMeta(Builder2, revarg_3_sr_2_0, idx));
                      if (gutils->getWidth() == 1) res = V;
                      else {
                        if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                        res = Builder2.CreateInsertValue(res, V, {idx});
                      }
                    }
                    res;
                  });
                  Value *res = UndefValue::get(gutils->getShadowType(ArrayType::get((gutils->getWidth() == 1) ? revarg_3_sr_0->getType() : getSubType(revarg_3_sr_0->getType(), -1), 2)));
                  for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(0);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_3_sr_0 : gutils->extractMeta(Builder2, revarg_3_sr_0, idx), idxs);
                    }
                    {
                      std::vector<unsigned> idxs;
                      if (gutils->getWidth() != 1) idxs.push_back(idx);
                      idxs.push_back(1);
                      res = Builder2.CreateInsertValue(res, (gutils->getWidth() == 1) ? revarg_3_sr_1 : gutils->extractMeta(Builder2, revarg_3_sr_1, idx), idxs);
                    }
                  }
                   res;
                });
              });
              Value *res = nullptr;
              for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
                Value *V = Builder2.CreateSelect(revarg_0, revarg_1, (gutils->getWidth() == 1) ? revarg_2 : gutils->extractMeta(Builder2, revarg_2, idx));
                if (gutils->getWidth() == 1) res = V;
                else {
                  if (idx == 0) res = UndefValue::get(ArrayType::get(V->getType(), gutils->getWidth()));
                  res = Builder2.CreateInsertValue(res, V, {idx});
                }
              }
              res;
            });
            Value *out = UndefValue::get(gutils->getShadowType(call.getOperand(0)->getType()));
            for(unsigned int idx=0, W=gutils->getWidth(); idx<W; idx++) {
              Value *prev = toadd ? (gutils->getWidth() == 1 ? toadd : gutils->extractMeta(Builder2, toadd, idx)) : nullptr;
              Value *next = tmp;
              if (gutils->getWidth() > 1) next = gutils->extractMeta(Builder2, next, idx);
              if (prev) next = Builder2.CreateFAdd(prev, next);
              out = (gutils->getWidth() > 1) ? Builder2.CreateInsertValue(out, next, idx) : next;
            }
            toadd = out;
          }
          if (toadd) addToDiffe(call.getOperand(0), toadd, Builder2, call.getOperand(0)->getType());
        }
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
      case DerivativeMode::ReverseModePrimal:{
        auto found = gutils->invertedPointers.find(&(call));
        if (found != gutils->invertedPointers.end() && !isa<PointerType>(call.getType())) {
          PHINode* PN = dyn_cast<PHINode>(&*found->second);
          if (!PN) {
            std::string str;
            raw_string_ostream ss(str);
            ss << "Shadow of instruction is not phi:\n";
            ss << *gutils->oldFunc << "\n";
            ss << *gutils->newFunc << "\n";
            ss << "orig: " << call << "\n";
            ss << "found: " << *found->second << "\n";
            if (CustomErrorHandler) {
              CustomErrorHandler(str.c_str(), wrap(&(call)), ErrorType::InternalError,
                                 nullptr, nullptr, nullptr);
            } else {
              EmitFailure("PHIError", (call).getDebugLoc(), &(call), ss.str());
            }
          }
          assert(PN);
          gutils->invertedPointers.erase(found);
          gutils->erase(PN);
        }
        break;
      }
    }
    return true;
  }
