| //===-- IntrinsicInst.cpp - Intrinsic Instruction Wrappers ---------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file implements methods that make it really easy to deal with intrinsic | |
| // functions. | |
| // | |
| // All intrinsic function calls are instances of the call instruction, so these | |
| // are all subclasses of the CallInst class. Note that none of these classes | |
| // has state or virtual methods, which is an important part of this gross/neat | |
| // hack working. | |
| // | |
| // In some cases, arguments to intrinsics need to be generic and are defined as | |
| // type pointer to empty struct { }*. To access the real item of interest the | |
| // cast instruction needs to be stripped away. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/IR/IntrinsicInst.h" | |
| #include "llvm/ADT/StringSwitch.h" | |
| #include "llvm/IR/Constants.h" | |
| #include "llvm/IR/DebugInfoMetadata.h" | |
| #include "llvm/IR/Metadata.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/IR/Operator.h" | |
| #include "llvm/IR/PatternMatch.h" | |
| #include "llvm/IR/Statepoint.h" | |
| #include <optional> | |
| using namespace llvm; | |
| bool IntrinsicInst::mayLowerToFunctionCall(Intrinsic::ID IID) { | |
| switch (IID) { | |
| case Intrinsic::objc_autorelease: | |
| case Intrinsic::objc_autoreleasePoolPop: | |
| case Intrinsic::objc_autoreleasePoolPush: | |
| case Intrinsic::objc_autoreleaseReturnValue: | |
| case Intrinsic::objc_claimAutoreleasedReturnValue: | |
| case Intrinsic::objc_copyWeak: | |
| case Intrinsic::objc_destroyWeak: | |
| case Intrinsic::objc_initWeak: | |
| case Intrinsic::objc_loadWeak: | |
| case Intrinsic::objc_loadWeakRetained: | |
| case Intrinsic::objc_moveWeak: | |
| case Intrinsic::objc_release: | |
| case Intrinsic::objc_retain: | |
| case Intrinsic::objc_retainAutorelease: | |
| case Intrinsic::objc_retainAutoreleaseReturnValue: | |
| case Intrinsic::objc_retainAutoreleasedReturnValue: | |
| case Intrinsic::objc_retainBlock: | |
| case Intrinsic::objc_storeStrong: | |
| case Intrinsic::objc_storeWeak: | |
| case Intrinsic::objc_unsafeClaimAutoreleasedReturnValue: | |
| case Intrinsic::objc_retainedObject: | |
| case Intrinsic::objc_unretainedObject: | |
| case Intrinsic::objc_unretainedPointer: | |
| case Intrinsic::objc_retain_autorelease: | |
| case Intrinsic::objc_sync_enter: | |
| case Intrinsic::objc_sync_exit: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| //===----------------------------------------------------------------------===// | |
| /// DbgVariableIntrinsic - This is the common base class for debug info | |
| /// intrinsics for variables. | |
| /// | |
| iterator_range<location_op_iterator> RawLocationWrapper::location_ops() const { | |
| Metadata *MD = getRawLocation(); | |
| assert(MD && "First operand of DbgVariableIntrinsic should be non-null."); | |
| // If operand is ValueAsMetadata, return a range over just that operand. | |
| if (auto *VAM = dyn_cast<ValueAsMetadata>(MD)) { | |
| return {location_op_iterator(VAM), location_op_iterator(VAM + 1)}; | |
| } | |
| // If operand is DIArgList, return a range over its args. | |
| if (auto *AL = dyn_cast<DIArgList>(MD)) | |
| return {location_op_iterator(AL->args_begin()), | |
| location_op_iterator(AL->args_end())}; | |
| // Operand must be an empty metadata tuple, so return empty iterator. | |
| return {location_op_iterator(static_cast<ValueAsMetadata *>(nullptr)), | |
| location_op_iterator(static_cast<ValueAsMetadata *>(nullptr))}; | |
| } | |
| Value *DbgVariableIntrinsic::getVariableLocationOp(unsigned OpIdx) const { | |
| return getWrappedLocation().getVariableLocationOp(OpIdx); | |
| } | |
| Value *RawLocationWrapper::getVariableLocationOp(unsigned OpIdx) const { | |
| Metadata *MD = getRawLocation(); | |
| assert(MD && "First operand of DbgVariableIntrinsic should be non-null."); | |
| if (auto *AL = dyn_cast<DIArgList>(MD)) | |
| return AL->getArgs()[OpIdx]->getValue(); | |
| if (isa<MDNode>(MD)) | |
| return nullptr; | |
| assert( | |
| isa<ValueAsMetadata>(MD) && | |
| "Attempted to get location operand from DbgVariableIntrinsic with none."); | |
| auto *V = cast<ValueAsMetadata>(MD); | |
| assert(OpIdx == 0 && "Operand Index must be 0 for a debug intrinsic with a " | |
| "single location operand."); | |
| return V->getValue(); | |
| } | |
| ConstantInt *InstrProfCntrInstBase::getNumCounters() const { | |
| if (InstrProfValueProfileInst::classof(this)) | |
| llvm_unreachable("InstrProfValueProfileInst does not have counters!"); | |
| return cast<ConstantInt>(getArgOperand(2)); | |
| } | |
| ConstantInt *InstrProfCntrInstBase::getIndex() const { | |
| if (InstrProfValueProfileInst::classof(this)) | |
| llvm_unreachable("Please use InstrProfValueProfileInst::getIndex()"); | |
| return cast<ConstantInt>(getArgOperand(3)); | |
| } | |
| void InstrProfCntrInstBase::setIndex(uint32_t Idx) { | |
| assert(isa<InstrProfCntrInstBase>(this)); | |
| setArgOperand(3, ConstantInt::get(Type::getInt32Ty(getContext()), Idx)); | |
| } | |
| Value *InstrProfIncrementInst::getStep() const { | |
| if (InstrProfIncrementInstStep::classof(this)) { | |
| return getArgOperand(4); | |
| } | |
| const Module *M = getModule(); | |
| LLVMContext &Context = M->getContext(); | |
| return ConstantInt::get(Type::getInt64Ty(Context), 1); | |
| } | |
| Value *InstrProfCallsite::getCallee() const { return getArgOperand(4); } | |
| void InstrProfCallsite::setCallee(Value *Callee) { | |
| assert(isa<InstrProfCallsite>(this)); | |
| setArgOperand(4, Callee); | |
| } | |
| std::optional<RoundingMode> ConstrainedFPIntrinsic::getRoundingMode() const { | |
| unsigned NumOperands = arg_size(); | |
| Metadata *MD = nullptr; | |
| auto *MAV = dyn_cast<MetadataAsValue>(getArgOperand(NumOperands - 2)); | |
| if (MAV) | |
| MD = MAV->getMetadata(); | |
| if (!MD || !isa<MDString>(MD)) | |
| return std::nullopt; | |
| return convertStrToRoundingMode(cast<MDString>(MD)->getString()); | |
| } | |
| std::optional<fp::ExceptionBehavior> | |
| ConstrainedFPIntrinsic::getExceptionBehavior() const { | |
| unsigned NumOperands = arg_size(); | |
| Metadata *MD = nullptr; | |
| auto *MAV = dyn_cast<MetadataAsValue>(getArgOperand(NumOperands - 1)); | |
| if (MAV) | |
| MD = MAV->getMetadata(); | |
| if (!MD || !isa<MDString>(MD)) | |
| return std::nullopt; | |
| return convertStrToExceptionBehavior(cast<MDString>(MD)->getString()); | |
| } | |
| bool ConstrainedFPIntrinsic::isDefaultFPEnvironment() const { | |
| std::optional<fp::ExceptionBehavior> Except = getExceptionBehavior(); | |
| if (Except) { | |
| if (*Except != fp::ebIgnore) | |
| return false; | |
| } | |
| std::optional<RoundingMode> Rounding = getRoundingMode(); | |
| if (Rounding) { | |
| if (*Rounding != RoundingMode::NearestTiesToEven) | |
| return false; | |
| } | |
| return true; | |
| } | |
| static FCmpInst::Predicate getFPPredicateFromMD(const Value *Op) { | |
| Metadata *MD = cast<MetadataAsValue>(Op)->getMetadata(); | |
| if (!MD || !isa<MDString>(MD)) | |
| return FCmpInst::BAD_FCMP_PREDICATE; | |
| return StringSwitch<FCmpInst::Predicate>(cast<MDString>(MD)->getString()) | |
| .Case("oeq", FCmpInst::FCMP_OEQ) | |
| .Case("ogt", FCmpInst::FCMP_OGT) | |
| .Case("oge", FCmpInst::FCMP_OGE) | |
| .Case("olt", FCmpInst::FCMP_OLT) | |
| .Case("ole", FCmpInst::FCMP_OLE) | |
| .Case("one", FCmpInst::FCMP_ONE) | |
| .Case("ord", FCmpInst::FCMP_ORD) | |
| .Case("uno", FCmpInst::FCMP_UNO) | |
| .Case("ueq", FCmpInst::FCMP_UEQ) | |
| .Case("ugt", FCmpInst::FCMP_UGT) | |
| .Case("uge", FCmpInst::FCMP_UGE) | |
| .Case("ult", FCmpInst::FCMP_ULT) | |
| .Case("ule", FCmpInst::FCMP_ULE) | |
| .Case("une", FCmpInst::FCMP_UNE) | |
| .Default(FCmpInst::BAD_FCMP_PREDICATE); | |
| } | |
| FCmpInst::Predicate ConstrainedFPCmpIntrinsic::getPredicate() const { | |
| return getFPPredicateFromMD(getArgOperand(2)); | |
| } | |
| unsigned ConstrainedFPIntrinsic::getNonMetadataArgCount() const { | |
| // All constrained fp intrinsics have "fpexcept" metadata. | |
| unsigned NumArgs = arg_size() - 1; | |
| // Some intrinsics have "round" metadata. | |
| if (Intrinsic::hasConstrainedFPRoundingModeOperand(getIntrinsicID())) | |
| NumArgs -= 1; | |
| // Compare intrinsics take their predicate as metadata. | |
| if (isa<ConstrainedFPCmpIntrinsic>(this)) | |
| NumArgs -= 1; | |
| return NumArgs; | |
| } | |
| bool ConstrainedFPIntrinsic::classof(const IntrinsicInst *I) { | |
| return Intrinsic::isConstrainedFPIntrinsic(I->getIntrinsicID()); | |
| } | |
| ElementCount VPIntrinsic::getStaticVectorLength() const { | |
| auto GetVectorLengthOfType = [](const Type *T) -> ElementCount { | |
| const auto *VT = cast<VectorType>(T); | |
| auto ElemCount = VT->getElementCount(); | |
| return ElemCount; | |
| }; | |
| Value *VPMask = getMaskParam(); | |
| if (!VPMask) { | |
| assert((getIntrinsicID() == Intrinsic::vp_merge) && | |
| "Unexpected VP intrinsic without mask operand"); | |
| return GetVectorLengthOfType(getType()); | |
| } | |
| return GetVectorLengthOfType(VPMask->getType()); | |
| } | |
| Value *VPIntrinsic::getMaskParam() const { | |
| if (auto MaskPos = getMaskParamPos(getIntrinsicID())) | |
| return getArgOperand(*MaskPos); | |
| return nullptr; | |
| } | |
| void VPIntrinsic::setMaskParam(Value *NewMask) { | |
| auto MaskPos = getMaskParamPos(getIntrinsicID()); | |
| setArgOperand(*MaskPos, NewMask); | |
| } | |
| Value *VPIntrinsic::getVectorLengthParam() const { | |
| if (auto EVLPos = getVectorLengthParamPos(getIntrinsicID())) | |
| return getArgOperand(*EVLPos); | |
| return nullptr; | |
| } | |
| void VPIntrinsic::setVectorLengthParam(Value *NewEVL) { | |
| auto EVLPos = getVectorLengthParamPos(getIntrinsicID()); | |
| setArgOperand(*EVLPos, NewEVL); | |
| } | |
| std::optional<unsigned> | |
| VPIntrinsic::getMaskParamPos(Intrinsic::ID IntrinsicID) { | |
| switch (IntrinsicID) { | |
| default: | |
| return std::nullopt; | |
| #define BEGIN_REGISTER_VP_INTRINSIC(VPID, MASKPOS, VLENPOS) \ | |
| case Intrinsic::VPID: \ | |
| return MASKPOS; | |
| #include "llvm/IR/VPIntrinsics.def" | |
| } | |
| } | |
| std::optional<unsigned> | |
| VPIntrinsic::getVectorLengthParamPos(Intrinsic::ID IntrinsicID) { | |
| switch (IntrinsicID) { | |
| default: | |
| return std::nullopt; | |
| #define BEGIN_REGISTER_VP_INTRINSIC(VPID, MASKPOS, VLENPOS) \ | |
| case Intrinsic::VPID: \ | |
| return VLENPOS; | |
| #include "llvm/IR/VPIntrinsics.def" | |
| } | |
| } | |
| /// \return the alignment of the pointer used by this load/store/gather or | |
| /// scatter. | |
| MaybeAlign VPIntrinsic::getPointerAlignment() const { | |
| std::optional<unsigned> PtrParamOpt = | |
| getMemoryPointerParamPos(getIntrinsicID()); | |
| assert(PtrParamOpt && "no pointer argument!"); | |
| return getParamAlign(*PtrParamOpt); | |
| } | |
| /// \return The pointer operand of this load,store, gather or scatter. | |
| Value *VPIntrinsic::getMemoryPointerParam() const { | |
| if (auto PtrParamOpt = getMemoryPointerParamPos(getIntrinsicID())) | |
| return getArgOperand(*PtrParamOpt); | |
| return nullptr; | |
| } | |
| std::optional<unsigned> | |
| VPIntrinsic::getMemoryPointerParamPos(Intrinsic::ID VPID) { | |
| switch (VPID) { | |
| default: | |
| return std::nullopt; | |
| case Intrinsic::vp_store: | |
| case Intrinsic::vp_scatter: | |
| case Intrinsic::experimental_vp_strided_store: | |
| return 1; | |
| case Intrinsic::vp_load: | |
| case Intrinsic::vp_load_ff: | |
| case Intrinsic::vp_gather: | |
| case Intrinsic::experimental_vp_strided_load: | |
| return 0; | |
| } | |
| } | |
| /// \return The data (payload) operand of this store or scatter. | |
| Value *VPIntrinsic::getMemoryDataParam() const { | |
| auto DataParamOpt = getMemoryDataParamPos(getIntrinsicID()); | |
| if (!DataParamOpt) | |
| return nullptr; | |
| return getArgOperand(*DataParamOpt); | |
| } | |
| std::optional<unsigned> VPIntrinsic::getMemoryDataParamPos(Intrinsic::ID VPID) { | |
| switch (VPID) { | |
| default: | |
| return std::nullopt; | |
| case Intrinsic::vp_store: | |
| case Intrinsic::vp_scatter: | |
| case Intrinsic::experimental_vp_strided_store: | |
| return 0; | |
| } | |
| } | |
| constexpr bool isVPIntrinsic(Intrinsic::ID ID) { | |
| switch (ID) { | |
| default: | |
| break; | |
| #define BEGIN_REGISTER_VP_INTRINSIC(VPID, MASKPOS, VLENPOS) \ | |
| case Intrinsic::VPID: \ | |
| return true; | |
| #include "llvm/IR/VPIntrinsics.def" | |
| } | |
| return false; | |
| } | |
| bool VPIntrinsic::isVPIntrinsic(Intrinsic::ID ID) { | |
| return ::isVPIntrinsic(ID); | |
| } | |
| // Equivalent non-predicated opcode | |
| constexpr static std::optional<unsigned> | |
| getFunctionalOpcodeForVP(Intrinsic::ID ID) { | |
| switch (ID) { | |
| default: | |
| break; | |
| #define BEGIN_REGISTER_VP_INTRINSIC(VPID, ...) case Intrinsic::VPID: | |
| #define VP_PROPERTY_FUNCTIONAL_OPC(OPC) return Instruction::OPC; | |
| #define END_REGISTER_VP_INTRINSIC(VPID) break; | |
| #include "llvm/IR/VPIntrinsics.def" | |
| } | |
| return std::nullopt; | |
| } | |
| std::optional<unsigned> | |
| VPIntrinsic::getFunctionalOpcodeForVP(Intrinsic::ID ID) { | |
| return ::getFunctionalOpcodeForVP(ID); | |
| } | |
| // Equivalent non-predicated intrinsic ID | |
| constexpr static std::optional<Intrinsic::ID> | |
| getFunctionalIntrinsicIDForVP(Intrinsic::ID ID) { | |
| switch (ID) { | |
| default: | |
| break; | |
| #define BEGIN_REGISTER_VP_INTRINSIC(VPID, ...) case Intrinsic::VPID: | |
| #define VP_PROPERTY_FUNCTIONAL_INTRINSIC(INTRIN) return Intrinsic::INTRIN; | |
| #define END_REGISTER_VP_INTRINSIC(VPID) break; | |
| #include "llvm/IR/VPIntrinsics.def" | |
| } | |
| return std::nullopt; | |
| } | |
| std::optional<Intrinsic::ID> | |
| VPIntrinsic::getFunctionalIntrinsicIDForVP(Intrinsic::ID ID) { | |
| return ::getFunctionalIntrinsicIDForVP(ID); | |
| } | |
| constexpr static bool doesVPHaveNoFunctionalEquivalent(Intrinsic::ID ID) { | |
| switch (ID) { | |
| default: | |
| break; | |
| #define BEGIN_REGISTER_VP_INTRINSIC(VPID, ...) case Intrinsic::VPID: | |
| #define VP_PROPERTY_NO_FUNCTIONAL return true; | |
| #define END_REGISTER_VP_INTRINSIC(VPID) break; | |
| #include "llvm/IR/VPIntrinsics.def" | |
| } | |
| return false; | |
| } | |
| // All VP intrinsics should have an equivalent non-VP opcode or intrinsic | |
| // defined, or be marked that they don't have one. | |
| #define BEGIN_REGISTER_VP_INTRINSIC(VPID, ...) \ | |
| static_assert(doesVPHaveNoFunctionalEquivalent(Intrinsic::VPID) || \ | |
| getFunctionalOpcodeForVP(Intrinsic::VPID) || \ | |
| getFunctionalIntrinsicIDForVP(Intrinsic::VPID)); | |
| #include "llvm/IR/VPIntrinsics.def" | |
| bool VPIntrinsic::canIgnoreVectorLengthParam() const { | |
| using namespace PatternMatch; | |
| ElementCount EC = getStaticVectorLength(); | |
| // No vlen param - no lanes masked-off by it. | |
| auto *VLParam = getVectorLengthParam(); | |
| if (!VLParam) | |
| return true; | |
| // Note that the VP intrinsic causes undefined behavior if the Explicit Vector | |
| // Length parameter is strictly greater-than the number of vector elements of | |
| // the operation. This function returns true when this is detected statically | |
| // in the IR. | |
| // Check whether "W == vscale * EC.getKnownMinValue()" | |
| if (EC.isScalable()) { | |
| // Compare vscale patterns | |
| uint64_t VScaleFactor; | |
| if (match(VLParam, m_Mul(m_VScale(), m_ConstantInt(VScaleFactor)))) | |
| return VScaleFactor >= EC.getKnownMinValue(); | |
| return (EC.getKnownMinValue() == 1) && match(VLParam, m_VScale()); | |
| } | |
| // standard SIMD operation | |
| const auto *VLConst = dyn_cast<ConstantInt>(VLParam); | |
| if (!VLConst) | |
| return false; | |
| uint64_t VLNum = VLConst->getZExtValue(); | |
| if (VLNum >= EC.getKnownMinValue()) | |
| return true; | |
| return false; | |
| } | |
| Function *VPIntrinsic::getOrInsertDeclarationForParams( | |
| Module *M, Intrinsic::ID VPID, Type *ReturnType, ArrayRef<Value *> Params) { | |
| assert(isVPIntrinsic(VPID) && "not a VP intrinsic"); | |
| Function *VPFunc; | |
| switch (VPID) { | |
| default: { | |
| Type *OverloadTy = Params[0]->getType(); | |
| if (VPReductionIntrinsic::isVPReduction(VPID)) | |
| OverloadTy = | |
| Params[*VPReductionIntrinsic::getVectorParamPos(VPID)]->getType(); | |
| VPFunc = Intrinsic::getOrInsertDeclaration(M, VPID, OverloadTy); | |
| break; | |
| } | |
| case Intrinsic::vp_cttz_elts: | |
| VPFunc = Intrinsic::getOrInsertDeclaration( | |
| M, VPID, {ReturnType, Params[0]->getType()}); | |
| break; | |
| case Intrinsic::vp_merge: | |
| VPFunc = Intrinsic::getOrInsertDeclaration(M, VPID, {Params[1]->getType()}); | |
| break; | |
| case Intrinsic::vp_load: | |
| VPFunc = Intrinsic::getOrInsertDeclaration( | |
| M, VPID, {ReturnType, Params[0]->getType()}); | |
| break; | |
| case Intrinsic::vp_load_ff: | |
| VPFunc = Intrinsic::getOrInsertDeclaration( | |
| M, VPID, {ReturnType->getStructElementType(0), Params[0]->getType()}); | |
| break; | |
| case Intrinsic::experimental_vp_strided_load: | |
| VPFunc = Intrinsic::getOrInsertDeclaration( | |
| M, VPID, {ReturnType, Params[0]->getType(), Params[1]->getType()}); | |
| break; | |
| case Intrinsic::vp_gather: | |
| VPFunc = Intrinsic::getOrInsertDeclaration( | |
| M, VPID, {ReturnType, Params[0]->getType()}); | |
| break; | |
| case Intrinsic::vp_store: | |
| VPFunc = Intrinsic::getOrInsertDeclaration( | |
| M, VPID, {Params[0]->getType(), Params[1]->getType()}); | |
| break; | |
| case Intrinsic::experimental_vp_strided_store: | |
| VPFunc = Intrinsic::getOrInsertDeclaration( | |
| M, VPID, | |
| {Params[0]->getType(), Params[1]->getType(), Params[2]->getType()}); | |
| break; | |
| case Intrinsic::vp_scatter: | |
| VPFunc = Intrinsic::getOrInsertDeclaration( | |
| M, VPID, {Params[0]->getType(), Params[1]->getType()}); | |
| break; | |
| } | |
| assert(VPFunc && "Could not declare VP intrinsic"); | |
| return VPFunc; | |
| } | |
| bool VPReductionIntrinsic::isVPReduction(Intrinsic::ID ID) { | |
| switch (ID) { | |
| case Intrinsic::vp_reduce_add: | |
| case Intrinsic::vp_reduce_mul: | |
| case Intrinsic::vp_reduce_and: | |
| case Intrinsic::vp_reduce_or: | |
| case Intrinsic::vp_reduce_xor: | |
| case Intrinsic::vp_reduce_smax: | |
| case Intrinsic::vp_reduce_smin: | |
| case Intrinsic::vp_reduce_umax: | |
| case Intrinsic::vp_reduce_umin: | |
| case Intrinsic::vp_reduce_fmax: | |
| case Intrinsic::vp_reduce_fmin: | |
| case Intrinsic::vp_reduce_fmaximum: | |
| case Intrinsic::vp_reduce_fminimum: | |
| case Intrinsic::vp_reduce_fadd: | |
| case Intrinsic::vp_reduce_fmul: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| unsigned VPReductionIntrinsic::getVectorParamPos() const { | |
| return *VPReductionIntrinsic::getVectorParamPos(getIntrinsicID()); | |
| } | |
| unsigned VPReductionIntrinsic::getStartParamPos() const { | |
| return *VPReductionIntrinsic::getStartParamPos(getIntrinsicID()); | |
| } | |
| std::optional<unsigned> | |
| VPReductionIntrinsic::getVectorParamPos(Intrinsic::ID ID) { | |
| if (isVPReduction(ID)) | |
| return 1; | |
| return std::nullopt; | |
| } | |
| std::optional<unsigned> | |
| VPReductionIntrinsic::getStartParamPos(Intrinsic::ID ID) { | |
| if (isVPReduction(ID)) | |
| return 0; | |
| return std::nullopt; | |
| } | |
| Instruction::BinaryOps BinaryOpIntrinsic::getBinaryOp() const { | |
| switch (getIntrinsicID()) { | |
| case Intrinsic::uadd_with_overflow: | |
| case Intrinsic::sadd_with_overflow: | |
| case Intrinsic::uadd_sat: | |
| case Intrinsic::sadd_sat: | |
| return Instruction::Add; | |
| case Intrinsic::usub_with_overflow: | |
| case Intrinsic::ssub_with_overflow: | |
| case Intrinsic::usub_sat: | |
| case Intrinsic::ssub_sat: | |
| return Instruction::Sub; | |
| case Intrinsic::umul_with_overflow: | |
| case Intrinsic::smul_with_overflow: | |
| return Instruction::Mul; | |
| default: | |
| llvm_unreachable("Invalid intrinsic"); | |
| } | |
| } | |
| bool BinaryOpIntrinsic::isSigned() const { | |
| switch (getIntrinsicID()) { | |
| case Intrinsic::sadd_with_overflow: | |
| case Intrinsic::ssub_with_overflow: | |
| case Intrinsic::smul_with_overflow: | |
| case Intrinsic::sadd_sat: | |
| case Intrinsic::ssub_sat: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| unsigned BinaryOpIntrinsic::getNoWrapKind() const { | |
| if (isSigned()) | |
| return OverflowingBinaryOperator::NoSignedWrap; | |
| else | |
| return OverflowingBinaryOperator::NoUnsignedWrap; | |
| } | |
| const Value *GCProjectionInst::getStatepoint() const { | |
| const Value *Token = getArgOperand(0); | |
| if (isa<UndefValue>(Token)) | |
| return Token; | |
| // Treat none token as if it was undef here | |
| if (isa<ConstantTokenNone>(Token)) | |
| return UndefValue::get(Token->getType()); | |
| // This takes care both of relocates for call statepoints and relocates | |
| // on normal path of invoke statepoint. | |
| if (!isa<LandingPadInst>(Token)) | |
| return cast<GCStatepointInst>(Token); | |
| // This relocate is on exceptional path of an invoke statepoint | |
| const BasicBlock *InvokeBB = | |
| cast<Instruction>(Token)->getParent()->getUniquePredecessor(); | |
| assert(InvokeBB && "safepoints should have unique landingpads"); | |
| return cast<GCStatepointInst>(InvokeBB->getTerminator()); | |
| } | |
| Value *GCRelocateInst::getBasePtr() const { | |
| auto Statepoint = getStatepoint(); | |
| if (isa<UndefValue>(Statepoint)) | |
| return UndefValue::get(Statepoint->getType()); | |
| // Handle too few (bundle) arguments to avoid crashes when printing invalid | |
| // IR, e.g. in the verifier. | |
| auto *GCInst = cast<GCStatepointInst>(Statepoint); | |
| if (auto Opt = GCInst->getOperandBundle(LLVMContext::OB_gc_live)) { | |
| if (getBasePtrIndex() > Opt->Inputs.size()) | |
| return nullptr; | |
| return *(Opt->Inputs.begin() + getBasePtrIndex()); | |
| } | |
| if (getBasePtrIndex() > GCInst->arg_size()) | |
| return nullptr; | |
| return *(GCInst->arg_begin() + getBasePtrIndex()); | |
| } | |
| Value *GCRelocateInst::getDerivedPtr() const { | |
| auto *Statepoint = getStatepoint(); | |
| if (isa<UndefValue>(Statepoint)) | |
| return UndefValue::get(Statepoint->getType()); | |
| // Handle too few (bundle) arguments to avoid crashes when printing invalid | |
| // IR, e.g. in the verifier. | |
| auto *GCInst = cast<GCStatepointInst>(Statepoint); | |
| if (auto Opt = GCInst->getOperandBundle(LLVMContext::OB_gc_live)) { | |
| if (getDerivedPtrIndex() > Opt->Inputs.size()) | |
| return nullptr; | |
| return *(Opt->Inputs.begin() + getDerivedPtrIndex()); | |
| } | |
| if (getDerivedPtrIndex() > GCInst->arg_size()) | |
| return nullptr; | |
| return *(GCInst->arg_begin() + getDerivedPtrIndex()); | |
| } | |
| ConvergenceControlInst *ConvergenceControlInst::CreateAnchor(BasicBlock &BB) { | |
| Module *M = BB.getModule(); | |
| Function *Fn = Intrinsic::getOrInsertDeclaration( | |
| M, llvm::Intrinsic::experimental_convergence_anchor); | |
| auto *Call = CallInst::Create(Fn, "", BB.getFirstInsertionPt()); | |
| return cast<ConvergenceControlInst>(Call); | |
| } | |
| ConvergenceControlInst *ConvergenceControlInst::CreateEntry(BasicBlock &BB) { | |
| Module *M = BB.getModule(); | |
| Function *Fn = Intrinsic::getOrInsertDeclaration( | |
| M, llvm::Intrinsic::experimental_convergence_entry); | |
| auto *Call = CallInst::Create(Fn, "", BB.getFirstInsertionPt()); | |
| return cast<ConvergenceControlInst>(Call); | |
| } | |
| ConvergenceControlInst * | |
| ConvergenceControlInst::CreateLoop(BasicBlock &BB, | |
| ConvergenceControlInst *ParentToken) { | |
| Module *M = BB.getModule(); | |
| Function *Fn = Intrinsic::getOrInsertDeclaration( | |
| M, llvm::Intrinsic::experimental_convergence_loop); | |
| llvm::Value *BundleArgs[] = {ParentToken}; | |
| llvm::OperandBundleDef OB("convergencectrl", BundleArgs); | |
| auto *Call = CallInst::Create(Fn, {}, {OB}, "", BB.getFirstInsertionPt()); | |
| return cast<ConvergenceControlInst>(Call); | |
| } |