
This introduces a new `ptrtoaddr` instruction which is similar to `ptrtoint` but has two differences: 1) Unlike `ptrtoint`, `ptrtoaddr` does not capture provenance 2) `ptrtoaddr` only extracts (and then extends/truncates) the low index-width bits of the pointer For most architectures, difference 2) does not matter since index (address) width and pointer representation width are the same, but this does make a difference for architectures that have pointers that aren't just plain integer addresses such as AMDGPU fat pointers or CHERI capabilities. This commit introduces textual and bitcode IR support as well as basic code generation, but optimization passes do not handle the new instruction yet so it may result in worse code than using ptrtoint. Follow-up changes will update capture tracking, etc. for the new instruction. RFC: https://discourse.llvm.org/t/clarifiying-the-semantics-of-ptrtoint/83987/54 Reviewed By: nikic Pull Request: https://github.com/llvm/llvm-project/pull/139357
4409 lines
155 KiB
C++
4409 lines
155 KiB
C++
//===-- ConstantFolding.cpp - Fold instructions into constants ------------===//
|
|
//
|
|
// 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 defines routines for folding instructions into constants.
|
|
//
|
|
// Also, to supplement the basic IR ConstantExpr simplifications,
|
|
// this file defines some additional folding routines that can make use of
|
|
// DataLayout information. These functions cannot go in IR due to library
|
|
// dependency issues.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Analysis/ConstantFolding.h"
|
|
#include "llvm/ADT/APFloat.h"
|
|
#include "llvm/ADT/APInt.h"
|
|
#include "llvm/ADT/APSInt.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Analysis/TargetFolder.h"
|
|
#include "llvm/Analysis/TargetLibraryInfo.h"
|
|
#include "llvm/Analysis/ValueTracking.h"
|
|
#include "llvm/Analysis/VectorUtils.h"
|
|
#include "llvm/Config/config.h"
|
|
#include "llvm/IR/Constant.h"
|
|
#include "llvm/IR/ConstantFold.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/DataLayout.h"
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/GlobalValue.h"
|
|
#include "llvm/IR/GlobalVariable.h"
|
|
#include "llvm/IR/InstrTypes.h"
|
|
#include "llvm/IR/Instruction.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/IntrinsicInst.h"
|
|
#include "llvm/IR/Intrinsics.h"
|
|
#include "llvm/IR/IntrinsicsAArch64.h"
|
|
#include "llvm/IR/IntrinsicsAMDGPU.h"
|
|
#include "llvm/IR/IntrinsicsARM.h"
|
|
#include "llvm/IR/IntrinsicsNVPTX.h"
|
|
#include "llvm/IR/IntrinsicsWebAssembly.h"
|
|
#include "llvm/IR/IntrinsicsX86.h"
|
|
#include "llvm/IR/NVVMIntrinsicUtils.h"
|
|
#include "llvm/IR/Operator.h"
|
|
#include "llvm/IR/Type.h"
|
|
#include "llvm/IR/Value.h"
|
|
#include "llvm/Support/Casting.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/KnownBits.h"
|
|
#include "llvm/Support/MathExtras.h"
|
|
#include <cassert>
|
|
#include <cerrno>
|
|
#include <cfenv>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
|
|
using namespace llvm;
|
|
|
|
static cl::opt<bool> DisableFPCallFolding(
|
|
"disable-fp-call-folding",
|
|
cl::desc("Disable constant-folding of FP intrinsics and libcalls."),
|
|
cl::init(false), cl::Hidden);
|
|
|
|
namespace {
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Constant Folding internal helper functions
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static Constant *foldConstVectorToAPInt(APInt &Result, Type *DestTy,
|
|
Constant *C, Type *SrcEltTy,
|
|
unsigned NumSrcElts,
|
|
const DataLayout &DL) {
|
|
// Now that we know that the input value is a vector of integers, just shift
|
|
// and insert them into our result.
|
|
unsigned BitShift = DL.getTypeSizeInBits(SrcEltTy);
|
|
for (unsigned i = 0; i != NumSrcElts; ++i) {
|
|
Constant *Element;
|
|
if (DL.isLittleEndian())
|
|
Element = C->getAggregateElement(NumSrcElts - i - 1);
|
|
else
|
|
Element = C->getAggregateElement(i);
|
|
|
|
if (isa_and_nonnull<UndefValue>(Element)) {
|
|
Result <<= BitShift;
|
|
continue;
|
|
}
|
|
|
|
auto *ElementCI = dyn_cast_or_null<ConstantInt>(Element);
|
|
if (!ElementCI)
|
|
return ConstantExpr::getBitCast(C, DestTy);
|
|
|
|
Result <<= BitShift;
|
|
Result |= ElementCI->getValue().zext(Result.getBitWidth());
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/// Constant fold bitcast, symbolically evaluating it with DataLayout.
|
|
/// This always returns a non-null constant, but it may be a
|
|
/// ConstantExpr if unfoldable.
|
|
Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
|
|
assert(CastInst::castIsValid(Instruction::BitCast, C, DestTy) &&
|
|
"Invalid constantexpr bitcast!");
|
|
|
|
// Catch the obvious splat cases.
|
|
if (Constant *Res = ConstantFoldLoadFromUniformValue(C, DestTy, DL))
|
|
return Res;
|
|
|
|
if (auto *VTy = dyn_cast<VectorType>(C->getType())) {
|
|
// Handle a vector->scalar integer/fp cast.
|
|
if (isa<IntegerType>(DestTy) || DestTy->isFloatingPointTy()) {
|
|
unsigned NumSrcElts = cast<FixedVectorType>(VTy)->getNumElements();
|
|
Type *SrcEltTy = VTy->getElementType();
|
|
|
|
// If the vector is a vector of floating point, convert it to vector of int
|
|
// to simplify things.
|
|
if (SrcEltTy->isFloatingPointTy()) {
|
|
unsigned FPWidth = SrcEltTy->getPrimitiveSizeInBits();
|
|
auto *SrcIVTy = FixedVectorType::get(
|
|
IntegerType::get(C->getContext(), FPWidth), NumSrcElts);
|
|
// Ask IR to do the conversion now that #elts line up.
|
|
C = ConstantExpr::getBitCast(C, SrcIVTy);
|
|
}
|
|
|
|
APInt Result(DL.getTypeSizeInBits(DestTy), 0);
|
|
if (Constant *CE = foldConstVectorToAPInt(Result, DestTy, C,
|
|
SrcEltTy, NumSrcElts, DL))
|
|
return CE;
|
|
|
|
if (isa<IntegerType>(DestTy))
|
|
return ConstantInt::get(DestTy, Result);
|
|
|
|
APFloat FP(DestTy->getFltSemantics(), Result);
|
|
return ConstantFP::get(DestTy->getContext(), FP);
|
|
}
|
|
}
|
|
|
|
// The code below only handles casts to vectors currently.
|
|
auto *DestVTy = dyn_cast<VectorType>(DestTy);
|
|
if (!DestVTy)
|
|
return ConstantExpr::getBitCast(C, DestTy);
|
|
|
|
// If this is a scalar -> vector cast, convert the input into a <1 x scalar>
|
|
// vector so the code below can handle it uniformly.
|
|
if (!isa<VectorType>(C->getType()) &&
|
|
(isa<ConstantFP>(C) || isa<ConstantInt>(C))) {
|
|
Constant *Ops = C; // don't take the address of C!
|
|
return FoldBitCast(ConstantVector::get(Ops), DestTy, DL);
|
|
}
|
|
|
|
// Some of what follows may extend to cover scalable vectors but the current
|
|
// implementation is fixed length specific.
|
|
if (!isa<FixedVectorType>(C->getType()))
|
|
return ConstantExpr::getBitCast(C, DestTy);
|
|
|
|
// If this is a bitcast from constant vector -> vector, fold it.
|
|
if (!isa<ConstantDataVector>(C) && !isa<ConstantVector>(C) &&
|
|
!isa<ConstantInt>(C) && !isa<ConstantFP>(C))
|
|
return ConstantExpr::getBitCast(C, DestTy);
|
|
|
|
// If the element types match, IR can fold it.
|
|
unsigned NumDstElt = cast<FixedVectorType>(DestVTy)->getNumElements();
|
|
unsigned NumSrcElt = cast<FixedVectorType>(C->getType())->getNumElements();
|
|
if (NumDstElt == NumSrcElt)
|
|
return ConstantExpr::getBitCast(C, DestTy);
|
|
|
|
Type *SrcEltTy = cast<VectorType>(C->getType())->getElementType();
|
|
Type *DstEltTy = DestVTy->getElementType();
|
|
|
|
// Otherwise, we're changing the number of elements in a vector, which
|
|
// requires endianness information to do the right thing. For example,
|
|
// bitcast (<2 x i64> <i64 0, i64 1> to <4 x i32>)
|
|
// folds to (little endian):
|
|
// <4 x i32> <i32 0, i32 0, i32 1, i32 0>
|
|
// and to (big endian):
|
|
// <4 x i32> <i32 0, i32 0, i32 0, i32 1>
|
|
|
|
// First thing is first. We only want to think about integer here, so if
|
|
// we have something in FP form, recast it as integer.
|
|
if (DstEltTy->isFloatingPointTy()) {
|
|
// Fold to an vector of integers with same size as our FP type.
|
|
unsigned FPWidth = DstEltTy->getPrimitiveSizeInBits();
|
|
auto *DestIVTy = FixedVectorType::get(
|
|
IntegerType::get(C->getContext(), FPWidth), NumDstElt);
|
|
// Recursively handle this integer conversion, if possible.
|
|
C = FoldBitCast(C, DestIVTy, DL);
|
|
|
|
// Finally, IR can handle this now that #elts line up.
|
|
return ConstantExpr::getBitCast(C, DestTy);
|
|
}
|
|
|
|
// Okay, we know the destination is integer, if the input is FP, convert
|
|
// it to integer first.
|
|
if (SrcEltTy->isFloatingPointTy()) {
|
|
unsigned FPWidth = SrcEltTy->getPrimitiveSizeInBits();
|
|
auto *SrcIVTy = FixedVectorType::get(
|
|
IntegerType::get(C->getContext(), FPWidth), NumSrcElt);
|
|
// Ask IR to do the conversion now that #elts line up.
|
|
C = ConstantExpr::getBitCast(C, SrcIVTy);
|
|
assert((isa<ConstantVector>(C) || // FIXME: Remove ConstantVector.
|
|
isa<ConstantDataVector>(C) || isa<ConstantInt>(C)) &&
|
|
"Constant folding cannot fail for plain fp->int bitcast!");
|
|
}
|
|
|
|
// Now we know that the input and output vectors are both integer vectors
|
|
// of the same size, and that their #elements is not the same. Do the
|
|
// conversion here, which depends on whether the input or output has
|
|
// more elements.
|
|
bool isLittleEndian = DL.isLittleEndian();
|
|
|
|
SmallVector<Constant*, 32> Result;
|
|
if (NumDstElt < NumSrcElt) {
|
|
// Handle: bitcast (<4 x i32> <i32 0, i32 1, i32 2, i32 3> to <2 x i64>)
|
|
Constant *Zero = Constant::getNullValue(DstEltTy);
|
|
unsigned Ratio = NumSrcElt/NumDstElt;
|
|
unsigned SrcBitSize = SrcEltTy->getPrimitiveSizeInBits();
|
|
unsigned SrcElt = 0;
|
|
for (unsigned i = 0; i != NumDstElt; ++i) {
|
|
// Build each element of the result.
|
|
Constant *Elt = Zero;
|
|
unsigned ShiftAmt = isLittleEndian ? 0 : SrcBitSize*(Ratio-1);
|
|
for (unsigned j = 0; j != Ratio; ++j) {
|
|
Constant *Src = C->getAggregateElement(SrcElt++);
|
|
if (isa_and_nonnull<UndefValue>(Src))
|
|
Src = Constant::getNullValue(
|
|
cast<VectorType>(C->getType())->getElementType());
|
|
else
|
|
Src = dyn_cast_or_null<ConstantInt>(Src);
|
|
if (!Src) // Reject constantexpr elements.
|
|
return ConstantExpr::getBitCast(C, DestTy);
|
|
|
|
// Zero extend the element to the right size.
|
|
Src = ConstantFoldCastOperand(Instruction::ZExt, Src, Elt->getType(),
|
|
DL);
|
|
assert(Src && "Constant folding cannot fail on plain integers");
|
|
|
|
// Shift it to the right place, depending on endianness.
|
|
Src = ConstantFoldBinaryOpOperands(
|
|
Instruction::Shl, Src, ConstantInt::get(Src->getType(), ShiftAmt),
|
|
DL);
|
|
assert(Src && "Constant folding cannot fail on plain integers");
|
|
|
|
ShiftAmt += isLittleEndian ? SrcBitSize : -SrcBitSize;
|
|
|
|
// Mix it in.
|
|
Elt = ConstantFoldBinaryOpOperands(Instruction::Or, Elt, Src, DL);
|
|
assert(Elt && "Constant folding cannot fail on plain integers");
|
|
}
|
|
Result.push_back(Elt);
|
|
}
|
|
return ConstantVector::get(Result);
|
|
}
|
|
|
|
// Handle: bitcast (<2 x i64> <i64 0, i64 1> to <4 x i32>)
|
|
unsigned Ratio = NumDstElt/NumSrcElt;
|
|
unsigned DstBitSize = DL.getTypeSizeInBits(DstEltTy);
|
|
|
|
// Loop over each source value, expanding into multiple results.
|
|
for (unsigned i = 0; i != NumSrcElt; ++i) {
|
|
auto *Element = C->getAggregateElement(i);
|
|
|
|
if (!Element) // Reject constantexpr elements.
|
|
return ConstantExpr::getBitCast(C, DestTy);
|
|
|
|
if (isa<UndefValue>(Element)) {
|
|
// Correctly Propagate undef values.
|
|
Result.append(Ratio, UndefValue::get(DstEltTy));
|
|
continue;
|
|
}
|
|
|
|
auto *Src = dyn_cast<ConstantInt>(Element);
|
|
if (!Src)
|
|
return ConstantExpr::getBitCast(C, DestTy);
|
|
|
|
unsigned ShiftAmt = isLittleEndian ? 0 : DstBitSize*(Ratio-1);
|
|
for (unsigned j = 0; j != Ratio; ++j) {
|
|
// Shift the piece of the value into the right place, depending on
|
|
// endianness.
|
|
APInt Elt = Src->getValue().lshr(ShiftAmt);
|
|
ShiftAmt += isLittleEndian ? DstBitSize : -DstBitSize;
|
|
|
|
// Truncate and remember this piece.
|
|
Result.push_back(ConstantInt::get(DstEltTy, Elt.trunc(DstBitSize)));
|
|
}
|
|
}
|
|
|
|
return ConstantVector::get(Result);
|
|
}
|
|
|
|
} // end anonymous namespace
|
|
|
|
/// If this constant is a constant offset from a global, return the global and
|
|
/// the constant. Because of constantexprs, this function is recursive.
|
|
bool llvm::IsConstantOffsetFromGlobal(Constant *C, GlobalValue *&GV,
|
|
APInt &Offset, const DataLayout &DL,
|
|
DSOLocalEquivalent **DSOEquiv) {
|
|
if (DSOEquiv)
|
|
*DSOEquiv = nullptr;
|
|
|
|
// Trivial case, constant is the global.
|
|
if ((GV = dyn_cast<GlobalValue>(C))) {
|
|
unsigned BitWidth = DL.getIndexTypeSizeInBits(GV->getType());
|
|
Offset = APInt(BitWidth, 0);
|
|
return true;
|
|
}
|
|
|
|
if (auto *FoundDSOEquiv = dyn_cast<DSOLocalEquivalent>(C)) {
|
|
if (DSOEquiv)
|
|
*DSOEquiv = FoundDSOEquiv;
|
|
GV = FoundDSOEquiv->getGlobalValue();
|
|
unsigned BitWidth = DL.getIndexTypeSizeInBits(GV->getType());
|
|
Offset = APInt(BitWidth, 0);
|
|
return true;
|
|
}
|
|
|
|
// Otherwise, if this isn't a constant expr, bail out.
|
|
auto *CE = dyn_cast<ConstantExpr>(C);
|
|
if (!CE) return false;
|
|
|
|
// Look through ptr->int and ptr->ptr casts.
|
|
if (CE->getOpcode() == Instruction::PtrToInt ||
|
|
CE->getOpcode() == Instruction::BitCast)
|
|
return IsConstantOffsetFromGlobal(CE->getOperand(0), GV, Offset, DL,
|
|
DSOEquiv);
|
|
|
|
// i32* getelementptr ([5 x i32]* @a, i32 0, i32 5)
|
|
auto *GEP = dyn_cast<GEPOperator>(CE);
|
|
if (!GEP)
|
|
return false;
|
|
|
|
unsigned BitWidth = DL.getIndexTypeSizeInBits(GEP->getType());
|
|
APInt TmpOffset(BitWidth, 0);
|
|
|
|
// If the base isn't a global+constant, we aren't either.
|
|
if (!IsConstantOffsetFromGlobal(CE->getOperand(0), GV, TmpOffset, DL,
|
|
DSOEquiv))
|
|
return false;
|
|
|
|
// Otherwise, add any offset that our operands provide.
|
|
if (!GEP->accumulateConstantOffset(DL, TmpOffset))
|
|
return false;
|
|
|
|
Offset = TmpOffset;
|
|
return true;
|
|
}
|
|
|
|
Constant *llvm::ConstantFoldLoadThroughBitcast(Constant *C, Type *DestTy,
|
|
const DataLayout &DL) {
|
|
do {
|
|
Type *SrcTy = C->getType();
|
|
if (SrcTy == DestTy)
|
|
return C;
|
|
|
|
TypeSize DestSize = DL.getTypeSizeInBits(DestTy);
|
|
TypeSize SrcSize = DL.getTypeSizeInBits(SrcTy);
|
|
if (!TypeSize::isKnownGE(SrcSize, DestSize))
|
|
return nullptr;
|
|
|
|
// Catch the obvious splat cases (since all-zeros can coerce non-integral
|
|
// pointers legally).
|
|
if (Constant *Res = ConstantFoldLoadFromUniformValue(C, DestTy, DL))
|
|
return Res;
|
|
|
|
// If the type sizes are the same and a cast is legal, just directly
|
|
// cast the constant.
|
|
// But be careful not to coerce non-integral pointers illegally.
|
|
if (SrcSize == DestSize &&
|
|
DL.isNonIntegralPointerType(SrcTy->getScalarType()) ==
|
|
DL.isNonIntegralPointerType(DestTy->getScalarType())) {
|
|
Instruction::CastOps Cast = Instruction::BitCast;
|
|
// If we are going from a pointer to int or vice versa, we spell the cast
|
|
// differently.
|
|
if (SrcTy->isIntegerTy() && DestTy->isPointerTy())
|
|
Cast = Instruction::IntToPtr;
|
|
else if (SrcTy->isPointerTy() && DestTy->isIntegerTy())
|
|
Cast = Instruction::PtrToInt;
|
|
|
|
if (CastInst::castIsValid(Cast, C, DestTy))
|
|
return ConstantFoldCastOperand(Cast, C, DestTy, DL);
|
|
}
|
|
|
|
// If this isn't an aggregate type, there is nothing we can do to drill down
|
|
// and find a bitcastable constant.
|
|
if (!SrcTy->isAggregateType() && !SrcTy->isVectorTy())
|
|
return nullptr;
|
|
|
|
// We're simulating a load through a pointer that was bitcast to point to
|
|
// a different type, so we can try to walk down through the initial
|
|
// elements of an aggregate to see if some part of the aggregate is
|
|
// castable to implement the "load" semantic model.
|
|
if (SrcTy->isStructTy()) {
|
|
// Struct types might have leading zero-length elements like [0 x i32],
|
|
// which are certainly not what we are looking for, so skip them.
|
|
unsigned Elem = 0;
|
|
Constant *ElemC;
|
|
do {
|
|
ElemC = C->getAggregateElement(Elem++);
|
|
} while (ElemC && DL.getTypeSizeInBits(ElemC->getType()).isZero());
|
|
C = ElemC;
|
|
} else {
|
|
// For non-byte-sized vector elements, the first element is not
|
|
// necessarily located at the vector base address.
|
|
if (auto *VT = dyn_cast<VectorType>(SrcTy))
|
|
if (!DL.typeSizeEqualsStoreSize(VT->getElementType()))
|
|
return nullptr;
|
|
|
|
C = C->getAggregateElement(0u);
|
|
}
|
|
} while (C);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// Recursive helper to read bits out of global. C is the constant being copied
|
|
/// out of. ByteOffset is an offset into C. CurPtr is the pointer to copy
|
|
/// results into and BytesLeft is the number of bytes left in
|
|
/// the CurPtr buffer. DL is the DataLayout.
|
|
bool ReadDataFromGlobal(Constant *C, uint64_t ByteOffset, unsigned char *CurPtr,
|
|
unsigned BytesLeft, const DataLayout &DL) {
|
|
assert(ByteOffset <= DL.getTypeAllocSize(C->getType()) &&
|
|
"Out of range access");
|
|
|
|
// Reading type padding, return zero.
|
|
if (ByteOffset >= DL.getTypeStoreSize(C->getType()))
|
|
return true;
|
|
|
|
// If this element is zero or undefined, we can just return since *CurPtr is
|
|
// zero initialized.
|
|
if (isa<ConstantAggregateZero>(C) || isa<UndefValue>(C))
|
|
return true;
|
|
|
|
if (auto *CI = dyn_cast<ConstantInt>(C)) {
|
|
if ((CI->getBitWidth() & 7) != 0)
|
|
return false;
|
|
const APInt &Val = CI->getValue();
|
|
unsigned IntBytes = unsigned(CI->getBitWidth()/8);
|
|
|
|
for (unsigned i = 0; i != BytesLeft && ByteOffset != IntBytes; ++i) {
|
|
unsigned n = ByteOffset;
|
|
if (!DL.isLittleEndian())
|
|
n = IntBytes - n - 1;
|
|
CurPtr[i] = Val.extractBits(8, n * 8).getZExtValue();
|
|
++ByteOffset;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (auto *CFP = dyn_cast<ConstantFP>(C)) {
|
|
if (CFP->getType()->isDoubleTy()) {
|
|
C = FoldBitCast(C, Type::getInt64Ty(C->getContext()), DL);
|
|
return ReadDataFromGlobal(C, ByteOffset, CurPtr, BytesLeft, DL);
|
|
}
|
|
if (CFP->getType()->isFloatTy()){
|
|
C = FoldBitCast(C, Type::getInt32Ty(C->getContext()), DL);
|
|
return ReadDataFromGlobal(C, ByteOffset, CurPtr, BytesLeft, DL);
|
|
}
|
|
if (CFP->getType()->isHalfTy()){
|
|
C = FoldBitCast(C, Type::getInt16Ty(C->getContext()), DL);
|
|
return ReadDataFromGlobal(C, ByteOffset, CurPtr, BytesLeft, DL);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (auto *CS = dyn_cast<ConstantStruct>(C)) {
|
|
const StructLayout *SL = DL.getStructLayout(CS->getType());
|
|
unsigned Index = SL->getElementContainingOffset(ByteOffset);
|
|
uint64_t CurEltOffset = SL->getElementOffset(Index);
|
|
ByteOffset -= CurEltOffset;
|
|
|
|
while (true) {
|
|
// If the element access is to the element itself and not to tail padding,
|
|
// read the bytes from the element.
|
|
uint64_t EltSize = DL.getTypeAllocSize(CS->getOperand(Index)->getType());
|
|
|
|
if (ByteOffset < EltSize &&
|
|
!ReadDataFromGlobal(CS->getOperand(Index), ByteOffset, CurPtr,
|
|
BytesLeft, DL))
|
|
return false;
|
|
|
|
++Index;
|
|
|
|
// Check to see if we read from the last struct element, if so we're done.
|
|
if (Index == CS->getType()->getNumElements())
|
|
return true;
|
|
|
|
// If we read all of the bytes we needed from this element we're done.
|
|
uint64_t NextEltOffset = SL->getElementOffset(Index);
|
|
|
|
if (BytesLeft <= NextEltOffset - CurEltOffset - ByteOffset)
|
|
return true;
|
|
|
|
// Move to the next element of the struct.
|
|
CurPtr += NextEltOffset - CurEltOffset - ByteOffset;
|
|
BytesLeft -= NextEltOffset - CurEltOffset - ByteOffset;
|
|
ByteOffset = 0;
|
|
CurEltOffset = NextEltOffset;
|
|
}
|
|
// not reached.
|
|
}
|
|
|
|
if (isa<ConstantArray>(C) || isa<ConstantVector>(C) ||
|
|
isa<ConstantDataSequential>(C)) {
|
|
uint64_t NumElts, EltSize;
|
|
Type *EltTy;
|
|
if (auto *AT = dyn_cast<ArrayType>(C->getType())) {
|
|
NumElts = AT->getNumElements();
|
|
EltTy = AT->getElementType();
|
|
EltSize = DL.getTypeAllocSize(EltTy);
|
|
} else {
|
|
NumElts = cast<FixedVectorType>(C->getType())->getNumElements();
|
|
EltTy = cast<FixedVectorType>(C->getType())->getElementType();
|
|
// TODO: For non-byte-sized vectors, current implementation assumes there is
|
|
// padding to the next byte boundary between elements.
|
|
if (!DL.typeSizeEqualsStoreSize(EltTy))
|
|
return false;
|
|
|
|
EltSize = DL.getTypeStoreSize(EltTy);
|
|
}
|
|
uint64_t Index = ByteOffset / EltSize;
|
|
uint64_t Offset = ByteOffset - Index * EltSize;
|
|
|
|
for (; Index != NumElts; ++Index) {
|
|
if (!ReadDataFromGlobal(C->getAggregateElement(Index), Offset, CurPtr,
|
|
BytesLeft, DL))
|
|
return false;
|
|
|
|
uint64_t BytesWritten = EltSize - Offset;
|
|
assert(BytesWritten <= EltSize && "Not indexing into this element?");
|
|
if (BytesWritten >= BytesLeft)
|
|
return true;
|
|
|
|
Offset = 0;
|
|
BytesLeft -= BytesWritten;
|
|
CurPtr += BytesWritten;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (auto *CE = dyn_cast<ConstantExpr>(C)) {
|
|
if (CE->getOpcode() == Instruction::IntToPtr &&
|
|
CE->getOperand(0)->getType() == DL.getIntPtrType(CE->getType())) {
|
|
return ReadDataFromGlobal(CE->getOperand(0), ByteOffset, CurPtr,
|
|
BytesLeft, DL);
|
|
}
|
|
}
|
|
|
|
// Otherwise, unknown initializer type.
|
|
return false;
|
|
}
|
|
|
|
Constant *FoldReinterpretLoadFromConst(Constant *C, Type *LoadTy,
|
|
int64_t Offset, const DataLayout &DL) {
|
|
// Bail out early. Not expect to load from scalable global variable.
|
|
if (isa<ScalableVectorType>(LoadTy))
|
|
return nullptr;
|
|
|
|
auto *IntType = dyn_cast<IntegerType>(LoadTy);
|
|
|
|
// If this isn't an integer load we can't fold it directly.
|
|
if (!IntType) {
|
|
// If this is a non-integer load, we can try folding it as an int load and
|
|
// then bitcast the result. This can be useful for union cases. Note
|
|
// that address spaces don't matter here since we're not going to result in
|
|
// an actual new load.
|
|
if (!LoadTy->isFloatingPointTy() && !LoadTy->isPointerTy() &&
|
|
!LoadTy->isVectorTy())
|
|
return nullptr;
|
|
|
|
Type *MapTy = Type::getIntNTy(C->getContext(),
|
|
DL.getTypeSizeInBits(LoadTy).getFixedValue());
|
|
if (Constant *Res = FoldReinterpretLoadFromConst(C, MapTy, Offset, DL)) {
|
|
if (Res->isNullValue() && !LoadTy->isX86_AMXTy())
|
|
// Materializing a zero can be done trivially without a bitcast
|
|
return Constant::getNullValue(LoadTy);
|
|
Type *CastTy = LoadTy->isPtrOrPtrVectorTy() ? DL.getIntPtrType(LoadTy) : LoadTy;
|
|
Res = FoldBitCast(Res, CastTy, DL);
|
|
if (LoadTy->isPtrOrPtrVectorTy()) {
|
|
// For vector of pointer, we needed to first convert to a vector of integer, then do vector inttoptr
|
|
if (Res->isNullValue() && !LoadTy->isX86_AMXTy())
|
|
return Constant::getNullValue(LoadTy);
|
|
if (DL.isNonIntegralPointerType(LoadTy->getScalarType()))
|
|
// Be careful not to replace a load of an addrspace value with an inttoptr here
|
|
return nullptr;
|
|
Res = ConstantExpr::getIntToPtr(Res, LoadTy);
|
|
}
|
|
return Res;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
unsigned BytesLoaded = (IntType->getBitWidth() + 7) / 8;
|
|
if (BytesLoaded > 32 || BytesLoaded == 0)
|
|
return nullptr;
|
|
|
|
// If we're not accessing anything in this constant, the result is undefined.
|
|
if (Offset <= -1 * static_cast<int64_t>(BytesLoaded))
|
|
return PoisonValue::get(IntType);
|
|
|
|
// TODO: We should be able to support scalable types.
|
|
TypeSize InitializerSize = DL.getTypeAllocSize(C->getType());
|
|
if (InitializerSize.isScalable())
|
|
return nullptr;
|
|
|
|
// If we're not accessing anything in this constant, the result is undefined.
|
|
if (Offset >= (int64_t)InitializerSize.getFixedValue())
|
|
return PoisonValue::get(IntType);
|
|
|
|
unsigned char RawBytes[32] = {0};
|
|
unsigned char *CurPtr = RawBytes;
|
|
unsigned BytesLeft = BytesLoaded;
|
|
|
|
// If we're loading off the beginning of the global, some bytes may be valid.
|
|
if (Offset < 0) {
|
|
CurPtr += -Offset;
|
|
BytesLeft += Offset;
|
|
Offset = 0;
|
|
}
|
|
|
|
if (!ReadDataFromGlobal(C, Offset, CurPtr, BytesLeft, DL))
|
|
return nullptr;
|
|
|
|
APInt ResultVal = APInt(IntType->getBitWidth(), 0);
|
|
if (DL.isLittleEndian()) {
|
|
ResultVal = RawBytes[BytesLoaded - 1];
|
|
for (unsigned i = 1; i != BytesLoaded; ++i) {
|
|
ResultVal <<= 8;
|
|
ResultVal |= RawBytes[BytesLoaded - 1 - i];
|
|
}
|
|
} else {
|
|
ResultVal = RawBytes[0];
|
|
for (unsigned i = 1; i != BytesLoaded; ++i) {
|
|
ResultVal <<= 8;
|
|
ResultVal |= RawBytes[i];
|
|
}
|
|
}
|
|
|
|
return ConstantInt::get(IntType->getContext(), ResultVal);
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
// If GV is a constant with an initializer read its representation starting
|
|
// at Offset and return it as a constant array of unsigned char. Otherwise
|
|
// return null.
|
|
Constant *llvm::ReadByteArrayFromGlobal(const GlobalVariable *GV,
|
|
uint64_t Offset) {
|
|
if (!GV->isConstant() || !GV->hasDefinitiveInitializer())
|
|
return nullptr;
|
|
|
|
const DataLayout &DL = GV->getDataLayout();
|
|
Constant *Init = const_cast<Constant *>(GV->getInitializer());
|
|
TypeSize InitSize = DL.getTypeAllocSize(Init->getType());
|
|
if (InitSize < Offset)
|
|
return nullptr;
|
|
|
|
uint64_t NBytes = InitSize - Offset;
|
|
if (NBytes > UINT16_MAX)
|
|
// Bail for large initializers in excess of 64K to avoid allocating
|
|
// too much memory.
|
|
// Offset is assumed to be less than or equal than InitSize (this
|
|
// is enforced in ReadDataFromGlobal).
|
|
return nullptr;
|
|
|
|
SmallVector<unsigned char, 256> RawBytes(static_cast<size_t>(NBytes));
|
|
unsigned char *CurPtr = RawBytes.data();
|
|
|
|
if (!ReadDataFromGlobal(Init, Offset, CurPtr, NBytes, DL))
|
|
return nullptr;
|
|
|
|
return ConstantDataArray::get(GV->getContext(), RawBytes);
|
|
}
|
|
|
|
/// If this Offset points exactly to the start of an aggregate element, return
|
|
/// that element, otherwise return nullptr.
|
|
Constant *getConstantAtOffset(Constant *Base, APInt Offset,
|
|
const DataLayout &DL) {
|
|
if (Offset.isZero())
|
|
return Base;
|
|
|
|
if (!isa<ConstantAggregate>(Base) && !isa<ConstantDataSequential>(Base))
|
|
return nullptr;
|
|
|
|
Type *ElemTy = Base->getType();
|
|
SmallVector<APInt> Indices = DL.getGEPIndicesForOffset(ElemTy, Offset);
|
|
if (!Offset.isZero() || !Indices[0].isZero())
|
|
return nullptr;
|
|
|
|
Constant *C = Base;
|
|
for (const APInt &Index : drop_begin(Indices)) {
|
|
if (Index.isNegative() || Index.getActiveBits() >= 32)
|
|
return nullptr;
|
|
|
|
C = C->getAggregateElement(Index.getZExtValue());
|
|
if (!C)
|
|
return nullptr;
|
|
}
|
|
|
|
return C;
|
|
}
|
|
|
|
Constant *llvm::ConstantFoldLoadFromConst(Constant *C, Type *Ty,
|
|
const APInt &Offset,
|
|
const DataLayout &DL) {
|
|
if (Constant *AtOffset = getConstantAtOffset(C, Offset, DL))
|
|
if (Constant *Result = ConstantFoldLoadThroughBitcast(AtOffset, Ty, DL))
|
|
return Result;
|
|
|
|
// Explicitly check for out-of-bounds access, so we return poison even if the
|
|
// constant is a uniform value.
|
|
TypeSize Size = DL.getTypeAllocSize(C->getType());
|
|
if (!Size.isScalable() && Offset.sge(Size.getFixedValue()))
|
|
return PoisonValue::get(Ty);
|
|
|
|
// Try an offset-independent fold of a uniform value.
|
|
if (Constant *Result = ConstantFoldLoadFromUniformValue(C, Ty, DL))
|
|
return Result;
|
|
|
|
// Try hard to fold loads from bitcasted strange and non-type-safe things.
|
|
if (Offset.getSignificantBits() <= 64)
|
|
if (Constant *Result =
|
|
FoldReinterpretLoadFromConst(C, Ty, Offset.getSExtValue(), DL))
|
|
return Result;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Constant *llvm::ConstantFoldLoadFromConst(Constant *C, Type *Ty,
|
|
const DataLayout &DL) {
|
|
return ConstantFoldLoadFromConst(C, Ty, APInt(64, 0), DL);
|
|
}
|
|
|
|
Constant *llvm::ConstantFoldLoadFromConstPtr(Constant *C, Type *Ty,
|
|
APInt Offset,
|
|
const DataLayout &DL) {
|
|
// We can only fold loads from constant globals with a definitive initializer.
|
|
// Check this upfront, to skip expensive offset calculations.
|
|
auto *GV = dyn_cast<GlobalVariable>(getUnderlyingObject(C));
|
|
if (!GV || !GV->isConstant() || !GV->hasDefinitiveInitializer())
|
|
return nullptr;
|
|
|
|
C = cast<Constant>(C->stripAndAccumulateConstantOffsets(
|
|
DL, Offset, /* AllowNonInbounds */ true));
|
|
|
|
if (C == GV)
|
|
if (Constant *Result = ConstantFoldLoadFromConst(GV->getInitializer(), Ty,
|
|
Offset, DL))
|
|
return Result;
|
|
|
|
// If this load comes from anywhere in a uniform constant global, the value
|
|
// is always the same, regardless of the loaded offset.
|
|
return ConstantFoldLoadFromUniformValue(GV->getInitializer(), Ty, DL);
|
|
}
|
|
|
|
Constant *llvm::ConstantFoldLoadFromConstPtr(Constant *C, Type *Ty,
|
|
const DataLayout &DL) {
|
|
APInt Offset(DL.getIndexTypeSizeInBits(C->getType()), 0);
|
|
return ConstantFoldLoadFromConstPtr(C, Ty, std::move(Offset), DL);
|
|
}
|
|
|
|
Constant *llvm::ConstantFoldLoadFromUniformValue(Constant *C, Type *Ty,
|
|
const DataLayout &DL) {
|
|
if (isa<PoisonValue>(C))
|
|
return PoisonValue::get(Ty);
|
|
if (isa<UndefValue>(C))
|
|
return UndefValue::get(Ty);
|
|
// If padding is needed when storing C to memory, then it isn't considered as
|
|
// uniform.
|
|
if (!DL.typeSizeEqualsStoreSize(C->getType()))
|
|
return nullptr;
|
|
if (C->isNullValue() && !Ty->isX86_AMXTy())
|
|
return Constant::getNullValue(Ty);
|
|
if (C->isAllOnesValue() &&
|
|
(Ty->isIntOrIntVectorTy() || Ty->isFPOrFPVectorTy()))
|
|
return Constant::getAllOnesValue(Ty);
|
|
return nullptr;
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// One of Op0/Op1 is a constant expression.
|
|
/// Attempt to symbolically evaluate the result of a binary operator merging
|
|
/// these together. If target data info is available, it is provided as DL,
|
|
/// otherwise DL is null.
|
|
Constant *SymbolicallyEvaluateBinop(unsigned Opc, Constant *Op0, Constant *Op1,
|
|
const DataLayout &DL) {
|
|
// SROA
|
|
|
|
// Fold (and 0xffffffff00000000, (shl x, 32)) -> shl.
|
|
// Fold (lshr (or X, Y), 32) -> (lshr [X/Y], 32) if one doesn't contribute
|
|
// bits.
|
|
|
|
if (Opc == Instruction::And) {
|
|
KnownBits Known0 = computeKnownBits(Op0, DL);
|
|
KnownBits Known1 = computeKnownBits(Op1, DL);
|
|
if ((Known1.One | Known0.Zero).isAllOnes()) {
|
|
// All the bits of Op0 that the 'and' could be masking are already zero.
|
|
return Op0;
|
|
}
|
|
if ((Known0.One | Known1.Zero).isAllOnes()) {
|
|
// All the bits of Op1 that the 'and' could be masking are already zero.
|
|
return Op1;
|
|
}
|
|
|
|
Known0 &= Known1;
|
|
if (Known0.isConstant())
|
|
return ConstantInt::get(Op0->getType(), Known0.getConstant());
|
|
}
|
|
|
|
// If the constant expr is something like &A[123] - &A[4].f, fold this into a
|
|
// constant. This happens frequently when iterating over a global array.
|
|
if (Opc == Instruction::Sub) {
|
|
GlobalValue *GV1, *GV2;
|
|
APInt Offs1, Offs2;
|
|
|
|
if (IsConstantOffsetFromGlobal(Op0, GV1, Offs1, DL))
|
|
if (IsConstantOffsetFromGlobal(Op1, GV2, Offs2, DL) && GV1 == GV2) {
|
|
unsigned OpSize = DL.getTypeSizeInBits(Op0->getType());
|
|
|
|
// (&GV+C1) - (&GV+C2) -> C1-C2, pointer arithmetic cannot overflow.
|
|
// PtrToInt may change the bitwidth so we have convert to the right size
|
|
// first.
|
|
return ConstantInt::get(Op0->getType(), Offs1.zextOrTrunc(OpSize) -
|
|
Offs2.zextOrTrunc(OpSize));
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/// If array indices are not pointer-sized integers, explicitly cast them so
|
|
/// that they aren't implicitly casted by the getelementptr.
|
|
Constant *CastGEPIndices(Type *SrcElemTy, ArrayRef<Constant *> Ops,
|
|
Type *ResultTy, GEPNoWrapFlags NW,
|
|
std::optional<ConstantRange> InRange,
|
|
const DataLayout &DL, const TargetLibraryInfo *TLI) {
|
|
Type *IntIdxTy = DL.getIndexType(ResultTy);
|
|
Type *IntIdxScalarTy = IntIdxTy->getScalarType();
|
|
|
|
bool Any = false;
|
|
SmallVector<Constant*, 32> NewIdxs;
|
|
for (unsigned i = 1, e = Ops.size(); i != e; ++i) {
|
|
if ((i == 1 ||
|
|
!isa<StructType>(GetElementPtrInst::getIndexedType(
|
|
SrcElemTy, Ops.slice(1, i - 1)))) &&
|
|
Ops[i]->getType()->getScalarType() != IntIdxScalarTy) {
|
|
Any = true;
|
|
Type *NewType =
|
|
Ops[i]->getType()->isVectorTy() ? IntIdxTy : IntIdxScalarTy;
|
|
Constant *NewIdx = ConstantFoldCastOperand(
|
|
CastInst::getCastOpcode(Ops[i], true, NewType, true), Ops[i], NewType,
|
|
DL);
|
|
if (!NewIdx)
|
|
return nullptr;
|
|
NewIdxs.push_back(NewIdx);
|
|
} else
|
|
NewIdxs.push_back(Ops[i]);
|
|
}
|
|
|
|
if (!Any)
|
|
return nullptr;
|
|
|
|
Constant *C =
|
|
ConstantExpr::getGetElementPtr(SrcElemTy, Ops[0], NewIdxs, NW, InRange);
|
|
return ConstantFoldConstant(C, DL, TLI);
|
|
}
|
|
|
|
/// If we can symbolically evaluate the GEP constant expression, do so.
|
|
Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP,
|
|
ArrayRef<Constant *> Ops,
|
|
const DataLayout &DL,
|
|
const TargetLibraryInfo *TLI) {
|
|
Type *SrcElemTy = GEP->getSourceElementType();
|
|
Type *ResTy = GEP->getType();
|
|
if (!SrcElemTy->isSized() || isa<ScalableVectorType>(SrcElemTy))
|
|
return nullptr;
|
|
|
|
if (Constant *C = CastGEPIndices(SrcElemTy, Ops, ResTy, GEP->getNoWrapFlags(),
|
|
GEP->getInRange(), DL, TLI))
|
|
return C;
|
|
|
|
Constant *Ptr = Ops[0];
|
|
if (!Ptr->getType()->isPointerTy())
|
|
return nullptr;
|
|
|
|
Type *IntIdxTy = DL.getIndexType(Ptr->getType());
|
|
|
|
for (unsigned i = 1, e = Ops.size(); i != e; ++i)
|
|
if (!isa<ConstantInt>(Ops[i]) || !Ops[i]->getType()->isIntegerTy())
|
|
return nullptr;
|
|
|
|
unsigned BitWidth = DL.getTypeSizeInBits(IntIdxTy);
|
|
APInt Offset = APInt(
|
|
BitWidth,
|
|
DL.getIndexedOffsetInType(
|
|
SrcElemTy, ArrayRef((Value *const *)Ops.data() + 1, Ops.size() - 1)),
|
|
/*isSigned=*/true, /*implicitTrunc=*/true);
|
|
|
|
std::optional<ConstantRange> InRange = GEP->getInRange();
|
|
if (InRange)
|
|
InRange = InRange->sextOrTrunc(BitWidth);
|
|
|
|
// If this is a GEP of a GEP, fold it all into a single GEP.
|
|
GEPNoWrapFlags NW = GEP->getNoWrapFlags();
|
|
bool Overflow = false;
|
|
while (auto *GEP = dyn_cast<GEPOperator>(Ptr)) {
|
|
NW &= GEP->getNoWrapFlags();
|
|
|
|
SmallVector<Value *, 4> NestedOps(llvm::drop_begin(GEP->operands()));
|
|
|
|
// Do not try the incorporate the sub-GEP if some index is not a number.
|
|
bool AllConstantInt = true;
|
|
for (Value *NestedOp : NestedOps)
|
|
if (!isa<ConstantInt>(NestedOp)) {
|
|
AllConstantInt = false;
|
|
break;
|
|
}
|
|
if (!AllConstantInt)
|
|
break;
|
|
|
|
// Adjust inrange offset and intersect inrange attributes
|
|
if (auto GEPRange = GEP->getInRange()) {
|
|
auto AdjustedGEPRange = GEPRange->sextOrTrunc(BitWidth).subtract(Offset);
|
|
InRange =
|
|
InRange ? InRange->intersectWith(AdjustedGEPRange) : AdjustedGEPRange;
|
|
}
|
|
|
|
Ptr = cast<Constant>(GEP->getOperand(0));
|
|
SrcElemTy = GEP->getSourceElementType();
|
|
Offset = Offset.sadd_ov(
|
|
APInt(BitWidth, DL.getIndexedOffsetInType(SrcElemTy, NestedOps),
|
|
/*isSigned=*/true, /*implicitTrunc=*/true),
|
|
Overflow);
|
|
}
|
|
|
|
// Preserving nusw (without inbounds) also requires that the offset
|
|
// additions did not overflow.
|
|
if (NW.hasNoUnsignedSignedWrap() && !NW.isInBounds() && Overflow)
|
|
NW = NW.withoutNoUnsignedSignedWrap();
|
|
|
|
// If the base value for this address is a literal integer value, fold the
|
|
// getelementptr to the resulting integer value casted to the pointer type.
|
|
APInt BasePtr(DL.getPointerTypeSizeInBits(Ptr->getType()), 0);
|
|
if (auto *CE = dyn_cast<ConstantExpr>(Ptr)) {
|
|
if (CE->getOpcode() == Instruction::IntToPtr) {
|
|
if (auto *Base = dyn_cast<ConstantInt>(CE->getOperand(0)))
|
|
BasePtr = Base->getValue().zextOrTrunc(BasePtr.getBitWidth());
|
|
}
|
|
}
|
|
|
|
auto *PTy = cast<PointerType>(Ptr->getType());
|
|
if ((Ptr->isNullValue() || BasePtr != 0) &&
|
|
!DL.isNonIntegralPointerType(PTy)) {
|
|
// If the index size is smaller than the pointer size, add to the low
|
|
// bits only.
|
|
BasePtr.insertBits(BasePtr.trunc(BitWidth) + Offset, 0);
|
|
Constant *C = ConstantInt::get(Ptr->getContext(), BasePtr);
|
|
return ConstantExpr::getIntToPtr(C, ResTy);
|
|
}
|
|
|
|
// Try to infer inbounds for GEPs of globals.
|
|
if (!NW.isInBounds() && Offset.isNonNegative()) {
|
|
bool CanBeNull, CanBeFreed;
|
|
uint64_t DerefBytes =
|
|
Ptr->getPointerDereferenceableBytes(DL, CanBeNull, CanBeFreed);
|
|
if (DerefBytes != 0 && !CanBeNull && Offset.sle(DerefBytes))
|
|
NW |= GEPNoWrapFlags::inBounds();
|
|
}
|
|
|
|
// nusw + nneg -> nuw
|
|
if (NW.hasNoUnsignedSignedWrap() && Offset.isNonNegative())
|
|
NW |= GEPNoWrapFlags::noUnsignedWrap();
|
|
|
|
// Otherwise canonicalize this to a single ptradd.
|
|
LLVMContext &Ctx = Ptr->getContext();
|
|
return ConstantExpr::getGetElementPtr(Type::getInt8Ty(Ctx), Ptr,
|
|
ConstantInt::get(Ctx, Offset), NW,
|
|
InRange);
|
|
}
|
|
|
|
/// Attempt to constant fold an instruction with the
|
|
/// specified opcode and operands. If successful, the constant result is
|
|
/// returned, if not, null is returned. Note that this function can fail when
|
|
/// attempting to fold instructions like loads and stores, which have no
|
|
/// constant expression form.
|
|
Constant *ConstantFoldInstOperandsImpl(const Value *InstOrCE, unsigned Opcode,
|
|
ArrayRef<Constant *> Ops,
|
|
const DataLayout &DL,
|
|
const TargetLibraryInfo *TLI,
|
|
bool AllowNonDeterministic) {
|
|
Type *DestTy = InstOrCE->getType();
|
|
|
|
if (Instruction::isUnaryOp(Opcode))
|
|
return ConstantFoldUnaryOpOperand(Opcode, Ops[0], DL);
|
|
|
|
if (Instruction::isBinaryOp(Opcode)) {
|
|
switch (Opcode) {
|
|
default:
|
|
break;
|
|
case Instruction::FAdd:
|
|
case Instruction::FSub:
|
|
case Instruction::FMul:
|
|
case Instruction::FDiv:
|
|
case Instruction::FRem:
|
|
// Handle floating point instructions separately to account for denormals
|
|
// TODO: If a constant expression is being folded rather than an
|
|
// instruction, denormals will not be flushed/treated as zero
|
|
if (const auto *I = dyn_cast<Instruction>(InstOrCE)) {
|
|
return ConstantFoldFPInstOperands(Opcode, Ops[0], Ops[1], DL, I,
|
|
AllowNonDeterministic);
|
|
}
|
|
}
|
|
return ConstantFoldBinaryOpOperands(Opcode, Ops[0], Ops[1], DL);
|
|
}
|
|
|
|
if (Instruction::isCast(Opcode))
|
|
return ConstantFoldCastOperand(Opcode, Ops[0], DestTy, DL);
|
|
|
|
if (auto *GEP = dyn_cast<GEPOperator>(InstOrCE)) {
|
|
Type *SrcElemTy = GEP->getSourceElementType();
|
|
if (!ConstantExpr::isSupportedGetElementPtr(SrcElemTy))
|
|
return nullptr;
|
|
|
|
if (Constant *C = SymbolicallyEvaluateGEP(GEP, Ops, DL, TLI))
|
|
return C;
|
|
|
|
return ConstantExpr::getGetElementPtr(SrcElemTy, Ops[0], Ops.slice(1),
|
|
GEP->getNoWrapFlags(),
|
|
GEP->getInRange());
|
|
}
|
|
|
|
if (auto *CE = dyn_cast<ConstantExpr>(InstOrCE))
|
|
return CE->getWithOperands(Ops);
|
|
|
|
switch (Opcode) {
|
|
default: return nullptr;
|
|
case Instruction::ICmp:
|
|
case Instruction::FCmp: {
|
|
auto *C = cast<CmpInst>(InstOrCE);
|
|
return ConstantFoldCompareInstOperands(C->getPredicate(), Ops[0], Ops[1],
|
|
DL, TLI, C);
|
|
}
|
|
case Instruction::Freeze:
|
|
return isGuaranteedNotToBeUndefOrPoison(Ops[0]) ? Ops[0] : nullptr;
|
|
case Instruction::Call:
|
|
if (auto *F = dyn_cast<Function>(Ops.back())) {
|
|
const auto *Call = cast<CallBase>(InstOrCE);
|
|
if (canConstantFoldCallTo(Call, F))
|
|
return ConstantFoldCall(Call, F, Ops.slice(0, Ops.size() - 1), TLI,
|
|
AllowNonDeterministic);
|
|
}
|
|
return nullptr;
|
|
case Instruction::Select:
|
|
return ConstantFoldSelectInstruction(Ops[0], Ops[1], Ops[2]);
|
|
case Instruction::ExtractElement:
|
|
return ConstantExpr::getExtractElement(Ops[0], Ops[1]);
|
|
case Instruction::ExtractValue:
|
|
return ConstantFoldExtractValueInstruction(
|
|
Ops[0], cast<ExtractValueInst>(InstOrCE)->getIndices());
|
|
case Instruction::InsertElement:
|
|
return ConstantExpr::getInsertElement(Ops[0], Ops[1], Ops[2]);
|
|
case Instruction::InsertValue:
|
|
return ConstantFoldInsertValueInstruction(
|
|
Ops[0], Ops[1], cast<InsertValueInst>(InstOrCE)->getIndices());
|
|
case Instruction::ShuffleVector:
|
|
return ConstantExpr::getShuffleVector(
|
|
Ops[0], Ops[1], cast<ShuffleVectorInst>(InstOrCE)->getShuffleMask());
|
|
case Instruction::Load: {
|
|
const auto *LI = dyn_cast<LoadInst>(InstOrCE);
|
|
if (LI->isVolatile())
|
|
return nullptr;
|
|
return ConstantFoldLoadFromConstPtr(Ops[0], LI->getType(), DL);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // end anonymous namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Constant Folding public APIs
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
|
|
Constant *
|
|
ConstantFoldConstantImpl(const Constant *C, const DataLayout &DL,
|
|
const TargetLibraryInfo *TLI,
|
|
SmallDenseMap<Constant *, Constant *> &FoldedOps) {
|
|
if (!isa<ConstantVector>(C) && !isa<ConstantExpr>(C))
|
|
return const_cast<Constant *>(C);
|
|
|
|
SmallVector<Constant *, 8> Ops;
|
|
for (const Use &OldU : C->operands()) {
|
|
Constant *OldC = cast<Constant>(&OldU);
|
|
Constant *NewC = OldC;
|
|
// Recursively fold the ConstantExpr's operands. If we have already folded
|
|
// a ConstantExpr, we don't have to process it again.
|
|
if (isa<ConstantVector>(OldC) || isa<ConstantExpr>(OldC)) {
|
|
auto It = FoldedOps.find(OldC);
|
|
if (It == FoldedOps.end()) {
|
|
NewC = ConstantFoldConstantImpl(OldC, DL, TLI, FoldedOps);
|
|
FoldedOps.insert({OldC, NewC});
|
|
} else {
|
|
NewC = It->second;
|
|
}
|
|
}
|
|
Ops.push_back(NewC);
|
|
}
|
|
|
|
if (auto *CE = dyn_cast<ConstantExpr>(C)) {
|
|
if (Constant *Res = ConstantFoldInstOperandsImpl(
|
|
CE, CE->getOpcode(), Ops, DL, TLI, /*AllowNonDeterministic=*/true))
|
|
return Res;
|
|
return const_cast<Constant *>(C);
|
|
}
|
|
|
|
assert(isa<ConstantVector>(C));
|
|
return ConstantVector::get(Ops);
|
|
}
|
|
|
|
} // end anonymous namespace
|
|
|
|
Constant *llvm::ConstantFoldInstruction(const Instruction *I,
|
|
const DataLayout &DL,
|
|
const TargetLibraryInfo *TLI) {
|
|
// Handle PHI nodes quickly here...
|
|
if (auto *PN = dyn_cast<PHINode>(I)) {
|
|
Constant *CommonValue = nullptr;
|
|
|
|
SmallDenseMap<Constant *, Constant *> FoldedOps;
|
|
for (Value *Incoming : PN->incoming_values()) {
|
|
// If the incoming value is undef then skip it. Note that while we could
|
|
// skip the value if it is equal to the phi node itself we choose not to
|
|
// because that would break the rule that constant folding only applies if
|
|
// all operands are constants.
|
|
if (isa<UndefValue>(Incoming))
|
|
continue;
|
|
// If the incoming value is not a constant, then give up.
|
|
auto *C = dyn_cast<Constant>(Incoming);
|
|
if (!C)
|
|
return nullptr;
|
|
// Fold the PHI's operands.
|
|
C = ConstantFoldConstantImpl(C, DL, TLI, FoldedOps);
|
|
// If the incoming value is a different constant to
|
|
// the one we saw previously, then give up.
|
|
if (CommonValue && C != CommonValue)
|
|
return nullptr;
|
|
CommonValue = C;
|
|
}
|
|
|
|
// If we reach here, all incoming values are the same constant or undef.
|
|
return CommonValue ? CommonValue : UndefValue::get(PN->getType());
|
|
}
|
|
|
|
// Scan the operand list, checking to see if they are all constants, if so,
|
|
// hand off to ConstantFoldInstOperandsImpl.
|
|
if (!all_of(I->operands(), [](const Use &U) { return isa<Constant>(U); }))
|
|
return nullptr;
|
|
|
|
SmallDenseMap<Constant *, Constant *> FoldedOps;
|
|
SmallVector<Constant *, 8> Ops;
|
|
for (const Use &OpU : I->operands()) {
|
|
auto *Op = cast<Constant>(&OpU);
|
|
// Fold the Instruction's operands.
|
|
Op = ConstantFoldConstantImpl(Op, DL, TLI, FoldedOps);
|
|
Ops.push_back(Op);
|
|
}
|
|
|
|
return ConstantFoldInstOperands(I, Ops, DL, TLI);
|
|
}
|
|
|
|
Constant *llvm::ConstantFoldConstant(const Constant *C, const DataLayout &DL,
|
|
const TargetLibraryInfo *TLI) {
|
|
SmallDenseMap<Constant *, Constant *> FoldedOps;
|
|
return ConstantFoldConstantImpl(C, DL, TLI, FoldedOps);
|
|
}
|
|
|
|
Constant *llvm::ConstantFoldInstOperands(const Instruction *I,
|
|
ArrayRef<Constant *> Ops,
|
|
const DataLayout &DL,
|
|
const TargetLibraryInfo *TLI,
|
|
bool AllowNonDeterministic) {
|
|
return ConstantFoldInstOperandsImpl(I, I->getOpcode(), Ops, DL, TLI,
|
|
AllowNonDeterministic);
|
|
}
|
|
|
|
Constant *llvm::ConstantFoldCompareInstOperands(
|
|
unsigned IntPredicate, Constant *Ops0, Constant *Ops1, const DataLayout &DL,
|
|
const TargetLibraryInfo *TLI, const Instruction *I) {
|
|
CmpInst::Predicate Predicate = (CmpInst::Predicate)IntPredicate;
|
|
// fold: icmp (inttoptr x), null -> icmp x, 0
|
|
// fold: icmp null, (inttoptr x) -> icmp 0, x
|
|
// fold: icmp (ptrtoint x), 0 -> icmp x, null
|
|
// fold: icmp 0, (ptrtoint x) -> icmp null, x
|
|
// fold: icmp (inttoptr x), (inttoptr y) -> icmp trunc/zext x, trunc/zext y
|
|
// fold: icmp (ptrtoint x), (ptrtoint y) -> icmp x, y
|
|
//
|
|
// FIXME: The following comment is out of data and the DataLayout is here now.
|
|
// ConstantExpr::getCompare cannot do this, because it doesn't have DL
|
|
// around to know if bit truncation is happening.
|
|
if (auto *CE0 = dyn_cast<ConstantExpr>(Ops0)) {
|
|
if (Ops1->isNullValue()) {
|
|
if (CE0->getOpcode() == Instruction::IntToPtr) {
|
|
Type *IntPtrTy = DL.getIntPtrType(CE0->getType());
|
|
// Convert the integer value to the right size to ensure we get the
|
|
// proper extension or truncation.
|
|
if (Constant *C = ConstantFoldIntegerCast(CE0->getOperand(0), IntPtrTy,
|
|
/*IsSigned*/ false, DL)) {
|
|
Constant *Null = Constant::getNullValue(C->getType());
|
|
return ConstantFoldCompareInstOperands(Predicate, C, Null, DL, TLI);
|
|
}
|
|
}
|
|
|
|
// Only do this transformation if the int is intptrty in size, otherwise
|
|
// there is a truncation or extension that we aren't modeling.
|
|
if (CE0->getOpcode() == Instruction::PtrToInt) {
|
|
Type *IntPtrTy = DL.getIntPtrType(CE0->getOperand(0)->getType());
|
|
if (CE0->getType() == IntPtrTy) {
|
|
Constant *C = CE0->getOperand(0);
|
|
Constant *Null = Constant::getNullValue(C->getType());
|
|
return ConstantFoldCompareInstOperands(Predicate, C, Null, DL, TLI);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto *CE1 = dyn_cast<ConstantExpr>(Ops1)) {
|
|
if (CE0->getOpcode() == CE1->getOpcode()) {
|
|
if (CE0->getOpcode() == Instruction::IntToPtr) {
|
|
Type *IntPtrTy = DL.getIntPtrType(CE0->getType());
|
|
|
|
// Convert the integer value to the right size to ensure we get the
|
|
// proper extension or truncation.
|
|
Constant *C0 = ConstantFoldIntegerCast(CE0->getOperand(0), IntPtrTy,
|
|
/*IsSigned*/ false, DL);
|
|
Constant *C1 = ConstantFoldIntegerCast(CE1->getOperand(0), IntPtrTy,
|
|
/*IsSigned*/ false, DL);
|
|
if (C0 && C1)
|
|
return ConstantFoldCompareInstOperands(Predicate, C0, C1, DL, TLI);
|
|
}
|
|
|
|
// Only do this transformation if the int is intptrty in size, otherwise
|
|
// there is a truncation or extension that we aren't modeling.
|
|
if (CE0->getOpcode() == Instruction::PtrToInt) {
|
|
Type *IntPtrTy = DL.getIntPtrType(CE0->getOperand(0)->getType());
|
|
if (CE0->getType() == IntPtrTy &&
|
|
CE0->getOperand(0)->getType() == CE1->getOperand(0)->getType()) {
|
|
return ConstantFoldCompareInstOperands(
|
|
Predicate, CE0->getOperand(0), CE1->getOperand(0), DL, TLI);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Convert pointer comparison (base+offset1) pred (base+offset2) into
|
|
// offset1 pred offset2, for the case where the offset is inbounds. This
|
|
// only works for equality and unsigned comparison, as inbounds permits
|
|
// crossing the sign boundary. However, the offset comparison itself is
|
|
// signed.
|
|
if (Ops0->getType()->isPointerTy() && !ICmpInst::isSigned(Predicate)) {
|
|
unsigned IndexWidth = DL.getIndexTypeSizeInBits(Ops0->getType());
|
|
APInt Offset0(IndexWidth, 0);
|
|
bool IsEqPred = ICmpInst::isEquality(Predicate);
|
|
Value *Stripped0 = Ops0->stripAndAccumulateConstantOffsets(
|
|
DL, Offset0, /*AllowNonInbounds=*/IsEqPred,
|
|
/*AllowInvariantGroup=*/false, /*ExternalAnalysis=*/nullptr,
|
|
/*LookThroughIntToPtr=*/IsEqPred);
|
|
APInt Offset1(IndexWidth, 0);
|
|
Value *Stripped1 = Ops1->stripAndAccumulateConstantOffsets(
|
|
DL, Offset1, /*AllowNonInbounds=*/IsEqPred,
|
|
/*AllowInvariantGroup=*/false, /*ExternalAnalysis=*/nullptr,
|
|
/*LookThroughIntToPtr=*/IsEqPred);
|
|
if (Stripped0 == Stripped1)
|
|
return ConstantInt::getBool(
|
|
Ops0->getContext(),
|
|
ICmpInst::compare(Offset0, Offset1,
|
|
ICmpInst::getSignedPredicate(Predicate)));
|
|
}
|
|
} else if (isa<ConstantExpr>(Ops1)) {
|
|
// If RHS is a constant expression, but the left side isn't, swap the
|
|
// operands and try again.
|
|
Predicate = ICmpInst::getSwappedPredicate(Predicate);
|
|
return ConstantFoldCompareInstOperands(Predicate, Ops1, Ops0, DL, TLI);
|
|
}
|
|
|
|
if (CmpInst::isFPPredicate(Predicate)) {
|
|
// Flush any denormal constant float input according to denormal handling
|
|
// mode.
|
|
Ops0 = FlushFPConstant(Ops0, I, /*IsOutput=*/false);
|
|
if (!Ops0)
|
|
return nullptr;
|
|
Ops1 = FlushFPConstant(Ops1, I, /*IsOutput=*/false);
|
|
if (!Ops1)
|
|
return nullptr;
|
|
}
|
|
|
|
return ConstantFoldCompareInstruction(Predicate, Ops0, Ops1);
|
|
}
|
|
|
|
Constant *llvm::ConstantFoldUnaryOpOperand(unsigned Opcode, Constant *Op,
|
|
const DataLayout &DL) {
|
|
assert(Instruction::isUnaryOp(Opcode));
|
|
|
|
return ConstantFoldUnaryInstruction(Opcode, Op);
|
|
}
|
|
|
|
Constant *llvm::ConstantFoldBinaryOpOperands(unsigned Opcode, Constant *LHS,
|
|
Constant *RHS,
|
|
const DataLayout &DL) {
|
|
assert(Instruction::isBinaryOp(Opcode));
|
|
if (isa<ConstantExpr>(LHS) || isa<ConstantExpr>(RHS))
|
|
if (Constant *C = SymbolicallyEvaluateBinop(Opcode, LHS, RHS, DL))
|
|
return C;
|
|
|
|
if (ConstantExpr::isDesirableBinOp(Opcode))
|
|
return ConstantExpr::get(Opcode, LHS, RHS);
|
|
return ConstantFoldBinaryInstruction(Opcode, LHS, RHS);
|
|
}
|
|
|
|
static ConstantFP *flushDenormalConstant(Type *Ty, const APFloat &APF,
|
|
DenormalMode::DenormalModeKind Mode) {
|
|
switch (Mode) {
|
|
case DenormalMode::Dynamic:
|
|
return nullptr;
|
|
case DenormalMode::IEEE:
|
|
return ConstantFP::get(Ty->getContext(), APF);
|
|
case DenormalMode::PreserveSign:
|
|
return ConstantFP::get(
|
|
Ty->getContext(),
|
|
APFloat::getZero(APF.getSemantics(), APF.isNegative()));
|
|
case DenormalMode::PositiveZero:
|
|
return ConstantFP::get(Ty->getContext(),
|
|
APFloat::getZero(APF.getSemantics(), false));
|
|
default:
|
|
break;
|
|
}
|
|
|
|
llvm_unreachable("unknown denormal mode");
|
|
}
|
|
|
|
/// Return the denormal mode that can be assumed when executing a floating point
|
|
/// operation at \p CtxI.
|
|
static DenormalMode getInstrDenormalMode(const Instruction *CtxI, Type *Ty) {
|
|
if (!CtxI || !CtxI->getParent() || !CtxI->getFunction())
|
|
return DenormalMode::getDynamic();
|
|
return CtxI->getFunction()->getDenormalMode(Ty->getFltSemantics());
|
|
}
|
|
|
|
static ConstantFP *flushDenormalConstantFP(ConstantFP *CFP,
|
|
const Instruction *Inst,
|
|
bool IsOutput) {
|
|
const APFloat &APF = CFP->getValueAPF();
|
|
if (!APF.isDenormal())
|
|
return CFP;
|
|
|
|
DenormalMode Mode = getInstrDenormalMode(Inst, CFP->getType());
|
|
return flushDenormalConstant(CFP->getType(), APF,
|
|
IsOutput ? Mode.Output : Mode.Input);
|
|
}
|
|
|
|
Constant *llvm::FlushFPConstant(Constant *Operand, const Instruction *Inst,
|
|
bool IsOutput) {
|
|
if (ConstantFP *CFP = dyn_cast<ConstantFP>(Operand))
|
|
return flushDenormalConstantFP(CFP, Inst, IsOutput);
|
|
|
|
if (isa<ConstantAggregateZero, UndefValue>(Operand))
|
|
return Operand;
|
|
|
|
Type *Ty = Operand->getType();
|
|
VectorType *VecTy = dyn_cast<VectorType>(Ty);
|
|
if (VecTy) {
|
|
if (auto *Splat = dyn_cast_or_null<ConstantFP>(Operand->getSplatValue())) {
|
|
ConstantFP *Folded = flushDenormalConstantFP(Splat, Inst, IsOutput);
|
|
if (!Folded)
|
|
return nullptr;
|
|
return ConstantVector::getSplat(VecTy->getElementCount(), Folded);
|
|
}
|
|
|
|
Ty = VecTy->getElementType();
|
|
}
|
|
|
|
if (isa<ConstantExpr>(Operand))
|
|
return Operand;
|
|
|
|
if (const auto *CV = dyn_cast<ConstantVector>(Operand)) {
|
|
SmallVector<Constant *, 16> NewElts;
|
|
for (unsigned i = 0, e = CV->getNumOperands(); i != e; ++i) {
|
|
Constant *Element = CV->getAggregateElement(i);
|
|
if (isa<UndefValue>(Element)) {
|
|
NewElts.push_back(Element);
|
|
continue;
|
|
}
|
|
|
|
ConstantFP *CFP = dyn_cast<ConstantFP>(Element);
|
|
if (!CFP)
|
|
return nullptr;
|
|
|
|
ConstantFP *Folded = flushDenormalConstantFP(CFP, Inst, IsOutput);
|
|
if (!Folded)
|
|
return nullptr;
|
|
NewElts.push_back(Folded);
|
|
}
|
|
|
|
return ConstantVector::get(NewElts);
|
|
}
|
|
|
|
if (const auto *CDV = dyn_cast<ConstantDataVector>(Operand)) {
|
|
SmallVector<Constant *, 16> NewElts;
|
|
for (unsigned I = 0, E = CDV->getNumElements(); I < E; ++I) {
|
|
const APFloat &Elt = CDV->getElementAsAPFloat(I);
|
|
if (!Elt.isDenormal()) {
|
|
NewElts.push_back(ConstantFP::get(Ty, Elt));
|
|
} else {
|
|
DenormalMode Mode = getInstrDenormalMode(Inst, Ty);
|
|
ConstantFP *Folded =
|
|
flushDenormalConstant(Ty, Elt, IsOutput ? Mode.Output : Mode.Input);
|
|
if (!Folded)
|
|
return nullptr;
|
|
NewElts.push_back(Folded);
|
|
}
|
|
}
|
|
|
|
return ConstantVector::get(NewElts);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Constant *llvm::ConstantFoldFPInstOperands(unsigned Opcode, Constant *LHS,
|
|
Constant *RHS, const DataLayout &DL,
|
|
const Instruction *I,
|
|
bool AllowNonDeterministic) {
|
|
if (Instruction::isBinaryOp(Opcode)) {
|
|
// Flush denormal inputs if needed.
|
|
Constant *Op0 = FlushFPConstant(LHS, I, /* IsOutput */ false);
|
|
if (!Op0)
|
|
return nullptr;
|
|
Constant *Op1 = FlushFPConstant(RHS, I, /* IsOutput */ false);
|
|
if (!Op1)
|
|
return nullptr;
|
|
|
|
// If nsz or an algebraic FMF flag is set, the result of the FP operation
|
|
// may change due to future optimization. Don't constant fold them if
|
|
// non-deterministic results are not allowed.
|
|
if (!AllowNonDeterministic)
|
|
if (auto *FP = dyn_cast_or_null<FPMathOperator>(I))
|
|
if (FP->hasNoSignedZeros() || FP->hasAllowReassoc() ||
|
|
FP->hasAllowContract() || FP->hasAllowReciprocal())
|
|
return nullptr;
|
|
|
|
// Calculate constant result.
|
|
Constant *C = ConstantFoldBinaryOpOperands(Opcode, Op0, Op1, DL);
|
|
if (!C)
|
|
return nullptr;
|
|
|
|
// Flush denormal output if needed.
|
|
C = FlushFPConstant(C, I, /* IsOutput */ true);
|
|
if (!C)
|
|
return nullptr;
|
|
|
|
// The precise NaN value is non-deterministic.
|
|
if (!AllowNonDeterministic && C->isNaN())
|
|
return nullptr;
|
|
|
|
return C;
|
|
}
|
|
// If instruction lacks a parent/function and the denormal mode cannot be
|
|
// determined, use the default (IEEE).
|
|
return ConstantFoldBinaryOpOperands(Opcode, LHS, RHS, DL);
|
|
}
|
|
|
|
Constant *llvm::ConstantFoldCastOperand(unsigned Opcode, Constant *C,
|
|
Type *DestTy, const DataLayout &DL) {
|
|
assert(Instruction::isCast(Opcode));
|
|
switch (Opcode) {
|
|
default:
|
|
llvm_unreachable("Missing case");
|
|
case Instruction::PtrToAddr:
|
|
// TODO: Add some of the ptrtoint folds here as well.
|
|
break;
|
|
case Instruction::PtrToInt:
|
|
if (auto *CE = dyn_cast<ConstantExpr>(C)) {
|
|
Constant *FoldedValue = nullptr;
|
|
// If the input is a inttoptr, eliminate the pair. This requires knowing
|
|
// the width of a pointer, so it can't be done in ConstantExpr::getCast.
|
|
if (CE->getOpcode() == Instruction::IntToPtr) {
|
|
// zext/trunc the inttoptr to pointer size.
|
|
FoldedValue = ConstantFoldIntegerCast(CE->getOperand(0),
|
|
DL.getIntPtrType(CE->getType()),
|
|
/*IsSigned=*/false, DL);
|
|
} else if (auto *GEP = dyn_cast<GEPOperator>(CE)) {
|
|
// If we have GEP, we can perform the following folds:
|
|
// (ptrtoint (gep null, x)) -> x
|
|
// (ptrtoint (gep (gep null, x), y) -> x + y, etc.
|
|
unsigned BitWidth = DL.getIndexTypeSizeInBits(GEP->getType());
|
|
APInt BaseOffset(BitWidth, 0);
|
|
auto *Base = cast<Constant>(GEP->stripAndAccumulateConstantOffsets(
|
|
DL, BaseOffset, /*AllowNonInbounds=*/true));
|
|
if (Base->isNullValue()) {
|
|
FoldedValue = ConstantInt::get(CE->getContext(), BaseOffset);
|
|
} else {
|
|
// ptrtoint (gep i8, Ptr, (sub 0, V)) -> sub (ptrtoint Ptr), V
|
|
if (GEP->getNumIndices() == 1 &&
|
|
GEP->getSourceElementType()->isIntegerTy(8)) {
|
|
auto *Ptr = cast<Constant>(GEP->getPointerOperand());
|
|
auto *Sub = dyn_cast<ConstantExpr>(GEP->getOperand(1));
|
|
Type *IntIdxTy = DL.getIndexType(Ptr->getType());
|
|
if (Sub && Sub->getType() == IntIdxTy &&
|
|
Sub->getOpcode() == Instruction::Sub &&
|
|
Sub->getOperand(0)->isNullValue())
|
|
FoldedValue = ConstantExpr::getSub(
|
|
ConstantExpr::getPtrToInt(Ptr, IntIdxTy), Sub->getOperand(1));
|
|
}
|
|
}
|
|
}
|
|
if (FoldedValue) {
|
|
// Do a zext or trunc to get to the ptrtoint dest size.
|
|
return ConstantFoldIntegerCast(FoldedValue, DestTy, /*IsSigned=*/false,
|
|
DL);
|
|
}
|
|
}
|
|
break;
|
|
case Instruction::IntToPtr:
|
|
// If the input is a ptrtoint, turn the pair into a ptr to ptr bitcast if
|
|
// the int size is >= the ptr size and the address spaces are the same.
|
|
// This requires knowing the width of a pointer, so it can't be done in
|
|
// ConstantExpr::getCast.
|
|
if (auto *CE = dyn_cast<ConstantExpr>(C)) {
|
|
if (CE->getOpcode() == Instruction::PtrToInt) {
|
|
Constant *SrcPtr = CE->getOperand(0);
|
|
unsigned SrcPtrSize = DL.getPointerTypeSizeInBits(SrcPtr->getType());
|
|
unsigned MidIntSize = CE->getType()->getScalarSizeInBits();
|
|
|
|
if (MidIntSize >= SrcPtrSize) {
|
|
unsigned SrcAS = SrcPtr->getType()->getPointerAddressSpace();
|
|
if (SrcAS == DestTy->getPointerAddressSpace())
|
|
return FoldBitCast(CE->getOperand(0), DestTy, DL);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case Instruction::Trunc:
|
|
case Instruction::ZExt:
|
|
case Instruction::SExt:
|
|
case Instruction::FPTrunc:
|
|
case Instruction::FPExt:
|
|
case Instruction::UIToFP:
|
|
case Instruction::SIToFP:
|
|
case Instruction::FPToUI:
|
|
case Instruction::FPToSI:
|
|
case Instruction::AddrSpaceCast:
|
|
break;
|
|
case Instruction::BitCast:
|
|
return FoldBitCast(C, DestTy, DL);
|
|
}
|
|
|
|
if (ConstantExpr::isDesirableCastOp(Opcode))
|
|
return ConstantExpr::getCast(Opcode, C, DestTy);
|
|
return ConstantFoldCastInstruction(Opcode, C, DestTy);
|
|
}
|
|
|
|
Constant *llvm::ConstantFoldIntegerCast(Constant *C, Type *DestTy,
|
|
bool IsSigned, const DataLayout &DL) {
|
|
Type *SrcTy = C->getType();
|
|
if (SrcTy == DestTy)
|
|
return C;
|
|
if (SrcTy->getScalarSizeInBits() > DestTy->getScalarSizeInBits())
|
|
return ConstantFoldCastOperand(Instruction::Trunc, C, DestTy, DL);
|
|
if (IsSigned)
|
|
return ConstantFoldCastOperand(Instruction::SExt, C, DestTy, DL);
|
|
return ConstantFoldCastOperand(Instruction::ZExt, C, DestTy, DL);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Constant Folding for Calls
|
|
//
|
|
|
|
bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) {
|
|
if (Call->isNoBuiltin())
|
|
return false;
|
|
if (Call->getFunctionType() != F->getFunctionType())
|
|
return false;
|
|
|
|
// Allow FP calls (both libcalls and intrinsics) to avoid being folded.
|
|
// This can be useful for GPU targets or in cross-compilation scenarios
|
|
// when the exact target FP behaviour is required, and the host compiler's
|
|
// behaviour may be slightly different from the device's run-time behaviour.
|
|
if (DisableFPCallFolding && (F->getReturnType()->isFloatingPointTy() ||
|
|
any_of(F->args(), [](const Argument &Arg) {
|
|
return Arg.getType()->isFloatingPointTy();
|
|
})))
|
|
return false;
|
|
|
|
switch (F->getIntrinsicID()) {
|
|
// Operations that do not operate floating-point numbers and do not depend on
|
|
// FP environment can be folded even in strictfp functions.
|
|
case Intrinsic::bswap:
|
|
case Intrinsic::ctpop:
|
|
case Intrinsic::ctlz:
|
|
case Intrinsic::cttz:
|
|
case Intrinsic::fshl:
|
|
case Intrinsic::fshr:
|
|
case Intrinsic::launder_invariant_group:
|
|
case Intrinsic::strip_invariant_group:
|
|
case Intrinsic::masked_load:
|
|
case Intrinsic::get_active_lane_mask:
|
|
case Intrinsic::abs:
|
|
case Intrinsic::smax:
|
|
case Intrinsic::smin:
|
|
case Intrinsic::umax:
|
|
case Intrinsic::umin:
|
|
case Intrinsic::scmp:
|
|
case Intrinsic::ucmp:
|
|
case Intrinsic::sadd_with_overflow:
|
|
case Intrinsic::uadd_with_overflow:
|
|
case Intrinsic::ssub_with_overflow:
|
|
case Intrinsic::usub_with_overflow:
|
|
case Intrinsic::smul_with_overflow:
|
|
case Intrinsic::umul_with_overflow:
|
|
case Intrinsic::sadd_sat:
|
|
case Intrinsic::uadd_sat:
|
|
case Intrinsic::ssub_sat:
|
|
case Intrinsic::usub_sat:
|
|
case Intrinsic::smul_fix:
|
|
case Intrinsic::smul_fix_sat:
|
|
case Intrinsic::bitreverse:
|
|
case Intrinsic::is_constant:
|
|
case Intrinsic::vector_reduce_add:
|
|
case Intrinsic::vector_reduce_mul:
|
|
case Intrinsic::vector_reduce_and:
|
|
case Intrinsic::vector_reduce_or:
|
|
case Intrinsic::vector_reduce_xor:
|
|
case Intrinsic::vector_reduce_smin:
|
|
case Intrinsic::vector_reduce_smax:
|
|
case Intrinsic::vector_reduce_umin:
|
|
case Intrinsic::vector_reduce_umax:
|
|
case Intrinsic::vector_extract:
|
|
case Intrinsic::vector_insert:
|
|
case Intrinsic::vector_interleave2:
|
|
case Intrinsic::vector_deinterleave2:
|
|
// Target intrinsics
|
|
case Intrinsic::amdgcn_perm:
|
|
case Intrinsic::amdgcn_wave_reduce_umin:
|
|
case Intrinsic::amdgcn_wave_reduce_umax:
|
|
case Intrinsic::amdgcn_s_wqm:
|
|
case Intrinsic::amdgcn_s_quadmask:
|
|
case Intrinsic::amdgcn_s_bitreplicate:
|
|
case Intrinsic::arm_mve_vctp8:
|
|
case Intrinsic::arm_mve_vctp16:
|
|
case Intrinsic::arm_mve_vctp32:
|
|
case Intrinsic::arm_mve_vctp64:
|
|
case Intrinsic::aarch64_sve_convert_from_svbool:
|
|
case Intrinsic::wasm_alltrue:
|
|
case Intrinsic::wasm_anytrue:
|
|
// WebAssembly float semantics are always known
|
|
case Intrinsic::wasm_trunc_signed:
|
|
case Intrinsic::wasm_trunc_unsigned:
|
|
return true;
|
|
|
|
// Floating point operations cannot be folded in strictfp functions in
|
|
// general case. They can be folded if FP environment is known to compiler.
|
|
case Intrinsic::minnum:
|
|
case Intrinsic::maxnum:
|
|
case Intrinsic::minimum:
|
|
case Intrinsic::maximum:
|
|
case Intrinsic::minimumnum:
|
|
case Intrinsic::maximumnum:
|
|
case Intrinsic::log:
|
|
case Intrinsic::log2:
|
|
case Intrinsic::log10:
|
|
case Intrinsic::exp:
|
|
case Intrinsic::exp2:
|
|
case Intrinsic::exp10:
|
|
case Intrinsic::sqrt:
|
|
case Intrinsic::sin:
|
|
case Intrinsic::cos:
|
|
case Intrinsic::sincos:
|
|
case Intrinsic::sinh:
|
|
case Intrinsic::cosh:
|
|
case Intrinsic::atan:
|
|
case Intrinsic::pow:
|
|
case Intrinsic::powi:
|
|
case Intrinsic::ldexp:
|
|
case Intrinsic::fma:
|
|
case Intrinsic::fmuladd:
|
|
case Intrinsic::frexp:
|
|
case Intrinsic::fptoui_sat:
|
|
case Intrinsic::fptosi_sat:
|
|
case Intrinsic::convert_from_fp16:
|
|
case Intrinsic::convert_to_fp16:
|
|
case Intrinsic::amdgcn_cos:
|
|
case Intrinsic::amdgcn_cubeid:
|
|
case Intrinsic::amdgcn_cubema:
|
|
case Intrinsic::amdgcn_cubesc:
|
|
case Intrinsic::amdgcn_cubetc:
|
|
case Intrinsic::amdgcn_fmul_legacy:
|
|
case Intrinsic::amdgcn_fma_legacy:
|
|
case Intrinsic::amdgcn_fract:
|
|
case Intrinsic::amdgcn_sin:
|
|
// The intrinsics below depend on rounding mode in MXCSR.
|
|
case Intrinsic::x86_sse_cvtss2si:
|
|
case Intrinsic::x86_sse_cvtss2si64:
|
|
case Intrinsic::x86_sse_cvttss2si:
|
|
case Intrinsic::x86_sse_cvttss2si64:
|
|
case Intrinsic::x86_sse2_cvtsd2si:
|
|
case Intrinsic::x86_sse2_cvtsd2si64:
|
|
case Intrinsic::x86_sse2_cvttsd2si:
|
|
case Intrinsic::x86_sse2_cvttsd2si64:
|
|
case Intrinsic::x86_avx512_vcvtss2si32:
|
|
case Intrinsic::x86_avx512_vcvtss2si64:
|
|
case Intrinsic::x86_avx512_cvttss2si:
|
|
case Intrinsic::x86_avx512_cvttss2si64:
|
|
case Intrinsic::x86_avx512_vcvtsd2si32:
|
|
case Intrinsic::x86_avx512_vcvtsd2si64:
|
|
case Intrinsic::x86_avx512_cvttsd2si:
|
|
case Intrinsic::x86_avx512_cvttsd2si64:
|
|
case Intrinsic::x86_avx512_vcvtss2usi32:
|
|
case Intrinsic::x86_avx512_vcvtss2usi64:
|
|
case Intrinsic::x86_avx512_cvttss2usi:
|
|
case Intrinsic::x86_avx512_cvttss2usi64:
|
|
case Intrinsic::x86_avx512_vcvtsd2usi32:
|
|
case Intrinsic::x86_avx512_vcvtsd2usi64:
|
|
case Intrinsic::x86_avx512_cvttsd2usi:
|
|
case Intrinsic::x86_avx512_cvttsd2usi64:
|
|
|
|
// NVVM FMax intrinsics
|
|
case Intrinsic::nvvm_fmax_d:
|
|
case Intrinsic::nvvm_fmax_f:
|
|
case Intrinsic::nvvm_fmax_ftz_f:
|
|
case Intrinsic::nvvm_fmax_ftz_nan_f:
|
|
case Intrinsic::nvvm_fmax_ftz_nan_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmax_ftz_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmax_nan_f:
|
|
case Intrinsic::nvvm_fmax_nan_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmax_xorsign_abs_f:
|
|
|
|
// NVVM FMin intrinsics
|
|
case Intrinsic::nvvm_fmin_d:
|
|
case Intrinsic::nvvm_fmin_f:
|
|
case Intrinsic::nvvm_fmin_ftz_f:
|
|
case Intrinsic::nvvm_fmin_ftz_nan_f:
|
|
case Intrinsic::nvvm_fmin_ftz_nan_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmin_ftz_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmin_nan_f:
|
|
case Intrinsic::nvvm_fmin_nan_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmin_xorsign_abs_f:
|
|
|
|
// NVVM float/double to int32/uint32 conversion intrinsics
|
|
case Intrinsic::nvvm_f2i_rm:
|
|
case Intrinsic::nvvm_f2i_rn:
|
|
case Intrinsic::nvvm_f2i_rp:
|
|
case Intrinsic::nvvm_f2i_rz:
|
|
case Intrinsic::nvvm_f2i_rm_ftz:
|
|
case Intrinsic::nvvm_f2i_rn_ftz:
|
|
case Intrinsic::nvvm_f2i_rp_ftz:
|
|
case Intrinsic::nvvm_f2i_rz_ftz:
|
|
case Intrinsic::nvvm_f2ui_rm:
|
|
case Intrinsic::nvvm_f2ui_rn:
|
|
case Intrinsic::nvvm_f2ui_rp:
|
|
case Intrinsic::nvvm_f2ui_rz:
|
|
case Intrinsic::nvvm_f2ui_rm_ftz:
|
|
case Intrinsic::nvvm_f2ui_rn_ftz:
|
|
case Intrinsic::nvvm_f2ui_rp_ftz:
|
|
case Intrinsic::nvvm_f2ui_rz_ftz:
|
|
case Intrinsic::nvvm_d2i_rm:
|
|
case Intrinsic::nvvm_d2i_rn:
|
|
case Intrinsic::nvvm_d2i_rp:
|
|
case Intrinsic::nvvm_d2i_rz:
|
|
case Intrinsic::nvvm_d2ui_rm:
|
|
case Intrinsic::nvvm_d2ui_rn:
|
|
case Intrinsic::nvvm_d2ui_rp:
|
|
case Intrinsic::nvvm_d2ui_rz:
|
|
|
|
// NVVM float/double to int64/uint64 conversion intrinsics
|
|
case Intrinsic::nvvm_f2ll_rm:
|
|
case Intrinsic::nvvm_f2ll_rn:
|
|
case Intrinsic::nvvm_f2ll_rp:
|
|
case Intrinsic::nvvm_f2ll_rz:
|
|
case Intrinsic::nvvm_f2ll_rm_ftz:
|
|
case Intrinsic::nvvm_f2ll_rn_ftz:
|
|
case Intrinsic::nvvm_f2ll_rp_ftz:
|
|
case Intrinsic::nvvm_f2ll_rz_ftz:
|
|
case Intrinsic::nvvm_f2ull_rm:
|
|
case Intrinsic::nvvm_f2ull_rn:
|
|
case Intrinsic::nvvm_f2ull_rp:
|
|
case Intrinsic::nvvm_f2ull_rz:
|
|
case Intrinsic::nvvm_f2ull_rm_ftz:
|
|
case Intrinsic::nvvm_f2ull_rn_ftz:
|
|
case Intrinsic::nvvm_f2ull_rp_ftz:
|
|
case Intrinsic::nvvm_f2ull_rz_ftz:
|
|
case Intrinsic::nvvm_d2ll_rm:
|
|
case Intrinsic::nvvm_d2ll_rn:
|
|
case Intrinsic::nvvm_d2ll_rp:
|
|
case Intrinsic::nvvm_d2ll_rz:
|
|
case Intrinsic::nvvm_d2ull_rm:
|
|
case Intrinsic::nvvm_d2ull_rn:
|
|
case Intrinsic::nvvm_d2ull_rp:
|
|
case Intrinsic::nvvm_d2ull_rz:
|
|
|
|
// NVVM math intrinsics:
|
|
case Intrinsic::nvvm_ceil_d:
|
|
case Intrinsic::nvvm_ceil_f:
|
|
case Intrinsic::nvvm_ceil_ftz_f:
|
|
|
|
case Intrinsic::nvvm_fabs:
|
|
case Intrinsic::nvvm_fabs_ftz:
|
|
|
|
case Intrinsic::nvvm_floor_d:
|
|
case Intrinsic::nvvm_floor_f:
|
|
case Intrinsic::nvvm_floor_ftz_f:
|
|
|
|
case Intrinsic::nvvm_rcp_rm_d:
|
|
case Intrinsic::nvvm_rcp_rm_f:
|
|
case Intrinsic::nvvm_rcp_rm_ftz_f:
|
|
case Intrinsic::nvvm_rcp_rn_d:
|
|
case Intrinsic::nvvm_rcp_rn_f:
|
|
case Intrinsic::nvvm_rcp_rn_ftz_f:
|
|
case Intrinsic::nvvm_rcp_rp_d:
|
|
case Intrinsic::nvvm_rcp_rp_f:
|
|
case Intrinsic::nvvm_rcp_rp_ftz_f:
|
|
case Intrinsic::nvvm_rcp_rz_d:
|
|
case Intrinsic::nvvm_rcp_rz_f:
|
|
case Intrinsic::nvvm_rcp_rz_ftz_f:
|
|
|
|
case Intrinsic::nvvm_round_d:
|
|
case Intrinsic::nvvm_round_f:
|
|
case Intrinsic::nvvm_round_ftz_f:
|
|
|
|
case Intrinsic::nvvm_saturate_d:
|
|
case Intrinsic::nvvm_saturate_f:
|
|
case Intrinsic::nvvm_saturate_ftz_f:
|
|
|
|
case Intrinsic::nvvm_sqrt_f:
|
|
case Intrinsic::nvvm_sqrt_rn_d:
|
|
case Intrinsic::nvvm_sqrt_rn_f:
|
|
case Intrinsic::nvvm_sqrt_rn_ftz_f:
|
|
return !Call->isStrictFP();
|
|
|
|
// Sign operations are actually bitwise operations, they do not raise
|
|
// exceptions even for SNANs.
|
|
case Intrinsic::fabs:
|
|
case Intrinsic::copysign:
|
|
case Intrinsic::is_fpclass:
|
|
// Non-constrained variants of rounding operations means default FP
|
|
// environment, they can be folded in any case.
|
|
case Intrinsic::ceil:
|
|
case Intrinsic::floor:
|
|
case Intrinsic::round:
|
|
case Intrinsic::roundeven:
|
|
case Intrinsic::trunc:
|
|
case Intrinsic::nearbyint:
|
|
case Intrinsic::rint:
|
|
case Intrinsic::canonicalize:
|
|
|
|
// Constrained intrinsics can be folded if FP environment is known
|
|
// to compiler.
|
|
case Intrinsic::experimental_constrained_fma:
|
|
case Intrinsic::experimental_constrained_fmuladd:
|
|
case Intrinsic::experimental_constrained_fadd:
|
|
case Intrinsic::experimental_constrained_fsub:
|
|
case Intrinsic::experimental_constrained_fmul:
|
|
case Intrinsic::experimental_constrained_fdiv:
|
|
case Intrinsic::experimental_constrained_frem:
|
|
case Intrinsic::experimental_constrained_ceil:
|
|
case Intrinsic::experimental_constrained_floor:
|
|
case Intrinsic::experimental_constrained_round:
|
|
case Intrinsic::experimental_constrained_roundeven:
|
|
case Intrinsic::experimental_constrained_trunc:
|
|
case Intrinsic::experimental_constrained_nearbyint:
|
|
case Intrinsic::experimental_constrained_rint:
|
|
case Intrinsic::experimental_constrained_fcmp:
|
|
case Intrinsic::experimental_constrained_fcmps:
|
|
return true;
|
|
default:
|
|
return false;
|
|
case Intrinsic::not_intrinsic: break;
|
|
}
|
|
|
|
if (!F->hasName() || Call->isStrictFP())
|
|
return false;
|
|
|
|
// In these cases, the check of the length is required. We don't want to
|
|
// return true for a name like "cos\0blah" which strcmp would return equal to
|
|
// "cos", but has length 8.
|
|
StringRef Name = F->getName();
|
|
switch (Name[0]) {
|
|
default:
|
|
return false;
|
|
case 'a':
|
|
return Name == "acos" || Name == "acosf" ||
|
|
Name == "asin" || Name == "asinf" ||
|
|
Name == "atan" || Name == "atanf" ||
|
|
Name == "atan2" || Name == "atan2f";
|
|
case 'c':
|
|
return Name == "ceil" || Name == "ceilf" ||
|
|
Name == "cos" || Name == "cosf" ||
|
|
Name == "cosh" || Name == "coshf";
|
|
case 'e':
|
|
return Name == "exp" || Name == "expf" || Name == "exp2" ||
|
|
Name == "exp2f" || Name == "erf" || Name == "erff";
|
|
case 'f':
|
|
return Name == "fabs" || Name == "fabsf" ||
|
|
Name == "floor" || Name == "floorf" ||
|
|
Name == "fmod" || Name == "fmodf";
|
|
case 'i':
|
|
return Name == "ilogb" || Name == "ilogbf";
|
|
case 'l':
|
|
return Name == "log" || Name == "logf" || Name == "logl" ||
|
|
Name == "log2" || Name == "log2f" || Name == "log10" ||
|
|
Name == "log10f" || Name == "logb" || Name == "logbf" ||
|
|
Name == "log1p" || Name == "log1pf";
|
|
case 'n':
|
|
return Name == "nearbyint" || Name == "nearbyintf";
|
|
case 'p':
|
|
return Name == "pow" || Name == "powf";
|
|
case 'r':
|
|
return Name == "remainder" || Name == "remainderf" ||
|
|
Name == "rint" || Name == "rintf" ||
|
|
Name == "round" || Name == "roundf";
|
|
case 's':
|
|
return Name == "sin" || Name == "sinf" ||
|
|
Name == "sinh" || Name == "sinhf" ||
|
|
Name == "sqrt" || Name == "sqrtf";
|
|
case 't':
|
|
return Name == "tan" || Name == "tanf" ||
|
|
Name == "tanh" || Name == "tanhf" ||
|
|
Name == "trunc" || Name == "truncf";
|
|
case '_':
|
|
// Check for various function names that get used for the math functions
|
|
// when the header files are preprocessed with the macro
|
|
// __FINITE_MATH_ONLY__ enabled.
|
|
// The '12' here is the length of the shortest name that can match.
|
|
// We need to check the size before looking at Name[1] and Name[2]
|
|
// so we may as well check a limit that will eliminate mismatches.
|
|
if (Name.size() < 12 || Name[1] != '_')
|
|
return false;
|
|
switch (Name[2]) {
|
|
default:
|
|
return false;
|
|
case 'a':
|
|
return Name == "__acos_finite" || Name == "__acosf_finite" ||
|
|
Name == "__asin_finite" || Name == "__asinf_finite" ||
|
|
Name == "__atan2_finite" || Name == "__atan2f_finite";
|
|
case 'c':
|
|
return Name == "__cosh_finite" || Name == "__coshf_finite";
|
|
case 'e':
|
|
return Name == "__exp_finite" || Name == "__expf_finite" ||
|
|
Name == "__exp2_finite" || Name == "__exp2f_finite";
|
|
case 'l':
|
|
return Name == "__log_finite" || Name == "__logf_finite" ||
|
|
Name == "__log10_finite" || Name == "__log10f_finite";
|
|
case 'p':
|
|
return Name == "__pow_finite" || Name == "__powf_finite";
|
|
case 's':
|
|
return Name == "__sinh_finite" || Name == "__sinhf_finite";
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
Constant *GetConstantFoldFPValue(double V, Type *Ty) {
|
|
if (Ty->isHalfTy() || Ty->isFloatTy()) {
|
|
APFloat APF(V);
|
|
bool unused;
|
|
APF.convert(Ty->getFltSemantics(), APFloat::rmNearestTiesToEven, &unused);
|
|
return ConstantFP::get(Ty->getContext(), APF);
|
|
}
|
|
if (Ty->isDoubleTy())
|
|
return ConstantFP::get(Ty->getContext(), APFloat(V));
|
|
llvm_unreachable("Can only constant fold half/float/double");
|
|
}
|
|
|
|
#if defined(HAS_IEE754_FLOAT128) && defined(HAS_LOGF128)
|
|
Constant *GetConstantFoldFPValue128(float128 V, Type *Ty) {
|
|
if (Ty->isFP128Ty())
|
|
return ConstantFP::get(Ty, V);
|
|
llvm_unreachable("Can only constant fold fp128");
|
|
}
|
|
#endif
|
|
|
|
/// Clear the floating-point exception state.
|
|
inline void llvm_fenv_clearexcept() {
|
|
#if HAVE_DECL_FE_ALL_EXCEPT
|
|
feclearexcept(FE_ALL_EXCEPT);
|
|
#endif
|
|
errno = 0;
|
|
}
|
|
|
|
/// Test if a floating-point exception was raised.
|
|
inline bool llvm_fenv_testexcept() {
|
|
int errno_val = errno;
|
|
if (errno_val == ERANGE || errno_val == EDOM)
|
|
return true;
|
|
#if HAVE_DECL_FE_ALL_EXCEPT && HAVE_DECL_FE_INEXACT
|
|
if (fetestexcept(FE_ALL_EXCEPT & ~FE_INEXACT))
|
|
return true;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
static APFloat FTZPreserveSign(const APFloat &V) {
|
|
if (V.isDenormal())
|
|
return APFloat::getZero(V.getSemantics(), V.isNegative());
|
|
return V;
|
|
}
|
|
|
|
static APFloat FlushToPositiveZero(const APFloat &V) {
|
|
if (V.isDenormal())
|
|
return APFloat::getZero(V.getSemantics(), false);
|
|
return V;
|
|
}
|
|
|
|
static APFloat FlushWithDenormKind(const APFloat &V,
|
|
DenormalMode::DenormalModeKind DenormKind) {
|
|
assert(DenormKind != DenormalMode::DenormalModeKind::Invalid &&
|
|
DenormKind != DenormalMode::DenormalModeKind::Dynamic);
|
|
switch (DenormKind) {
|
|
case DenormalMode::DenormalModeKind::IEEE:
|
|
return V;
|
|
case DenormalMode::DenormalModeKind::PreserveSign:
|
|
return FTZPreserveSign(V);
|
|
case DenormalMode::DenormalModeKind::PositiveZero:
|
|
return FlushToPositiveZero(V);
|
|
default:
|
|
llvm_unreachable("Invalid denormal mode!");
|
|
}
|
|
}
|
|
|
|
Constant *ConstantFoldFP(double (*NativeFP)(double), const APFloat &V, Type *Ty,
|
|
DenormalMode DenormMode = DenormalMode::getIEEE()) {
|
|
if (!DenormMode.isValid() ||
|
|
DenormMode.Input == DenormalMode::DenormalModeKind::Dynamic ||
|
|
DenormMode.Output == DenormalMode::DenormalModeKind::Dynamic)
|
|
return nullptr;
|
|
|
|
llvm_fenv_clearexcept();
|
|
auto Input = FlushWithDenormKind(V, DenormMode.Input);
|
|
double Result = NativeFP(Input.convertToDouble());
|
|
if (llvm_fenv_testexcept()) {
|
|
llvm_fenv_clearexcept();
|
|
return nullptr;
|
|
}
|
|
|
|
Constant *Output = GetConstantFoldFPValue(Result, Ty);
|
|
if (DenormMode.Output == DenormalMode::DenormalModeKind::IEEE)
|
|
return Output;
|
|
const auto *CFP = static_cast<ConstantFP *>(Output);
|
|
const auto Res = FlushWithDenormKind(CFP->getValueAPF(), DenormMode.Output);
|
|
return ConstantFP::get(Ty->getContext(), Res);
|
|
}
|
|
|
|
#if defined(HAS_IEE754_FLOAT128) && defined(HAS_LOGF128)
|
|
Constant *ConstantFoldFP128(float128 (*NativeFP)(float128), const APFloat &V,
|
|
Type *Ty) {
|
|
llvm_fenv_clearexcept();
|
|
float128 Result = NativeFP(V.convertToQuad());
|
|
if (llvm_fenv_testexcept()) {
|
|
llvm_fenv_clearexcept();
|
|
return nullptr;
|
|
}
|
|
|
|
return GetConstantFoldFPValue128(Result, Ty);
|
|
}
|
|
#endif
|
|
|
|
Constant *ConstantFoldBinaryFP(double (*NativeFP)(double, double),
|
|
const APFloat &V, const APFloat &W, Type *Ty) {
|
|
llvm_fenv_clearexcept();
|
|
double Result = NativeFP(V.convertToDouble(), W.convertToDouble());
|
|
if (llvm_fenv_testexcept()) {
|
|
llvm_fenv_clearexcept();
|
|
return nullptr;
|
|
}
|
|
|
|
return GetConstantFoldFPValue(Result, Ty);
|
|
}
|
|
|
|
Constant *constantFoldVectorReduce(Intrinsic::ID IID, Constant *Op) {
|
|
FixedVectorType *VT = dyn_cast<FixedVectorType>(Op->getType());
|
|
if (!VT)
|
|
return nullptr;
|
|
|
|
// This isn't strictly necessary, but handle the special/common case of zero:
|
|
// all integer reductions of a zero input produce zero.
|
|
if (isa<ConstantAggregateZero>(Op))
|
|
return ConstantInt::get(VT->getElementType(), 0);
|
|
|
|
// This is the same as the underlying binops - poison propagates.
|
|
if (isa<PoisonValue>(Op) || Op->containsPoisonElement())
|
|
return PoisonValue::get(VT->getElementType());
|
|
|
|
// TODO: Handle undef.
|
|
if (!isa<ConstantVector>(Op) && !isa<ConstantDataVector>(Op))
|
|
return nullptr;
|
|
|
|
auto *EltC = dyn_cast<ConstantInt>(Op->getAggregateElement(0U));
|
|
if (!EltC)
|
|
return nullptr;
|
|
|
|
APInt Acc = EltC->getValue();
|
|
for (unsigned I = 1, E = VT->getNumElements(); I != E; I++) {
|
|
if (!(EltC = dyn_cast<ConstantInt>(Op->getAggregateElement(I))))
|
|
return nullptr;
|
|
const APInt &X = EltC->getValue();
|
|
switch (IID) {
|
|
case Intrinsic::vector_reduce_add:
|
|
Acc = Acc + X;
|
|
break;
|
|
case Intrinsic::vector_reduce_mul:
|
|
Acc = Acc * X;
|
|
break;
|
|
case Intrinsic::vector_reduce_and:
|
|
Acc = Acc & X;
|
|
break;
|
|
case Intrinsic::vector_reduce_or:
|
|
Acc = Acc | X;
|
|
break;
|
|
case Intrinsic::vector_reduce_xor:
|
|
Acc = Acc ^ X;
|
|
break;
|
|
case Intrinsic::vector_reduce_smin:
|
|
Acc = APIntOps::smin(Acc, X);
|
|
break;
|
|
case Intrinsic::vector_reduce_smax:
|
|
Acc = APIntOps::smax(Acc, X);
|
|
break;
|
|
case Intrinsic::vector_reduce_umin:
|
|
Acc = APIntOps::umin(Acc, X);
|
|
break;
|
|
case Intrinsic::vector_reduce_umax:
|
|
Acc = APIntOps::umax(Acc, X);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ConstantInt::get(Op->getContext(), Acc);
|
|
}
|
|
|
|
/// Attempt to fold an SSE floating point to integer conversion of a constant
|
|
/// floating point. If roundTowardZero is false, the default IEEE rounding is
|
|
/// used (toward nearest, ties to even). This matches the behavior of the
|
|
/// non-truncating SSE instructions in the default rounding mode. The desired
|
|
/// integer type Ty is used to select how many bits are available for the
|
|
/// result. Returns null if the conversion cannot be performed, otherwise
|
|
/// returns the Constant value resulting from the conversion.
|
|
Constant *ConstantFoldSSEConvertToInt(const APFloat &Val, bool roundTowardZero,
|
|
Type *Ty, bool IsSigned) {
|
|
// All of these conversion intrinsics form an integer of at most 64bits.
|
|
unsigned ResultWidth = Ty->getIntegerBitWidth();
|
|
assert(ResultWidth <= 64 &&
|
|
"Can only constant fold conversions to 64 and 32 bit ints");
|
|
|
|
uint64_t UIntVal;
|
|
bool isExact = false;
|
|
APFloat::roundingMode mode = roundTowardZero? APFloat::rmTowardZero
|
|
: APFloat::rmNearestTiesToEven;
|
|
APFloat::opStatus status =
|
|
Val.convertToInteger(MutableArrayRef(UIntVal), ResultWidth,
|
|
IsSigned, mode, &isExact);
|
|
if (status != APFloat::opOK &&
|
|
(!roundTowardZero || status != APFloat::opInexact))
|
|
return nullptr;
|
|
return ConstantInt::get(Ty, UIntVal, IsSigned);
|
|
}
|
|
|
|
double getValueAsDouble(ConstantFP *Op) {
|
|
Type *Ty = Op->getType();
|
|
|
|
if (Ty->isBFloatTy() || Ty->isHalfTy() || Ty->isFloatTy() || Ty->isDoubleTy())
|
|
return Op->getValueAPF().convertToDouble();
|
|
|
|
bool unused;
|
|
APFloat APF = Op->getValueAPF();
|
|
APF.convert(APFloat::IEEEdouble(), APFloat::rmNearestTiesToEven, &unused);
|
|
return APF.convertToDouble();
|
|
}
|
|
|
|
static bool getConstIntOrUndef(Value *Op, const APInt *&C) {
|
|
if (auto *CI = dyn_cast<ConstantInt>(Op)) {
|
|
C = &CI->getValue();
|
|
return true;
|
|
}
|
|
if (isa<UndefValue>(Op)) {
|
|
C = nullptr;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Checks if the given intrinsic call, which evaluates to constant, is allowed
|
|
/// to be folded.
|
|
///
|
|
/// \param CI Constrained intrinsic call.
|
|
/// \param St Exception flags raised during constant evaluation.
|
|
static bool mayFoldConstrained(ConstrainedFPIntrinsic *CI,
|
|
APFloat::opStatus St) {
|
|
std::optional<RoundingMode> ORM = CI->getRoundingMode();
|
|
std::optional<fp::ExceptionBehavior> EB = CI->getExceptionBehavior();
|
|
|
|
// If the operation does not change exception status flags, it is safe
|
|
// to fold.
|
|
if (St == APFloat::opStatus::opOK)
|
|
return true;
|
|
|
|
// If evaluation raised FP exception, the result can depend on rounding
|
|
// mode. If the latter is unknown, folding is not possible.
|
|
if (ORM == RoundingMode::Dynamic)
|
|
return false;
|
|
|
|
// If FP exceptions are ignored, fold the call, even if such exception is
|
|
// raised.
|
|
if (EB && *EB != fp::ExceptionBehavior::ebStrict)
|
|
return true;
|
|
|
|
// Leave the calculation for runtime so that exception flags be correctly set
|
|
// in hardware.
|
|
return false;
|
|
}
|
|
|
|
/// Returns the rounding mode that should be used for constant evaluation.
|
|
static RoundingMode
|
|
getEvaluationRoundingMode(const ConstrainedFPIntrinsic *CI) {
|
|
std::optional<RoundingMode> ORM = CI->getRoundingMode();
|
|
if (!ORM || *ORM == RoundingMode::Dynamic)
|
|
// Even if the rounding mode is unknown, try evaluating the operation.
|
|
// If it does not raise inexact exception, rounding was not applied,
|
|
// so the result is exact and does not depend on rounding mode. Whether
|
|
// other FP exceptions are raised, it does not depend on rounding mode.
|
|
return RoundingMode::NearestTiesToEven;
|
|
return *ORM;
|
|
}
|
|
|
|
/// Try to constant fold llvm.canonicalize for the given caller and value.
|
|
static Constant *constantFoldCanonicalize(const Type *Ty, const CallBase *CI,
|
|
const APFloat &Src) {
|
|
// Zero, positive and negative, is always OK to fold.
|
|
if (Src.isZero()) {
|
|
// Get a fresh 0, since ppc_fp128 does have non-canonical zeros.
|
|
return ConstantFP::get(
|
|
CI->getContext(),
|
|
APFloat::getZero(Src.getSemantics(), Src.isNegative()));
|
|
}
|
|
|
|
if (!Ty->isIEEELikeFPTy())
|
|
return nullptr;
|
|
|
|
// Zero is always canonical and the sign must be preserved.
|
|
//
|
|
// Denorms and nans may have special encodings, but it should be OK to fold a
|
|
// totally average number.
|
|
if (Src.isNormal() || Src.isInfinity())
|
|
return ConstantFP::get(CI->getContext(), Src);
|
|
|
|
if (Src.isDenormal() && CI->getParent() && CI->getFunction()) {
|
|
DenormalMode DenormMode =
|
|
CI->getFunction()->getDenormalMode(Src.getSemantics());
|
|
|
|
if (DenormMode == DenormalMode::getIEEE())
|
|
return ConstantFP::get(CI->getContext(), Src);
|
|
|
|
if (DenormMode.Input == DenormalMode::Dynamic)
|
|
return nullptr;
|
|
|
|
// If we know if either input or output is flushed, we can fold.
|
|
if ((DenormMode.Input == DenormalMode::Dynamic &&
|
|
DenormMode.Output == DenormalMode::IEEE) ||
|
|
(DenormMode.Input == DenormalMode::IEEE &&
|
|
DenormMode.Output == DenormalMode::Dynamic))
|
|
return nullptr;
|
|
|
|
bool IsPositive =
|
|
(!Src.isNegative() || DenormMode.Input == DenormalMode::PositiveZero ||
|
|
(DenormMode.Output == DenormalMode::PositiveZero &&
|
|
DenormMode.Input == DenormalMode::IEEE));
|
|
|
|
return ConstantFP::get(CI->getContext(),
|
|
APFloat::getZero(Src.getSemantics(), !IsPositive));
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static Constant *ConstantFoldScalarCall1(StringRef Name,
|
|
Intrinsic::ID IntrinsicID,
|
|
Type *Ty,
|
|
ArrayRef<Constant *> Operands,
|
|
const TargetLibraryInfo *TLI,
|
|
const CallBase *Call) {
|
|
assert(Operands.size() == 1 && "Wrong number of operands.");
|
|
|
|
if (IntrinsicID == Intrinsic::is_constant) {
|
|
// We know we have a "Constant" argument. But we want to only
|
|
// return true for manifest constants, not those that depend on
|
|
// constants with unknowable values, e.g. GlobalValue or BlockAddress.
|
|
if (Operands[0]->isManifestConstant())
|
|
return ConstantInt::getTrue(Ty->getContext());
|
|
return nullptr;
|
|
}
|
|
|
|
if (isa<UndefValue>(Operands[0])) {
|
|
// cosine(arg) is between -1 and 1. cosine(invalid arg) is NaN.
|
|
// ctpop() is between 0 and bitwidth, pick 0 for undef.
|
|
// fptoui.sat and fptosi.sat can always fold to zero (for a zero input).
|
|
if (IntrinsicID == Intrinsic::cos ||
|
|
IntrinsicID == Intrinsic::ctpop ||
|
|
IntrinsicID == Intrinsic::fptoui_sat ||
|
|
IntrinsicID == Intrinsic::fptosi_sat ||
|
|
IntrinsicID == Intrinsic::canonicalize)
|
|
return Constant::getNullValue(Ty);
|
|
if (IntrinsicID == Intrinsic::bswap ||
|
|
IntrinsicID == Intrinsic::bitreverse ||
|
|
IntrinsicID == Intrinsic::launder_invariant_group ||
|
|
IntrinsicID == Intrinsic::strip_invariant_group)
|
|
return Operands[0];
|
|
}
|
|
|
|
if (isa<ConstantPointerNull>(Operands[0])) {
|
|
// launder(null) == null == strip(null) iff in addrspace 0
|
|
if (IntrinsicID == Intrinsic::launder_invariant_group ||
|
|
IntrinsicID == Intrinsic::strip_invariant_group) {
|
|
// If instruction is not yet put in a basic block (e.g. when cloning
|
|
// a function during inlining), Call's caller may not be available.
|
|
// So check Call's BB first before querying Call->getCaller.
|
|
const Function *Caller =
|
|
Call->getParent() ? Call->getCaller() : nullptr;
|
|
if (Caller &&
|
|
!NullPointerIsDefined(
|
|
Caller, Operands[0]->getType()->getPointerAddressSpace())) {
|
|
return Operands[0];
|
|
}
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if (auto *Op = dyn_cast<ConstantFP>(Operands[0])) {
|
|
if (IntrinsicID == Intrinsic::convert_to_fp16) {
|
|
APFloat Val(Op->getValueAPF());
|
|
|
|
bool lost = false;
|
|
Val.convert(APFloat::IEEEhalf(), APFloat::rmNearestTiesToEven, &lost);
|
|
|
|
return ConstantInt::get(Ty->getContext(), Val.bitcastToAPInt());
|
|
}
|
|
|
|
APFloat U = Op->getValueAPF();
|
|
|
|
if (IntrinsicID == Intrinsic::wasm_trunc_signed ||
|
|
IntrinsicID == Intrinsic::wasm_trunc_unsigned) {
|
|
bool Signed = IntrinsicID == Intrinsic::wasm_trunc_signed;
|
|
|
|
if (U.isNaN())
|
|
return nullptr;
|
|
|
|
unsigned Width = Ty->getIntegerBitWidth();
|
|
APSInt Int(Width, !Signed);
|
|
bool IsExact = false;
|
|
APFloat::opStatus Status =
|
|
U.convertToInteger(Int, APFloat::rmTowardZero, &IsExact);
|
|
|
|
if (Status == APFloat::opOK || Status == APFloat::opInexact)
|
|
return ConstantInt::get(Ty, Int);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
if (IntrinsicID == Intrinsic::fptoui_sat ||
|
|
IntrinsicID == Intrinsic::fptosi_sat) {
|
|
// convertToInteger() already has the desired saturation semantics.
|
|
APSInt Int(Ty->getIntegerBitWidth(),
|
|
IntrinsicID == Intrinsic::fptoui_sat);
|
|
bool IsExact;
|
|
U.convertToInteger(Int, APFloat::rmTowardZero, &IsExact);
|
|
return ConstantInt::get(Ty, Int);
|
|
}
|
|
|
|
if (IntrinsicID == Intrinsic::canonicalize)
|
|
return constantFoldCanonicalize(Ty, Call, U);
|
|
|
|
#if defined(HAS_IEE754_FLOAT128) && defined(HAS_LOGF128)
|
|
if (Ty->isFP128Ty()) {
|
|
if (IntrinsicID == Intrinsic::log) {
|
|
float128 Result = logf128(Op->getValueAPF().convertToQuad());
|
|
return GetConstantFoldFPValue128(Result, Ty);
|
|
}
|
|
|
|
LibFunc Fp128Func = NotLibFunc;
|
|
if (TLI && TLI->getLibFunc(Name, Fp128Func) && TLI->has(Fp128Func) &&
|
|
Fp128Func == LibFunc_logl)
|
|
return ConstantFoldFP128(logf128, Op->getValueAPF(), Ty);
|
|
}
|
|
#endif
|
|
|
|
if (!Ty->isHalfTy() && !Ty->isFloatTy() && !Ty->isDoubleTy() &&
|
|
!Ty->isIntegerTy())
|
|
return nullptr;
|
|
|
|
// Use internal versions of these intrinsics.
|
|
|
|
if (IntrinsicID == Intrinsic::nearbyint || IntrinsicID == Intrinsic::rint) {
|
|
U.roundToIntegral(APFloat::rmNearestTiesToEven);
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
}
|
|
|
|
if (IntrinsicID == Intrinsic::round) {
|
|
U.roundToIntegral(APFloat::rmNearestTiesToAway);
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
}
|
|
|
|
if (IntrinsicID == Intrinsic::roundeven) {
|
|
U.roundToIntegral(APFloat::rmNearestTiesToEven);
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
}
|
|
|
|
if (IntrinsicID == Intrinsic::ceil) {
|
|
U.roundToIntegral(APFloat::rmTowardPositive);
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
}
|
|
|
|
if (IntrinsicID == Intrinsic::floor) {
|
|
U.roundToIntegral(APFloat::rmTowardNegative);
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
}
|
|
|
|
if (IntrinsicID == Intrinsic::trunc) {
|
|
U.roundToIntegral(APFloat::rmTowardZero);
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
}
|
|
|
|
if (IntrinsicID == Intrinsic::fabs) {
|
|
U.clearSign();
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
}
|
|
|
|
if (IntrinsicID == Intrinsic::amdgcn_fract) {
|
|
// The v_fract instruction behaves like the OpenCL spec, which defines
|
|
// fract(x) as fmin(x - floor(x), 0x1.fffffep-1f): "The min() operator is
|
|
// there to prevent fract(-small) from returning 1.0. It returns the
|
|
// largest positive floating-point number less than 1.0."
|
|
APFloat FloorU(U);
|
|
FloorU.roundToIntegral(APFloat::rmTowardNegative);
|
|
APFloat FractU(U - FloorU);
|
|
APFloat AlmostOne(U.getSemantics(), 1);
|
|
AlmostOne.next(/*nextDown*/ true);
|
|
return ConstantFP::get(Ty->getContext(), minimum(FractU, AlmostOne));
|
|
}
|
|
|
|
// Rounding operations (floor, trunc, ceil, round and nearbyint) do not
|
|
// raise FP exceptions, unless the argument is signaling NaN.
|
|
|
|
std::optional<APFloat::roundingMode> RM;
|
|
switch (IntrinsicID) {
|
|
default:
|
|
break;
|
|
case Intrinsic::experimental_constrained_nearbyint:
|
|
case Intrinsic::experimental_constrained_rint: {
|
|
auto CI = cast<ConstrainedFPIntrinsic>(Call);
|
|
RM = CI->getRoundingMode();
|
|
if (!RM || *RM == RoundingMode::Dynamic)
|
|
return nullptr;
|
|
break;
|
|
}
|
|
case Intrinsic::experimental_constrained_round:
|
|
RM = APFloat::rmNearestTiesToAway;
|
|
break;
|
|
case Intrinsic::experimental_constrained_ceil:
|
|
RM = APFloat::rmTowardPositive;
|
|
break;
|
|
case Intrinsic::experimental_constrained_floor:
|
|
RM = APFloat::rmTowardNegative;
|
|
break;
|
|
case Intrinsic::experimental_constrained_trunc:
|
|
RM = APFloat::rmTowardZero;
|
|
break;
|
|
}
|
|
if (RM) {
|
|
auto CI = cast<ConstrainedFPIntrinsic>(Call);
|
|
if (U.isFinite()) {
|
|
APFloat::opStatus St = U.roundToIntegral(*RM);
|
|
if (IntrinsicID == Intrinsic::experimental_constrained_rint &&
|
|
St == APFloat::opInexact) {
|
|
std::optional<fp::ExceptionBehavior> EB = CI->getExceptionBehavior();
|
|
if (EB == fp::ebStrict)
|
|
return nullptr;
|
|
}
|
|
} else if (U.isSignaling()) {
|
|
std::optional<fp::ExceptionBehavior> EB = CI->getExceptionBehavior();
|
|
if (EB && *EB != fp::ebIgnore)
|
|
return nullptr;
|
|
U = APFloat::getQNaN(U.getSemantics());
|
|
}
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
}
|
|
|
|
// NVVM float/double to signed/unsigned int32/int64 conversions:
|
|
switch (IntrinsicID) {
|
|
// f2i
|
|
case Intrinsic::nvvm_f2i_rm:
|
|
case Intrinsic::nvvm_f2i_rn:
|
|
case Intrinsic::nvvm_f2i_rp:
|
|
case Intrinsic::nvvm_f2i_rz:
|
|
case Intrinsic::nvvm_f2i_rm_ftz:
|
|
case Intrinsic::nvvm_f2i_rn_ftz:
|
|
case Intrinsic::nvvm_f2i_rp_ftz:
|
|
case Intrinsic::nvvm_f2i_rz_ftz:
|
|
// f2ui
|
|
case Intrinsic::nvvm_f2ui_rm:
|
|
case Intrinsic::nvvm_f2ui_rn:
|
|
case Intrinsic::nvvm_f2ui_rp:
|
|
case Intrinsic::nvvm_f2ui_rz:
|
|
case Intrinsic::nvvm_f2ui_rm_ftz:
|
|
case Intrinsic::nvvm_f2ui_rn_ftz:
|
|
case Intrinsic::nvvm_f2ui_rp_ftz:
|
|
case Intrinsic::nvvm_f2ui_rz_ftz:
|
|
// d2i
|
|
case Intrinsic::nvvm_d2i_rm:
|
|
case Intrinsic::nvvm_d2i_rn:
|
|
case Intrinsic::nvvm_d2i_rp:
|
|
case Intrinsic::nvvm_d2i_rz:
|
|
// d2ui
|
|
case Intrinsic::nvvm_d2ui_rm:
|
|
case Intrinsic::nvvm_d2ui_rn:
|
|
case Intrinsic::nvvm_d2ui_rp:
|
|
case Intrinsic::nvvm_d2ui_rz:
|
|
// f2ll
|
|
case Intrinsic::nvvm_f2ll_rm:
|
|
case Intrinsic::nvvm_f2ll_rn:
|
|
case Intrinsic::nvvm_f2ll_rp:
|
|
case Intrinsic::nvvm_f2ll_rz:
|
|
case Intrinsic::nvvm_f2ll_rm_ftz:
|
|
case Intrinsic::nvvm_f2ll_rn_ftz:
|
|
case Intrinsic::nvvm_f2ll_rp_ftz:
|
|
case Intrinsic::nvvm_f2ll_rz_ftz:
|
|
// f2ull
|
|
case Intrinsic::nvvm_f2ull_rm:
|
|
case Intrinsic::nvvm_f2ull_rn:
|
|
case Intrinsic::nvvm_f2ull_rp:
|
|
case Intrinsic::nvvm_f2ull_rz:
|
|
case Intrinsic::nvvm_f2ull_rm_ftz:
|
|
case Intrinsic::nvvm_f2ull_rn_ftz:
|
|
case Intrinsic::nvvm_f2ull_rp_ftz:
|
|
case Intrinsic::nvvm_f2ull_rz_ftz:
|
|
// d2ll
|
|
case Intrinsic::nvvm_d2ll_rm:
|
|
case Intrinsic::nvvm_d2ll_rn:
|
|
case Intrinsic::nvvm_d2ll_rp:
|
|
case Intrinsic::nvvm_d2ll_rz:
|
|
// d2ull
|
|
case Intrinsic::nvvm_d2ull_rm:
|
|
case Intrinsic::nvvm_d2ull_rn:
|
|
case Intrinsic::nvvm_d2ull_rp:
|
|
case Intrinsic::nvvm_d2ull_rz: {
|
|
// In float-to-integer conversion, NaN inputs are converted to 0.
|
|
if (U.isNaN())
|
|
return ConstantInt::get(Ty, 0);
|
|
|
|
APFloat::roundingMode RMode =
|
|
nvvm::GetFPToIntegerRoundingMode(IntrinsicID);
|
|
bool IsFTZ = nvvm::FPToIntegerIntrinsicShouldFTZ(IntrinsicID);
|
|
bool IsSigned = nvvm::FPToIntegerIntrinsicResultIsSigned(IntrinsicID);
|
|
|
|
APSInt ResInt(Ty->getIntegerBitWidth(), !IsSigned);
|
|
auto FloatToRound = IsFTZ ? FTZPreserveSign(U) : U;
|
|
|
|
bool IsExact = false;
|
|
APFloat::opStatus Status =
|
|
FloatToRound.convertToInteger(ResInt, RMode, &IsExact);
|
|
|
|
if (Status != APFloat::opInvalidOp)
|
|
return ConstantInt::get(Ty, ResInt);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
/// We only fold functions with finite arguments. Folding NaN and inf is
|
|
/// likely to be aborted with an exception anyway, and some host libms
|
|
/// have known errors raising exceptions.
|
|
if (!U.isFinite())
|
|
return nullptr;
|
|
|
|
/// Currently APFloat versions of these functions do not exist, so we use
|
|
/// the host native double versions. Float versions are not called
|
|
/// directly but for all these it is true (float)(f((double)arg)) ==
|
|
/// f(arg). Long double not supported yet.
|
|
const APFloat &APF = Op->getValueAPF();
|
|
|
|
switch (IntrinsicID) {
|
|
default: break;
|
|
case Intrinsic::log:
|
|
return ConstantFoldFP(log, APF, Ty);
|
|
case Intrinsic::log2:
|
|
// TODO: What about hosts that lack a C99 library?
|
|
return ConstantFoldFP(log2, APF, Ty);
|
|
case Intrinsic::log10:
|
|
// TODO: What about hosts that lack a C99 library?
|
|
return ConstantFoldFP(log10, APF, Ty);
|
|
case Intrinsic::exp:
|
|
return ConstantFoldFP(exp, APF, Ty);
|
|
case Intrinsic::exp2:
|
|
// Fold exp2(x) as pow(2, x), in case the host lacks a C99 library.
|
|
return ConstantFoldBinaryFP(pow, APFloat(2.0), APF, Ty);
|
|
case Intrinsic::exp10:
|
|
// Fold exp10(x) as pow(10, x), in case the host lacks a C99 library.
|
|
return ConstantFoldBinaryFP(pow, APFloat(10.0), APF, Ty);
|
|
case Intrinsic::sin:
|
|
return ConstantFoldFP(sin, APF, Ty);
|
|
case Intrinsic::cos:
|
|
return ConstantFoldFP(cos, APF, Ty);
|
|
case Intrinsic::sinh:
|
|
return ConstantFoldFP(sinh, APF, Ty);
|
|
case Intrinsic::cosh:
|
|
return ConstantFoldFP(cosh, APF, Ty);
|
|
case Intrinsic::atan:
|
|
// Implement optional behavior from C's Annex F for +/-0.0.
|
|
if (U.isZero())
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
return ConstantFoldFP(atan, APF, Ty);
|
|
case Intrinsic::sqrt:
|
|
return ConstantFoldFP(sqrt, APF, Ty);
|
|
|
|
// NVVM Intrinsics:
|
|
case Intrinsic::nvvm_ceil_ftz_f:
|
|
case Intrinsic::nvvm_ceil_f:
|
|
case Intrinsic::nvvm_ceil_d:
|
|
return ConstantFoldFP(
|
|
ceil, APF, Ty,
|
|
nvvm::GetNVVMDenormMode(
|
|
nvvm::UnaryMathIntrinsicShouldFTZ(IntrinsicID)));
|
|
|
|
case Intrinsic::nvvm_fabs_ftz:
|
|
case Intrinsic::nvvm_fabs:
|
|
return ConstantFoldFP(
|
|
fabs, APF, Ty,
|
|
nvvm::GetNVVMDenormMode(
|
|
nvvm::UnaryMathIntrinsicShouldFTZ(IntrinsicID)));
|
|
|
|
case Intrinsic::nvvm_floor_ftz_f:
|
|
case Intrinsic::nvvm_floor_f:
|
|
case Intrinsic::nvvm_floor_d:
|
|
return ConstantFoldFP(
|
|
floor, APF, Ty,
|
|
nvvm::GetNVVMDenormMode(
|
|
nvvm::UnaryMathIntrinsicShouldFTZ(IntrinsicID)));
|
|
|
|
case Intrinsic::nvvm_rcp_rm_ftz_f:
|
|
case Intrinsic::nvvm_rcp_rn_ftz_f:
|
|
case Intrinsic::nvvm_rcp_rp_ftz_f:
|
|
case Intrinsic::nvvm_rcp_rz_ftz_f:
|
|
case Intrinsic::nvvm_rcp_rm_d:
|
|
case Intrinsic::nvvm_rcp_rm_f:
|
|
case Intrinsic::nvvm_rcp_rn_d:
|
|
case Intrinsic::nvvm_rcp_rn_f:
|
|
case Intrinsic::nvvm_rcp_rp_d:
|
|
case Intrinsic::nvvm_rcp_rp_f:
|
|
case Intrinsic::nvvm_rcp_rz_d:
|
|
case Intrinsic::nvvm_rcp_rz_f: {
|
|
APFloat::roundingMode RoundMode = nvvm::GetRCPRoundingMode(IntrinsicID);
|
|
bool IsFTZ = nvvm::RCPShouldFTZ(IntrinsicID);
|
|
|
|
auto Denominator = IsFTZ ? FTZPreserveSign(APF) : APF;
|
|
APFloat Res = APFloat::getOne(APF.getSemantics());
|
|
APFloat::opStatus Status = Res.divide(Denominator, RoundMode);
|
|
|
|
if (Status == APFloat::opOK || Status == APFloat::opInexact) {
|
|
if (IsFTZ)
|
|
Res = FTZPreserveSign(Res);
|
|
return ConstantFP::get(Ty->getContext(), Res);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
case Intrinsic::nvvm_round_ftz_f:
|
|
case Intrinsic::nvvm_round_f:
|
|
case Intrinsic::nvvm_round_d: {
|
|
// nvvm_round is lowered to PTX cvt.rni, which will round to nearest
|
|
// integer, choosing even integer if source is equidistant between two
|
|
// integers, so the semantics are closer to "rint" rather than "round".
|
|
bool IsFTZ = nvvm::UnaryMathIntrinsicShouldFTZ(IntrinsicID);
|
|
auto V = IsFTZ ? FTZPreserveSign(APF) : APF;
|
|
V.roundToIntegral(APFloat::rmNearestTiesToEven);
|
|
return ConstantFP::get(Ty->getContext(), V);
|
|
}
|
|
|
|
case Intrinsic::nvvm_saturate_ftz_f:
|
|
case Intrinsic::nvvm_saturate_d:
|
|
case Intrinsic::nvvm_saturate_f: {
|
|
bool IsFTZ = nvvm::UnaryMathIntrinsicShouldFTZ(IntrinsicID);
|
|
auto V = IsFTZ ? FTZPreserveSign(APF) : APF;
|
|
if (V.isNegative() || V.isZero() || V.isNaN())
|
|
return ConstantFP::getZero(Ty);
|
|
APFloat One = APFloat::getOne(APF.getSemantics());
|
|
if (V > One)
|
|
return ConstantFP::get(Ty->getContext(), One);
|
|
return ConstantFP::get(Ty->getContext(), APF);
|
|
}
|
|
|
|
case Intrinsic::nvvm_sqrt_rn_ftz_f:
|
|
case Intrinsic::nvvm_sqrt_f:
|
|
case Intrinsic::nvvm_sqrt_rn_d:
|
|
case Intrinsic::nvvm_sqrt_rn_f:
|
|
if (APF.isNegative())
|
|
return nullptr;
|
|
return ConstantFoldFP(
|
|
sqrt, APF, Ty,
|
|
nvvm::GetNVVMDenormMode(
|
|
nvvm::UnaryMathIntrinsicShouldFTZ(IntrinsicID)));
|
|
|
|
// AMDGCN Intrinsics:
|
|
case Intrinsic::amdgcn_cos:
|
|
case Intrinsic::amdgcn_sin: {
|
|
double V = getValueAsDouble(Op);
|
|
if (V < -256.0 || V > 256.0)
|
|
// The gfx8 and gfx9 architectures handle arguments outside the range
|
|
// [-256, 256] differently. This should be a rare case so bail out
|
|
// rather than trying to handle the difference.
|
|
return nullptr;
|
|
bool IsCos = IntrinsicID == Intrinsic::amdgcn_cos;
|
|
double V4 = V * 4.0;
|
|
if (V4 == floor(V4)) {
|
|
// Force exact results for quarter-integer inputs.
|
|
const double SinVals[4] = { 0.0, 1.0, 0.0, -1.0 };
|
|
V = SinVals[((int)V4 + (IsCos ? 1 : 0)) & 3];
|
|
} else {
|
|
if (IsCos)
|
|
V = cos(V * 2.0 * numbers::pi);
|
|
else
|
|
V = sin(V * 2.0 * numbers::pi);
|
|
}
|
|
return GetConstantFoldFPValue(V, Ty);
|
|
}
|
|
}
|
|
|
|
if (!TLI)
|
|
return nullptr;
|
|
|
|
LibFunc Func = NotLibFunc;
|
|
if (!TLI->getLibFunc(Name, Func))
|
|
return nullptr;
|
|
|
|
switch (Func) {
|
|
default:
|
|
break;
|
|
case LibFunc_acos:
|
|
case LibFunc_acosf:
|
|
case LibFunc_acos_finite:
|
|
case LibFunc_acosf_finite:
|
|
if (TLI->has(Func))
|
|
return ConstantFoldFP(acos, APF, Ty);
|
|
break;
|
|
case LibFunc_asin:
|
|
case LibFunc_asinf:
|
|
case LibFunc_asin_finite:
|
|
case LibFunc_asinf_finite:
|
|
if (TLI->has(Func))
|
|
return ConstantFoldFP(asin, APF, Ty);
|
|
break;
|
|
case LibFunc_atan:
|
|
case LibFunc_atanf:
|
|
// Implement optional behavior from C's Annex F for +/-0.0.
|
|
if (U.isZero())
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
if (TLI->has(Func))
|
|
return ConstantFoldFP(atan, APF, Ty);
|
|
break;
|
|
case LibFunc_ceil:
|
|
case LibFunc_ceilf:
|
|
if (TLI->has(Func)) {
|
|
U.roundToIntegral(APFloat::rmTowardPositive);
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
}
|
|
break;
|
|
case LibFunc_cos:
|
|
case LibFunc_cosf:
|
|
if (TLI->has(Func))
|
|
return ConstantFoldFP(cos, APF, Ty);
|
|
break;
|
|
case LibFunc_cosh:
|
|
case LibFunc_coshf:
|
|
case LibFunc_cosh_finite:
|
|
case LibFunc_coshf_finite:
|
|
if (TLI->has(Func))
|
|
return ConstantFoldFP(cosh, APF, Ty);
|
|
break;
|
|
case LibFunc_exp:
|
|
case LibFunc_expf:
|
|
case LibFunc_exp_finite:
|
|
case LibFunc_expf_finite:
|
|
if (TLI->has(Func))
|
|
return ConstantFoldFP(exp, APF, Ty);
|
|
break;
|
|
case LibFunc_exp2:
|
|
case LibFunc_exp2f:
|
|
case LibFunc_exp2_finite:
|
|
case LibFunc_exp2f_finite:
|
|
if (TLI->has(Func))
|
|
// Fold exp2(x) as pow(2, x), in case the host lacks a C99 library.
|
|
return ConstantFoldBinaryFP(pow, APFloat(2.0), APF, Ty);
|
|
break;
|
|
case LibFunc_fabs:
|
|
case LibFunc_fabsf:
|
|
if (TLI->has(Func)) {
|
|
U.clearSign();
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
}
|
|
break;
|
|
case LibFunc_floor:
|
|
case LibFunc_floorf:
|
|
if (TLI->has(Func)) {
|
|
U.roundToIntegral(APFloat::rmTowardNegative);
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
}
|
|
break;
|
|
case LibFunc_log:
|
|
case LibFunc_logf:
|
|
case LibFunc_log_finite:
|
|
case LibFunc_logf_finite:
|
|
if (!APF.isNegative() && !APF.isZero() && TLI->has(Func))
|
|
return ConstantFoldFP(log, APF, Ty);
|
|
break;
|
|
case LibFunc_log2:
|
|
case LibFunc_log2f:
|
|
case LibFunc_log2_finite:
|
|
case LibFunc_log2f_finite:
|
|
if (!APF.isNegative() && !APF.isZero() && TLI->has(Func))
|
|
// TODO: What about hosts that lack a C99 library?
|
|
return ConstantFoldFP(log2, APF, Ty);
|
|
break;
|
|
case LibFunc_log10:
|
|
case LibFunc_log10f:
|
|
case LibFunc_log10_finite:
|
|
case LibFunc_log10f_finite:
|
|
if (!APF.isNegative() && !APF.isZero() && TLI->has(Func))
|
|
// TODO: What about hosts that lack a C99 library?
|
|
return ConstantFoldFP(log10, APF, Ty);
|
|
break;
|
|
case LibFunc_ilogb:
|
|
case LibFunc_ilogbf:
|
|
if (!APF.isZero() && TLI->has(Func))
|
|
return ConstantInt::get(Ty, ilogb(APF), true);
|
|
break;
|
|
case LibFunc_logb:
|
|
case LibFunc_logbf:
|
|
if (!APF.isZero() && TLI->has(Func))
|
|
return ConstantFoldFP(logb, APF, Ty);
|
|
break;
|
|
case LibFunc_log1p:
|
|
case LibFunc_log1pf:
|
|
// Implement optional behavior from C's Annex F for +/-0.0.
|
|
if (U.isZero())
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
if (APF > APFloat::getOne(APF.getSemantics(), true) && TLI->has(Func))
|
|
return ConstantFoldFP(log1p, APF, Ty);
|
|
break;
|
|
case LibFunc_logl:
|
|
return nullptr;
|
|
case LibFunc_erf:
|
|
case LibFunc_erff:
|
|
if (TLI->has(Func))
|
|
return ConstantFoldFP(erf, APF, Ty);
|
|
break;
|
|
case LibFunc_nearbyint:
|
|
case LibFunc_nearbyintf:
|
|
case LibFunc_rint:
|
|
case LibFunc_rintf:
|
|
if (TLI->has(Func)) {
|
|
U.roundToIntegral(APFloat::rmNearestTiesToEven);
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
}
|
|
break;
|
|
case LibFunc_round:
|
|
case LibFunc_roundf:
|
|
if (TLI->has(Func)) {
|
|
U.roundToIntegral(APFloat::rmNearestTiesToAway);
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
}
|
|
break;
|
|
case LibFunc_sin:
|
|
case LibFunc_sinf:
|
|
if (TLI->has(Func))
|
|
return ConstantFoldFP(sin, APF, Ty);
|
|
break;
|
|
case LibFunc_sinh:
|
|
case LibFunc_sinhf:
|
|
case LibFunc_sinh_finite:
|
|
case LibFunc_sinhf_finite:
|
|
if (TLI->has(Func))
|
|
return ConstantFoldFP(sinh, APF, Ty);
|
|
break;
|
|
case LibFunc_sqrt:
|
|
case LibFunc_sqrtf:
|
|
if (!APF.isNegative() && TLI->has(Func))
|
|
return ConstantFoldFP(sqrt, APF, Ty);
|
|
break;
|
|
case LibFunc_tan:
|
|
case LibFunc_tanf:
|
|
if (TLI->has(Func))
|
|
return ConstantFoldFP(tan, APF, Ty);
|
|
break;
|
|
case LibFunc_tanh:
|
|
case LibFunc_tanhf:
|
|
if (TLI->has(Func))
|
|
return ConstantFoldFP(tanh, APF, Ty);
|
|
break;
|
|
case LibFunc_trunc:
|
|
case LibFunc_truncf:
|
|
if (TLI->has(Func)) {
|
|
U.roundToIntegral(APFloat::rmTowardZero);
|
|
return ConstantFP::get(Ty->getContext(), U);
|
|
}
|
|
break;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
if (auto *Op = dyn_cast<ConstantInt>(Operands[0])) {
|
|
switch (IntrinsicID) {
|
|
case Intrinsic::bswap:
|
|
return ConstantInt::get(Ty->getContext(), Op->getValue().byteSwap());
|
|
case Intrinsic::ctpop:
|
|
return ConstantInt::get(Ty, Op->getValue().popcount());
|
|
case Intrinsic::bitreverse:
|
|
return ConstantInt::get(Ty->getContext(), Op->getValue().reverseBits());
|
|
case Intrinsic::convert_from_fp16: {
|
|
APFloat Val(APFloat::IEEEhalf(), Op->getValue());
|
|
|
|
bool lost = false;
|
|
APFloat::opStatus status = Val.convert(
|
|
Ty->getFltSemantics(), APFloat::rmNearestTiesToEven, &lost);
|
|
|
|
// Conversion is always precise.
|
|
(void)status;
|
|
assert(status != APFloat::opInexact && !lost &&
|
|
"Precision lost during fp16 constfolding");
|
|
|
|
return ConstantFP::get(Ty->getContext(), Val);
|
|
}
|
|
|
|
case Intrinsic::amdgcn_s_wqm: {
|
|
uint64_t Val = Op->getZExtValue();
|
|
Val |= (Val & 0x5555555555555555ULL) << 1 |
|
|
((Val >> 1) & 0x5555555555555555ULL);
|
|
Val |= (Val & 0x3333333333333333ULL) << 2 |
|
|
((Val >> 2) & 0x3333333333333333ULL);
|
|
return ConstantInt::get(Ty, Val);
|
|
}
|
|
|
|
case Intrinsic::amdgcn_s_quadmask: {
|
|
uint64_t Val = Op->getZExtValue();
|
|
uint64_t QuadMask = 0;
|
|
for (unsigned I = 0; I < Op->getBitWidth() / 4; ++I, Val >>= 4) {
|
|
if (!(Val & 0xF))
|
|
continue;
|
|
|
|
QuadMask |= (1ULL << I);
|
|
}
|
|
return ConstantInt::get(Ty, QuadMask);
|
|
}
|
|
|
|
case Intrinsic::amdgcn_s_bitreplicate: {
|
|
uint64_t Val = Op->getZExtValue();
|
|
Val = (Val & 0x000000000000FFFFULL) | (Val & 0x00000000FFFF0000ULL) << 16;
|
|
Val = (Val & 0x000000FF000000FFULL) | (Val & 0x0000FF000000FF00ULL) << 8;
|
|
Val = (Val & 0x000F000F000F000FULL) | (Val & 0x00F000F000F000F0ULL) << 4;
|
|
Val = (Val & 0x0303030303030303ULL) | (Val & 0x0C0C0C0C0C0C0C0CULL) << 2;
|
|
Val = (Val & 0x1111111111111111ULL) | (Val & 0x2222222222222222ULL) << 1;
|
|
Val = Val | Val << 1;
|
|
return ConstantInt::get(Ty, Val);
|
|
}
|
|
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
switch (IntrinsicID) {
|
|
default: break;
|
|
case Intrinsic::vector_reduce_add:
|
|
case Intrinsic::vector_reduce_mul:
|
|
case Intrinsic::vector_reduce_and:
|
|
case Intrinsic::vector_reduce_or:
|
|
case Intrinsic::vector_reduce_xor:
|
|
case Intrinsic::vector_reduce_smin:
|
|
case Intrinsic::vector_reduce_smax:
|
|
case Intrinsic::vector_reduce_umin:
|
|
case Intrinsic::vector_reduce_umax:
|
|
if (Constant *C = constantFoldVectorReduce(IntrinsicID, Operands[0]))
|
|
return C;
|
|
break;
|
|
}
|
|
|
|
// Support ConstantVector in case we have an Undef in the top.
|
|
if (isa<ConstantVector>(Operands[0]) ||
|
|
isa<ConstantDataVector>(Operands[0]) ||
|
|
isa<ConstantAggregateZero>(Operands[0])) {
|
|
auto *Op = cast<Constant>(Operands[0]);
|
|
switch (IntrinsicID) {
|
|
default: break;
|
|
case Intrinsic::x86_sse_cvtss2si:
|
|
case Intrinsic::x86_sse_cvtss2si64:
|
|
case Intrinsic::x86_sse2_cvtsd2si:
|
|
case Intrinsic::x86_sse2_cvtsd2si64:
|
|
if (ConstantFP *FPOp =
|
|
dyn_cast_or_null<ConstantFP>(Op->getAggregateElement(0U)))
|
|
return ConstantFoldSSEConvertToInt(FPOp->getValueAPF(),
|
|
/*roundTowardZero=*/false, Ty,
|
|
/*IsSigned*/true);
|
|
break;
|
|
case Intrinsic::x86_sse_cvttss2si:
|
|
case Intrinsic::x86_sse_cvttss2si64:
|
|
case Intrinsic::x86_sse2_cvttsd2si:
|
|
case Intrinsic::x86_sse2_cvttsd2si64:
|
|
if (ConstantFP *FPOp =
|
|
dyn_cast_or_null<ConstantFP>(Op->getAggregateElement(0U)))
|
|
return ConstantFoldSSEConvertToInt(FPOp->getValueAPF(),
|
|
/*roundTowardZero=*/true, Ty,
|
|
/*IsSigned*/true);
|
|
break;
|
|
|
|
case Intrinsic::wasm_anytrue:
|
|
return Op->isZeroValue() ? ConstantInt::get(Ty, 0)
|
|
: ConstantInt::get(Ty, 1);
|
|
|
|
case Intrinsic::wasm_alltrue:
|
|
// Check each element individually
|
|
unsigned E = cast<FixedVectorType>(Op->getType())->getNumElements();
|
|
for (unsigned I = 0; I != E; ++I)
|
|
if (Constant *Elt = Op->getAggregateElement(I))
|
|
if (Elt->isZeroValue())
|
|
return ConstantInt::get(Ty, 0);
|
|
|
|
return ConstantInt::get(Ty, 1);
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static Constant *evaluateCompare(const APFloat &Op1, const APFloat &Op2,
|
|
const ConstrainedFPIntrinsic *Call) {
|
|
APFloat::opStatus St = APFloat::opOK;
|
|
auto *FCmp = cast<ConstrainedFPCmpIntrinsic>(Call);
|
|
FCmpInst::Predicate Cond = FCmp->getPredicate();
|
|
if (FCmp->isSignaling()) {
|
|
if (Op1.isNaN() || Op2.isNaN())
|
|
St = APFloat::opInvalidOp;
|
|
} else {
|
|
if (Op1.isSignaling() || Op2.isSignaling())
|
|
St = APFloat::opInvalidOp;
|
|
}
|
|
bool Result = FCmpInst::compare(Op1, Op2, Cond);
|
|
if (mayFoldConstrained(const_cast<ConstrainedFPCmpIntrinsic *>(FCmp), St))
|
|
return ConstantInt::get(Call->getType()->getScalarType(), Result);
|
|
return nullptr;
|
|
}
|
|
|
|
static Constant *ConstantFoldLibCall2(StringRef Name, Type *Ty,
|
|
ArrayRef<Constant *> Operands,
|
|
const TargetLibraryInfo *TLI) {
|
|
if (!TLI)
|
|
return nullptr;
|
|
|
|
LibFunc Func = NotLibFunc;
|
|
if (!TLI->getLibFunc(Name, Func))
|
|
return nullptr;
|
|
|
|
const auto *Op1 = dyn_cast<ConstantFP>(Operands[0]);
|
|
if (!Op1)
|
|
return nullptr;
|
|
|
|
const auto *Op2 = dyn_cast<ConstantFP>(Operands[1]);
|
|
if (!Op2)
|
|
return nullptr;
|
|
|
|
const APFloat &Op1V = Op1->getValueAPF();
|
|
const APFloat &Op2V = Op2->getValueAPF();
|
|
|
|
switch (Func) {
|
|
default:
|
|
break;
|
|
case LibFunc_pow:
|
|
case LibFunc_powf:
|
|
case LibFunc_pow_finite:
|
|
case LibFunc_powf_finite:
|
|
if (TLI->has(Func))
|
|
return ConstantFoldBinaryFP(pow, Op1V, Op2V, Ty);
|
|
break;
|
|
case LibFunc_fmod:
|
|
case LibFunc_fmodf:
|
|
if (TLI->has(Func)) {
|
|
APFloat V = Op1->getValueAPF();
|
|
if (APFloat::opStatus::opOK == V.mod(Op2->getValueAPF()))
|
|
return ConstantFP::get(Ty->getContext(), V);
|
|
}
|
|
break;
|
|
case LibFunc_remainder:
|
|
case LibFunc_remainderf:
|
|
if (TLI->has(Func)) {
|
|
APFloat V = Op1->getValueAPF();
|
|
if (APFloat::opStatus::opOK == V.remainder(Op2->getValueAPF()))
|
|
return ConstantFP::get(Ty->getContext(), V);
|
|
}
|
|
break;
|
|
case LibFunc_atan2:
|
|
case LibFunc_atan2f:
|
|
// atan2(+/-0.0, +/-0.0) is known to raise an exception on some libm
|
|
// (Solaris), so we do not assume a known result for that.
|
|
if (Op1V.isZero() && Op2V.isZero())
|
|
return nullptr;
|
|
[[fallthrough]];
|
|
case LibFunc_atan2_finite:
|
|
case LibFunc_atan2f_finite:
|
|
if (TLI->has(Func))
|
|
return ConstantFoldBinaryFP(atan2, Op1V, Op2V, Ty);
|
|
break;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
|
|
ArrayRef<Constant *> Operands,
|
|
const CallBase *Call) {
|
|
assert(Operands.size() == 2 && "Wrong number of operands.");
|
|
|
|
if (Ty->isFloatingPointTy()) {
|
|
// TODO: We should have undef handling for all of the FP intrinsics that
|
|
// are attempted to be folded in this function.
|
|
bool IsOp0Undef = isa<UndefValue>(Operands[0]);
|
|
bool IsOp1Undef = isa<UndefValue>(Operands[1]);
|
|
switch (IntrinsicID) {
|
|
case Intrinsic::maxnum:
|
|
case Intrinsic::minnum:
|
|
case Intrinsic::maximum:
|
|
case Intrinsic::minimum:
|
|
case Intrinsic::maximumnum:
|
|
case Intrinsic::minimumnum:
|
|
case Intrinsic::nvvm_fmax_d:
|
|
case Intrinsic::nvvm_fmin_d:
|
|
// If one argument is undef, return the other argument.
|
|
if (IsOp0Undef)
|
|
return Operands[1];
|
|
if (IsOp1Undef)
|
|
return Operands[0];
|
|
break;
|
|
|
|
case Intrinsic::nvvm_fmax_f:
|
|
case Intrinsic::nvvm_fmax_ftz_f:
|
|
case Intrinsic::nvvm_fmax_ftz_nan_f:
|
|
case Intrinsic::nvvm_fmax_ftz_nan_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmax_ftz_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmax_nan_f:
|
|
case Intrinsic::nvvm_fmax_nan_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmax_xorsign_abs_f:
|
|
|
|
case Intrinsic::nvvm_fmin_f:
|
|
case Intrinsic::nvvm_fmin_ftz_f:
|
|
case Intrinsic::nvvm_fmin_ftz_nan_f:
|
|
case Intrinsic::nvvm_fmin_ftz_nan_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmin_ftz_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmin_nan_f:
|
|
case Intrinsic::nvvm_fmin_nan_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmin_xorsign_abs_f:
|
|
// If one arg is undef, the other arg can be returned only if it is
|
|
// constant, as we may need to flush it to sign-preserving zero or
|
|
// canonicalize the NaN.
|
|
if (!IsOp0Undef && !IsOp1Undef)
|
|
break;
|
|
if (auto *Op = dyn_cast<ConstantFP>(Operands[IsOp0Undef ? 1 : 0])) {
|
|
if (Op->isNaN()) {
|
|
APInt NVCanonicalNaN(32, 0x7fffffff);
|
|
return ConstantFP::get(
|
|
Ty, APFloat(Ty->getFltSemantics(), NVCanonicalNaN));
|
|
}
|
|
if (nvvm::FMinFMaxShouldFTZ(IntrinsicID))
|
|
return ConstantFP::get(Ty, FTZPreserveSign(Op->getValueAPF()));
|
|
else
|
|
return Op;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (const auto *Op1 = dyn_cast<ConstantFP>(Operands[0])) {
|
|
const APFloat &Op1V = Op1->getValueAPF();
|
|
|
|
if (const auto *Op2 = dyn_cast<ConstantFP>(Operands[1])) {
|
|
if (Op2->getType() != Op1->getType())
|
|
return nullptr;
|
|
const APFloat &Op2V = Op2->getValueAPF();
|
|
|
|
if (const auto *ConstrIntr =
|
|
dyn_cast_if_present<ConstrainedFPIntrinsic>(Call)) {
|
|
RoundingMode RM = getEvaluationRoundingMode(ConstrIntr);
|
|
APFloat Res = Op1V;
|
|
APFloat::opStatus St;
|
|
switch (IntrinsicID) {
|
|
default:
|
|
return nullptr;
|
|
case Intrinsic::experimental_constrained_fadd:
|
|
St = Res.add(Op2V, RM);
|
|
break;
|
|
case Intrinsic::experimental_constrained_fsub:
|
|
St = Res.subtract(Op2V, RM);
|
|
break;
|
|
case Intrinsic::experimental_constrained_fmul:
|
|
St = Res.multiply(Op2V, RM);
|
|
break;
|
|
case Intrinsic::experimental_constrained_fdiv:
|
|
St = Res.divide(Op2V, RM);
|
|
break;
|
|
case Intrinsic::experimental_constrained_frem:
|
|
St = Res.mod(Op2V);
|
|
break;
|
|
case Intrinsic::experimental_constrained_fcmp:
|
|
case Intrinsic::experimental_constrained_fcmps:
|
|
return evaluateCompare(Op1V, Op2V, ConstrIntr);
|
|
}
|
|
if (mayFoldConstrained(const_cast<ConstrainedFPIntrinsic *>(ConstrIntr),
|
|
St))
|
|
return ConstantFP::get(Ty->getContext(), Res);
|
|
return nullptr;
|
|
}
|
|
|
|
switch (IntrinsicID) {
|
|
default:
|
|
break;
|
|
case Intrinsic::copysign:
|
|
return ConstantFP::get(Ty->getContext(), APFloat::copySign(Op1V, Op2V));
|
|
case Intrinsic::minnum:
|
|
return ConstantFP::get(Ty->getContext(), minnum(Op1V, Op2V));
|
|
case Intrinsic::maxnum:
|
|
return ConstantFP::get(Ty->getContext(), maxnum(Op1V, Op2V));
|
|
case Intrinsic::minimum:
|
|
return ConstantFP::get(Ty->getContext(), minimum(Op1V, Op2V));
|
|
case Intrinsic::maximum:
|
|
return ConstantFP::get(Ty->getContext(), maximum(Op1V, Op2V));
|
|
case Intrinsic::minimumnum:
|
|
return ConstantFP::get(Ty->getContext(), minimumnum(Op1V, Op2V));
|
|
case Intrinsic::maximumnum:
|
|
return ConstantFP::get(Ty->getContext(), maximumnum(Op1V, Op2V));
|
|
|
|
case Intrinsic::nvvm_fmax_d:
|
|
case Intrinsic::nvvm_fmax_f:
|
|
case Intrinsic::nvvm_fmax_ftz_f:
|
|
case Intrinsic::nvvm_fmax_ftz_nan_f:
|
|
case Intrinsic::nvvm_fmax_ftz_nan_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmax_ftz_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmax_nan_f:
|
|
case Intrinsic::nvvm_fmax_nan_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmax_xorsign_abs_f:
|
|
|
|
case Intrinsic::nvvm_fmin_d:
|
|
case Intrinsic::nvvm_fmin_f:
|
|
case Intrinsic::nvvm_fmin_ftz_f:
|
|
case Intrinsic::nvvm_fmin_ftz_nan_f:
|
|
case Intrinsic::nvvm_fmin_ftz_nan_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmin_ftz_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmin_nan_f:
|
|
case Intrinsic::nvvm_fmin_nan_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmin_xorsign_abs_f: {
|
|
|
|
bool ShouldCanonicalizeNaNs = !(IntrinsicID == Intrinsic::nvvm_fmax_d ||
|
|
IntrinsicID == Intrinsic::nvvm_fmin_d);
|
|
bool IsFTZ = nvvm::FMinFMaxShouldFTZ(IntrinsicID);
|
|
bool IsNaNPropagating = nvvm::FMinFMaxPropagatesNaNs(IntrinsicID);
|
|
bool IsXorSignAbs = nvvm::FMinFMaxIsXorSignAbs(IntrinsicID);
|
|
|
|
APFloat A = IsFTZ ? FTZPreserveSign(Op1V) : Op1V;
|
|
APFloat B = IsFTZ ? FTZPreserveSign(Op2V) : Op2V;
|
|
|
|
bool XorSign = false;
|
|
if (IsXorSignAbs) {
|
|
XorSign = A.isNegative() ^ B.isNegative();
|
|
A = abs(A);
|
|
B = abs(B);
|
|
}
|
|
|
|
bool IsFMax = false;
|
|
switch (IntrinsicID) {
|
|
case Intrinsic::nvvm_fmax_d:
|
|
case Intrinsic::nvvm_fmax_f:
|
|
case Intrinsic::nvvm_fmax_ftz_f:
|
|
case Intrinsic::nvvm_fmax_ftz_nan_f:
|
|
case Intrinsic::nvvm_fmax_ftz_nan_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmax_ftz_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmax_nan_f:
|
|
case Intrinsic::nvvm_fmax_nan_xorsign_abs_f:
|
|
case Intrinsic::nvvm_fmax_xorsign_abs_f:
|
|
IsFMax = true;
|
|
break;
|
|
}
|
|
APFloat Res = IsFMax ? maximum(A, B) : minimum(A, B);
|
|
|
|
if (ShouldCanonicalizeNaNs) {
|
|
APFloat NVCanonicalNaN(Res.getSemantics(), APInt(32, 0x7fffffff));
|
|
if (A.isNaN() && B.isNaN())
|
|
return ConstantFP::get(Ty, NVCanonicalNaN);
|
|
else if (IsNaNPropagating && (A.isNaN() || B.isNaN()))
|
|
return ConstantFP::get(Ty, NVCanonicalNaN);
|
|
}
|
|
|
|
if (A.isNaN() && B.isNaN())
|
|
return Operands[1];
|
|
else if (A.isNaN())
|
|
Res = B;
|
|
else if (B.isNaN())
|
|
Res = A;
|
|
|
|
if (IsXorSignAbs && XorSign != Res.isNegative())
|
|
Res.changeSign();
|
|
|
|
return ConstantFP::get(Ty->getContext(), Res);
|
|
}
|
|
}
|
|
|
|
if (!Ty->isHalfTy() && !Ty->isFloatTy() && !Ty->isDoubleTy())
|
|
return nullptr;
|
|
|
|
switch (IntrinsicID) {
|
|
default:
|
|
break;
|
|
case Intrinsic::pow:
|
|
return ConstantFoldBinaryFP(pow, Op1V, Op2V, Ty);
|
|
case Intrinsic::amdgcn_fmul_legacy:
|
|
// The legacy behaviour is that multiplying +/- 0.0 by anything, even
|
|
// NaN or infinity, gives +0.0.
|
|
if (Op1V.isZero() || Op2V.isZero())
|
|
return ConstantFP::getZero(Ty);
|
|
return ConstantFP::get(Ty->getContext(), Op1V * Op2V);
|
|
}
|
|
|
|
} else if (auto *Op2C = dyn_cast<ConstantInt>(Operands[1])) {
|
|
switch (IntrinsicID) {
|
|
case Intrinsic::ldexp: {
|
|
return ConstantFP::get(
|
|
Ty->getContext(),
|
|
scalbn(Op1V, Op2C->getSExtValue(), APFloat::rmNearestTiesToEven));
|
|
}
|
|
case Intrinsic::is_fpclass: {
|
|
FPClassTest Mask = static_cast<FPClassTest>(Op2C->getZExtValue());
|
|
bool Result =
|
|
((Mask & fcSNan) && Op1V.isNaN() && Op1V.isSignaling()) ||
|
|
((Mask & fcQNan) && Op1V.isNaN() && !Op1V.isSignaling()) ||
|
|
((Mask & fcNegInf) && Op1V.isNegInfinity()) ||
|
|
((Mask & fcNegNormal) && Op1V.isNormal() && Op1V.isNegative()) ||
|
|
((Mask & fcNegSubnormal) && Op1V.isDenormal() && Op1V.isNegative()) ||
|
|
((Mask & fcNegZero) && Op1V.isZero() && Op1V.isNegative()) ||
|
|
((Mask & fcPosZero) && Op1V.isZero() && !Op1V.isNegative()) ||
|
|
((Mask & fcPosSubnormal) && Op1V.isDenormal() && !Op1V.isNegative()) ||
|
|
((Mask & fcPosNormal) && Op1V.isNormal() && !Op1V.isNegative()) ||
|
|
((Mask & fcPosInf) && Op1V.isPosInfinity());
|
|
return ConstantInt::get(Ty, Result);
|
|
}
|
|
case Intrinsic::powi: {
|
|
int Exp = static_cast<int>(Op2C->getSExtValue());
|
|
switch (Ty->getTypeID()) {
|
|
case Type::HalfTyID:
|
|
case Type::FloatTyID: {
|
|
APFloat Res(static_cast<float>(std::pow(Op1V.convertToFloat(), Exp)));
|
|
if (Ty->isHalfTy()) {
|
|
bool Unused;
|
|
Res.convert(APFloat::IEEEhalf(), APFloat::rmNearestTiesToEven,
|
|
&Unused);
|
|
}
|
|
return ConstantFP::get(Ty->getContext(), Res);
|
|
}
|
|
case Type::DoubleTyID:
|
|
return ConstantFP::get(Ty, std::pow(Op1V.convertToDouble(), Exp));
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
if (Operands[0]->getType()->isIntegerTy() &&
|
|
Operands[1]->getType()->isIntegerTy()) {
|
|
const APInt *C0, *C1;
|
|
if (!getConstIntOrUndef(Operands[0], C0) ||
|
|
!getConstIntOrUndef(Operands[1], C1))
|
|
return nullptr;
|
|
|
|
switch (IntrinsicID) {
|
|
default: break;
|
|
case Intrinsic::smax:
|
|
case Intrinsic::smin:
|
|
case Intrinsic::umax:
|
|
case Intrinsic::umin:
|
|
if (!C0 && !C1)
|
|
return UndefValue::get(Ty);
|
|
if (!C0 || !C1)
|
|
return MinMaxIntrinsic::getSaturationPoint(IntrinsicID, Ty);
|
|
return ConstantInt::get(
|
|
Ty, ICmpInst::compare(*C0, *C1,
|
|
MinMaxIntrinsic::getPredicate(IntrinsicID))
|
|
? *C0
|
|
: *C1);
|
|
|
|
case Intrinsic::scmp:
|
|
case Intrinsic::ucmp:
|
|
if (!C0 || !C1)
|
|
return ConstantInt::get(Ty, 0);
|
|
|
|
int Res;
|
|
if (IntrinsicID == Intrinsic::scmp)
|
|
Res = C0->sgt(*C1) ? 1 : C0->slt(*C1) ? -1 : 0;
|
|
else
|
|
Res = C0->ugt(*C1) ? 1 : C0->ult(*C1) ? -1 : 0;
|
|
return ConstantInt::get(Ty, Res, /*IsSigned=*/true);
|
|
|
|
case Intrinsic::usub_with_overflow:
|
|
case Intrinsic::ssub_with_overflow:
|
|
// X - undef -> { 0, false }
|
|
// undef - X -> { 0, false }
|
|
if (!C0 || !C1)
|
|
return Constant::getNullValue(Ty);
|
|
[[fallthrough]];
|
|
case Intrinsic::uadd_with_overflow:
|
|
case Intrinsic::sadd_with_overflow:
|
|
// X + undef -> { -1, false }
|
|
// undef + x -> { -1, false }
|
|
if (!C0 || !C1) {
|
|
return ConstantStruct::get(
|
|
cast<StructType>(Ty),
|
|
{Constant::getAllOnesValue(Ty->getStructElementType(0)),
|
|
Constant::getNullValue(Ty->getStructElementType(1))});
|
|
}
|
|
[[fallthrough]];
|
|
case Intrinsic::smul_with_overflow:
|
|
case Intrinsic::umul_with_overflow: {
|
|
// undef * X -> { 0, false }
|
|
// X * undef -> { 0, false }
|
|
if (!C0 || !C1)
|
|
return Constant::getNullValue(Ty);
|
|
|
|
APInt Res;
|
|
bool Overflow;
|
|
switch (IntrinsicID) {
|
|
default: llvm_unreachable("Invalid case");
|
|
case Intrinsic::sadd_with_overflow:
|
|
Res = C0->sadd_ov(*C1, Overflow);
|
|
break;
|
|
case Intrinsic::uadd_with_overflow:
|
|
Res = C0->uadd_ov(*C1, Overflow);
|
|
break;
|
|
case Intrinsic::ssub_with_overflow:
|
|
Res = C0->ssub_ov(*C1, Overflow);
|
|
break;
|
|
case Intrinsic::usub_with_overflow:
|
|
Res = C0->usub_ov(*C1, Overflow);
|
|
break;
|
|
case Intrinsic::smul_with_overflow:
|
|
Res = C0->smul_ov(*C1, Overflow);
|
|
break;
|
|
case Intrinsic::umul_with_overflow:
|
|
Res = C0->umul_ov(*C1, Overflow);
|
|
break;
|
|
}
|
|
Constant *Ops[] = {
|
|
ConstantInt::get(Ty->getContext(), Res),
|
|
ConstantInt::get(Type::getInt1Ty(Ty->getContext()), Overflow)
|
|
};
|
|
return ConstantStruct::get(cast<StructType>(Ty), Ops);
|
|
}
|
|
case Intrinsic::uadd_sat:
|
|
case Intrinsic::sadd_sat:
|
|
if (!C0 && !C1)
|
|
return UndefValue::get(Ty);
|
|
if (!C0 || !C1)
|
|
return Constant::getAllOnesValue(Ty);
|
|
if (IntrinsicID == Intrinsic::uadd_sat)
|
|
return ConstantInt::get(Ty, C0->uadd_sat(*C1));
|
|
else
|
|
return ConstantInt::get(Ty, C0->sadd_sat(*C1));
|
|
case Intrinsic::usub_sat:
|
|
case Intrinsic::ssub_sat:
|
|
if (!C0 && !C1)
|
|
return UndefValue::get(Ty);
|
|
if (!C0 || !C1)
|
|
return Constant::getNullValue(Ty);
|
|
if (IntrinsicID == Intrinsic::usub_sat)
|
|
return ConstantInt::get(Ty, C0->usub_sat(*C1));
|
|
else
|
|
return ConstantInt::get(Ty, C0->ssub_sat(*C1));
|
|
case Intrinsic::cttz:
|
|
case Intrinsic::ctlz:
|
|
assert(C1 && "Must be constant int");
|
|
|
|
// cttz(0, 1) and ctlz(0, 1) are poison.
|
|
if (C1->isOne() && (!C0 || C0->isZero()))
|
|
return PoisonValue::get(Ty);
|
|
if (!C0)
|
|
return Constant::getNullValue(Ty);
|
|
if (IntrinsicID == Intrinsic::cttz)
|
|
return ConstantInt::get(Ty, C0->countr_zero());
|
|
else
|
|
return ConstantInt::get(Ty, C0->countl_zero());
|
|
|
|
case Intrinsic::abs:
|
|
assert(C1 && "Must be constant int");
|
|
assert((C1->isOne() || C1->isZero()) && "Must be 0 or 1");
|
|
|
|
// Undef or minimum val operand with poison min --> poison
|
|
if (C1->isOne() && (!C0 || C0->isMinSignedValue()))
|
|
return PoisonValue::get(Ty);
|
|
|
|
// Undef operand with no poison min --> 0 (sign bit must be clear)
|
|
if (!C0)
|
|
return Constant::getNullValue(Ty);
|
|
|
|
return ConstantInt::get(Ty, C0->abs());
|
|
case Intrinsic::amdgcn_wave_reduce_umin:
|
|
case Intrinsic::amdgcn_wave_reduce_umax:
|
|
return dyn_cast<Constant>(Operands[0]);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// Support ConstantVector in case we have an Undef in the top.
|
|
if ((isa<ConstantVector>(Operands[0]) ||
|
|
isa<ConstantDataVector>(Operands[0])) &&
|
|
// Check for default rounding mode.
|
|
// FIXME: Support other rounding modes?
|
|
isa<ConstantInt>(Operands[1]) &&
|
|
cast<ConstantInt>(Operands[1])->getValue() == 4) {
|
|
auto *Op = cast<Constant>(Operands[0]);
|
|
switch (IntrinsicID) {
|
|
default: break;
|
|
case Intrinsic::x86_avx512_vcvtss2si32:
|
|
case Intrinsic::x86_avx512_vcvtss2si64:
|
|
case Intrinsic::x86_avx512_vcvtsd2si32:
|
|
case Intrinsic::x86_avx512_vcvtsd2si64:
|
|
if (ConstantFP *FPOp =
|
|
dyn_cast_or_null<ConstantFP>(Op->getAggregateElement(0U)))
|
|
return ConstantFoldSSEConvertToInt(FPOp->getValueAPF(),
|
|
/*roundTowardZero=*/false, Ty,
|
|
/*IsSigned*/true);
|
|
break;
|
|
case Intrinsic::x86_avx512_vcvtss2usi32:
|
|
case Intrinsic::x86_avx512_vcvtss2usi64:
|
|
case Intrinsic::x86_avx512_vcvtsd2usi32:
|
|
case Intrinsic::x86_avx512_vcvtsd2usi64:
|
|
if (ConstantFP *FPOp =
|
|
dyn_cast_or_null<ConstantFP>(Op->getAggregateElement(0U)))
|
|
return ConstantFoldSSEConvertToInt(FPOp->getValueAPF(),
|
|
/*roundTowardZero=*/false, Ty,
|
|
/*IsSigned*/false);
|
|
break;
|
|
case Intrinsic::x86_avx512_cvttss2si:
|
|
case Intrinsic::x86_avx512_cvttss2si64:
|
|
case Intrinsic::x86_avx512_cvttsd2si:
|
|
case Intrinsic::x86_avx512_cvttsd2si64:
|
|
if (ConstantFP *FPOp =
|
|
dyn_cast_or_null<ConstantFP>(Op->getAggregateElement(0U)))
|
|
return ConstantFoldSSEConvertToInt(FPOp->getValueAPF(),
|
|
/*roundTowardZero=*/true, Ty,
|
|
/*IsSigned*/true);
|
|
break;
|
|
case Intrinsic::x86_avx512_cvttss2usi:
|
|
case Intrinsic::x86_avx512_cvttss2usi64:
|
|
case Intrinsic::x86_avx512_cvttsd2usi:
|
|
case Intrinsic::x86_avx512_cvttsd2usi64:
|
|
if (ConstantFP *FPOp =
|
|
dyn_cast_or_null<ConstantFP>(Op->getAggregateElement(0U)))
|
|
return ConstantFoldSSEConvertToInt(FPOp->getValueAPF(),
|
|
/*roundTowardZero=*/true, Ty,
|
|
/*IsSigned*/false);
|
|
break;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static APFloat ConstantFoldAMDGCNCubeIntrinsic(Intrinsic::ID IntrinsicID,
|
|
const APFloat &S0,
|
|
const APFloat &S1,
|
|
const APFloat &S2) {
|
|
unsigned ID;
|
|
const fltSemantics &Sem = S0.getSemantics();
|
|
APFloat MA(Sem), SC(Sem), TC(Sem);
|
|
if (abs(S2) >= abs(S0) && abs(S2) >= abs(S1)) {
|
|
if (S2.isNegative() && S2.isNonZero() && !S2.isNaN()) {
|
|
// S2 < 0
|
|
ID = 5;
|
|
SC = -S0;
|
|
} else {
|
|
ID = 4;
|
|
SC = S0;
|
|
}
|
|
MA = S2;
|
|
TC = -S1;
|
|
} else if (abs(S1) >= abs(S0)) {
|
|
if (S1.isNegative() && S1.isNonZero() && !S1.isNaN()) {
|
|
// S1 < 0
|
|
ID = 3;
|
|
TC = -S2;
|
|
} else {
|
|
ID = 2;
|
|
TC = S2;
|
|
}
|
|
MA = S1;
|
|
SC = S0;
|
|
} else {
|
|
if (S0.isNegative() && S0.isNonZero() && !S0.isNaN()) {
|
|
// S0 < 0
|
|
ID = 1;
|
|
SC = S2;
|
|
} else {
|
|
ID = 0;
|
|
SC = -S2;
|
|
}
|
|
MA = S0;
|
|
TC = -S1;
|
|
}
|
|
switch (IntrinsicID) {
|
|
default:
|
|
llvm_unreachable("unhandled amdgcn cube intrinsic");
|
|
case Intrinsic::amdgcn_cubeid:
|
|
return APFloat(Sem, ID);
|
|
case Intrinsic::amdgcn_cubema:
|
|
return MA + MA;
|
|
case Intrinsic::amdgcn_cubesc:
|
|
return SC;
|
|
case Intrinsic::amdgcn_cubetc:
|
|
return TC;
|
|
}
|
|
}
|
|
|
|
static Constant *ConstantFoldAMDGCNPermIntrinsic(ArrayRef<Constant *> Operands,
|
|
Type *Ty) {
|
|
const APInt *C0, *C1, *C2;
|
|
if (!getConstIntOrUndef(Operands[0], C0) ||
|
|
!getConstIntOrUndef(Operands[1], C1) ||
|
|
!getConstIntOrUndef(Operands[2], C2))
|
|
return nullptr;
|
|
|
|
if (!C2)
|
|
return UndefValue::get(Ty);
|
|
|
|
APInt Val(32, 0);
|
|
unsigned NumUndefBytes = 0;
|
|
for (unsigned I = 0; I < 32; I += 8) {
|
|
unsigned Sel = C2->extractBitsAsZExtValue(8, I);
|
|
unsigned B = 0;
|
|
|
|
if (Sel >= 13)
|
|
B = 0xff;
|
|
else if (Sel == 12)
|
|
B = 0x00;
|
|
else {
|
|
const APInt *Src = ((Sel & 10) == 10 || (Sel & 12) == 4) ? C0 : C1;
|
|
if (!Src)
|
|
++NumUndefBytes;
|
|
else if (Sel < 8)
|
|
B = Src->extractBitsAsZExtValue(8, (Sel & 3) * 8);
|
|
else
|
|
B = Src->extractBitsAsZExtValue(1, (Sel & 1) ? 31 : 15) * 0xff;
|
|
}
|
|
|
|
Val.insertBits(B, I, 8);
|
|
}
|
|
|
|
if (NumUndefBytes == 4)
|
|
return UndefValue::get(Ty);
|
|
|
|
return ConstantInt::get(Ty, Val);
|
|
}
|
|
|
|
static Constant *ConstantFoldScalarCall3(StringRef Name,
|
|
Intrinsic::ID IntrinsicID,
|
|
Type *Ty,
|
|
ArrayRef<Constant *> Operands,
|
|
const TargetLibraryInfo *TLI,
|
|
const CallBase *Call) {
|
|
assert(Operands.size() == 3 && "Wrong number of operands.");
|
|
|
|
if (const auto *Op1 = dyn_cast<ConstantFP>(Operands[0])) {
|
|
if (const auto *Op2 = dyn_cast<ConstantFP>(Operands[1])) {
|
|
if (const auto *Op3 = dyn_cast<ConstantFP>(Operands[2])) {
|
|
const APFloat &C1 = Op1->getValueAPF();
|
|
const APFloat &C2 = Op2->getValueAPF();
|
|
const APFloat &C3 = Op3->getValueAPF();
|
|
|
|
if (const auto *ConstrIntr = dyn_cast<ConstrainedFPIntrinsic>(Call)) {
|
|
RoundingMode RM = getEvaluationRoundingMode(ConstrIntr);
|
|
APFloat Res = C1;
|
|
APFloat::opStatus St;
|
|
switch (IntrinsicID) {
|
|
default:
|
|
return nullptr;
|
|
case Intrinsic::experimental_constrained_fma:
|
|
case Intrinsic::experimental_constrained_fmuladd:
|
|
St = Res.fusedMultiplyAdd(C2, C3, RM);
|
|
break;
|
|
}
|
|
if (mayFoldConstrained(
|
|
const_cast<ConstrainedFPIntrinsic *>(ConstrIntr), St))
|
|
return ConstantFP::get(Ty->getContext(), Res);
|
|
return nullptr;
|
|
}
|
|
|
|
switch (IntrinsicID) {
|
|
default: break;
|
|
case Intrinsic::amdgcn_fma_legacy: {
|
|
// The legacy behaviour is that multiplying +/- 0.0 by anything, even
|
|
// NaN or infinity, gives +0.0.
|
|
if (C1.isZero() || C2.isZero()) {
|
|
// It's tempting to just return C3 here, but that would give the
|
|
// wrong result if C3 was -0.0.
|
|
return ConstantFP::get(Ty->getContext(), APFloat(0.0f) + C3);
|
|
}
|
|
[[fallthrough]];
|
|
}
|
|
case Intrinsic::fma:
|
|
case Intrinsic::fmuladd: {
|
|
APFloat V = C1;
|
|
V.fusedMultiplyAdd(C2, C3, APFloat::rmNearestTiesToEven);
|
|
return ConstantFP::get(Ty->getContext(), V);
|
|
}
|
|
case Intrinsic::amdgcn_cubeid:
|
|
case Intrinsic::amdgcn_cubema:
|
|
case Intrinsic::amdgcn_cubesc:
|
|
case Intrinsic::amdgcn_cubetc: {
|
|
APFloat V = ConstantFoldAMDGCNCubeIntrinsic(IntrinsicID, C1, C2, C3);
|
|
return ConstantFP::get(Ty->getContext(), V);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IntrinsicID == Intrinsic::smul_fix ||
|
|
IntrinsicID == Intrinsic::smul_fix_sat) {
|
|
const APInt *C0, *C1;
|
|
if (!getConstIntOrUndef(Operands[0], C0) ||
|
|
!getConstIntOrUndef(Operands[1], C1))
|
|
return nullptr;
|
|
|
|
// undef * C -> 0
|
|
// C * undef -> 0
|
|
if (!C0 || !C1)
|
|
return Constant::getNullValue(Ty);
|
|
|
|
// This code performs rounding towards negative infinity in case the result
|
|
// cannot be represented exactly for the given scale. Targets that do care
|
|
// about rounding should use a target hook for specifying how rounding
|
|
// should be done, and provide their own folding to be consistent with
|
|
// rounding. This is the same approach as used by
|
|
// DAGTypeLegalizer::ExpandIntRes_MULFIX.
|
|
unsigned Scale = cast<ConstantInt>(Operands[2])->getZExtValue();
|
|
unsigned Width = C0->getBitWidth();
|
|
assert(Scale < Width && "Illegal scale.");
|
|
unsigned ExtendedWidth = Width * 2;
|
|
APInt Product =
|
|
(C0->sext(ExtendedWidth) * C1->sext(ExtendedWidth)).ashr(Scale);
|
|
if (IntrinsicID == Intrinsic::smul_fix_sat) {
|
|
APInt Max = APInt::getSignedMaxValue(Width).sext(ExtendedWidth);
|
|
APInt Min = APInt::getSignedMinValue(Width).sext(ExtendedWidth);
|
|
Product = APIntOps::smin(Product, Max);
|
|
Product = APIntOps::smax(Product, Min);
|
|
}
|
|
return ConstantInt::get(Ty->getContext(), Product.sextOrTrunc(Width));
|
|
}
|
|
|
|
if (IntrinsicID == Intrinsic::fshl || IntrinsicID == Intrinsic::fshr) {
|
|
const APInt *C0, *C1, *C2;
|
|
if (!getConstIntOrUndef(Operands[0], C0) ||
|
|
!getConstIntOrUndef(Operands[1], C1) ||
|
|
!getConstIntOrUndef(Operands[2], C2))
|
|
return nullptr;
|
|
|
|
bool IsRight = IntrinsicID == Intrinsic::fshr;
|
|
if (!C2)
|
|
return Operands[IsRight ? 1 : 0];
|
|
if (!C0 && !C1)
|
|
return UndefValue::get(Ty);
|
|
|
|
// The shift amount is interpreted as modulo the bitwidth. If the shift
|
|
// amount is effectively 0, avoid UB due to oversized inverse shift below.
|
|
unsigned BitWidth = C2->getBitWidth();
|
|
unsigned ShAmt = C2->urem(BitWidth);
|
|
if (!ShAmt)
|
|
return Operands[IsRight ? 1 : 0];
|
|
|
|
// (C0 << ShlAmt) | (C1 >> LshrAmt)
|
|
unsigned LshrAmt = IsRight ? ShAmt : BitWidth - ShAmt;
|
|
unsigned ShlAmt = !IsRight ? ShAmt : BitWidth - ShAmt;
|
|
if (!C0)
|
|
return ConstantInt::get(Ty, C1->lshr(LshrAmt));
|
|
if (!C1)
|
|
return ConstantInt::get(Ty, C0->shl(ShlAmt));
|
|
return ConstantInt::get(Ty, C0->shl(ShlAmt) | C1->lshr(LshrAmt));
|
|
}
|
|
|
|
if (IntrinsicID == Intrinsic::amdgcn_perm)
|
|
return ConstantFoldAMDGCNPermIntrinsic(Operands, Ty);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static Constant *ConstantFoldScalarCall(StringRef Name,
|
|
Intrinsic::ID IntrinsicID,
|
|
Type *Ty,
|
|
ArrayRef<Constant *> Operands,
|
|
const TargetLibraryInfo *TLI,
|
|
const CallBase *Call) {
|
|
if (IntrinsicID != Intrinsic::not_intrinsic &&
|
|
any_of(Operands, IsaPred<PoisonValue>) &&
|
|
intrinsicPropagatesPoison(IntrinsicID))
|
|
return PoisonValue::get(Ty);
|
|
|
|
if (Operands.size() == 1)
|
|
return ConstantFoldScalarCall1(Name, IntrinsicID, Ty, Operands, TLI, Call);
|
|
|
|
if (Operands.size() == 2) {
|
|
if (Constant *FoldedLibCall =
|
|
ConstantFoldLibCall2(Name, Ty, Operands, TLI)) {
|
|
return FoldedLibCall;
|
|
}
|
|
return ConstantFoldIntrinsicCall2(IntrinsicID, Ty, Operands, Call);
|
|
}
|
|
|
|
if (Operands.size() == 3)
|
|
return ConstantFoldScalarCall3(Name, IntrinsicID, Ty, Operands, TLI, Call);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static Constant *ConstantFoldFixedVectorCall(
|
|
StringRef Name, Intrinsic::ID IntrinsicID, FixedVectorType *FVTy,
|
|
ArrayRef<Constant *> Operands, const DataLayout &DL,
|
|
const TargetLibraryInfo *TLI, const CallBase *Call) {
|
|
SmallVector<Constant *, 4> Result(FVTy->getNumElements());
|
|
SmallVector<Constant *, 4> Lane(Operands.size());
|
|
Type *Ty = FVTy->getElementType();
|
|
|
|
switch (IntrinsicID) {
|
|
case Intrinsic::masked_load: {
|
|
auto *SrcPtr = Operands[0];
|
|
auto *Mask = Operands[2];
|
|
auto *Passthru = Operands[3];
|
|
|
|
Constant *VecData = ConstantFoldLoadFromConstPtr(SrcPtr, FVTy, DL);
|
|
|
|
SmallVector<Constant *, 32> NewElements;
|
|
for (unsigned I = 0, E = FVTy->getNumElements(); I != E; ++I) {
|
|
auto *MaskElt = Mask->getAggregateElement(I);
|
|
if (!MaskElt)
|
|
break;
|
|
auto *PassthruElt = Passthru->getAggregateElement(I);
|
|
auto *VecElt = VecData ? VecData->getAggregateElement(I) : nullptr;
|
|
if (isa<UndefValue>(MaskElt)) {
|
|
if (PassthruElt)
|
|
NewElements.push_back(PassthruElt);
|
|
else if (VecElt)
|
|
NewElements.push_back(VecElt);
|
|
else
|
|
return nullptr;
|
|
}
|
|
if (MaskElt->isNullValue()) {
|
|
if (!PassthruElt)
|
|
return nullptr;
|
|
NewElements.push_back(PassthruElt);
|
|
} else if (MaskElt->isOneValue()) {
|
|
if (!VecElt)
|
|
return nullptr;
|
|
NewElements.push_back(VecElt);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
if (NewElements.size() != FVTy->getNumElements())
|
|
return nullptr;
|
|
return ConstantVector::get(NewElements);
|
|
}
|
|
case Intrinsic::arm_mve_vctp8:
|
|
case Intrinsic::arm_mve_vctp16:
|
|
case Intrinsic::arm_mve_vctp32:
|
|
case Intrinsic::arm_mve_vctp64: {
|
|
if (auto *Op = dyn_cast<ConstantInt>(Operands[0])) {
|
|
unsigned Lanes = FVTy->getNumElements();
|
|
uint64_t Limit = Op->getZExtValue();
|
|
|
|
SmallVector<Constant *, 16> NCs;
|
|
for (unsigned i = 0; i < Lanes; i++) {
|
|
if (i < Limit)
|
|
NCs.push_back(ConstantInt::getTrue(Ty));
|
|
else
|
|
NCs.push_back(ConstantInt::getFalse(Ty));
|
|
}
|
|
return ConstantVector::get(NCs);
|
|
}
|
|
return nullptr;
|
|
}
|
|
case Intrinsic::get_active_lane_mask: {
|
|
auto *Op0 = dyn_cast<ConstantInt>(Operands[0]);
|
|
auto *Op1 = dyn_cast<ConstantInt>(Operands[1]);
|
|
if (Op0 && Op1) {
|
|
unsigned Lanes = FVTy->getNumElements();
|
|
uint64_t Base = Op0->getZExtValue();
|
|
uint64_t Limit = Op1->getZExtValue();
|
|
|
|
SmallVector<Constant *, 16> NCs;
|
|
for (unsigned i = 0; i < Lanes; i++) {
|
|
if (Base + i < Limit)
|
|
NCs.push_back(ConstantInt::getTrue(Ty));
|
|
else
|
|
NCs.push_back(ConstantInt::getFalse(Ty));
|
|
}
|
|
return ConstantVector::get(NCs);
|
|
}
|
|
return nullptr;
|
|
}
|
|
case Intrinsic::vector_extract: {
|
|
auto *Idx = dyn_cast<ConstantInt>(Operands[1]);
|
|
Constant *Vec = Operands[0];
|
|
if (!Idx || !isa<FixedVectorType>(Vec->getType()))
|
|
return nullptr;
|
|
|
|
unsigned NumElements = FVTy->getNumElements();
|
|
unsigned VecNumElements =
|
|
cast<FixedVectorType>(Vec->getType())->getNumElements();
|
|
unsigned StartingIndex = Idx->getZExtValue();
|
|
|
|
// Extracting entire vector is nop
|
|
if (NumElements == VecNumElements && StartingIndex == 0)
|
|
return Vec;
|
|
|
|
for (unsigned I = StartingIndex, E = StartingIndex + NumElements; I < E;
|
|
++I) {
|
|
Constant *Elt = Vec->getAggregateElement(I);
|
|
if (!Elt)
|
|
return nullptr;
|
|
Result[I - StartingIndex] = Elt;
|
|
}
|
|
|
|
return ConstantVector::get(Result);
|
|
}
|
|
case Intrinsic::vector_insert: {
|
|
Constant *Vec = Operands[0];
|
|
Constant *SubVec = Operands[1];
|
|
auto *Idx = dyn_cast<ConstantInt>(Operands[2]);
|
|
if (!Idx || !isa<FixedVectorType>(Vec->getType()))
|
|
return nullptr;
|
|
|
|
unsigned SubVecNumElements =
|
|
cast<FixedVectorType>(SubVec->getType())->getNumElements();
|
|
unsigned VecNumElements =
|
|
cast<FixedVectorType>(Vec->getType())->getNumElements();
|
|
unsigned IdxN = Idx->getZExtValue();
|
|
// Replacing entire vector with a subvec is nop
|
|
if (SubVecNumElements == VecNumElements && IdxN == 0)
|
|
return SubVec;
|
|
|
|
for (unsigned I = 0; I < VecNumElements; ++I) {
|
|
Constant *Elt;
|
|
if (I < IdxN + SubVecNumElements)
|
|
Elt = SubVec->getAggregateElement(I - IdxN);
|
|
else
|
|
Elt = Vec->getAggregateElement(I);
|
|
if (!Elt)
|
|
return nullptr;
|
|
Result[I] = Elt;
|
|
}
|
|
return ConstantVector::get(Result);
|
|
}
|
|
case Intrinsic::vector_interleave2: {
|
|
unsigned NumElements =
|
|
cast<FixedVectorType>(Operands[0]->getType())->getNumElements();
|
|
for (unsigned I = 0; I < NumElements; ++I) {
|
|
Constant *Elt0 = Operands[0]->getAggregateElement(I);
|
|
Constant *Elt1 = Operands[1]->getAggregateElement(I);
|
|
if (!Elt0 || !Elt1)
|
|
return nullptr;
|
|
Result[2 * I] = Elt0;
|
|
Result[2 * I + 1] = Elt1;
|
|
}
|
|
return ConstantVector::get(Result);
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
for (unsigned I = 0, E = FVTy->getNumElements(); I != E; ++I) {
|
|
// Gather a column of constants.
|
|
for (unsigned J = 0, JE = Operands.size(); J != JE; ++J) {
|
|
// Some intrinsics use a scalar type for certain arguments.
|
|
if (isVectorIntrinsicWithScalarOpAtArg(IntrinsicID, J, /*TTI=*/nullptr)) {
|
|
Lane[J] = Operands[J];
|
|
continue;
|
|
}
|
|
|
|
Constant *Agg = Operands[J]->getAggregateElement(I);
|
|
if (!Agg)
|
|
return nullptr;
|
|
|
|
Lane[J] = Agg;
|
|
}
|
|
|
|
// Use the regular scalar folding to simplify this column.
|
|
Constant *Folded =
|
|
ConstantFoldScalarCall(Name, IntrinsicID, Ty, Lane, TLI, Call);
|
|
if (!Folded)
|
|
return nullptr;
|
|
Result[I] = Folded;
|
|
}
|
|
|
|
return ConstantVector::get(Result);
|
|
}
|
|
|
|
static Constant *ConstantFoldScalableVectorCall(
|
|
StringRef Name, Intrinsic::ID IntrinsicID, ScalableVectorType *SVTy,
|
|
ArrayRef<Constant *> Operands, const DataLayout &DL,
|
|
const TargetLibraryInfo *TLI, const CallBase *Call) {
|
|
switch (IntrinsicID) {
|
|
case Intrinsic::aarch64_sve_convert_from_svbool: {
|
|
auto *Src = dyn_cast<Constant>(Operands[0]);
|
|
if (!Src || !Src->isNullValue())
|
|
break;
|
|
|
|
return ConstantInt::getFalse(SVTy);
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// If trivially vectorizable, try folding it via the scalar call if all
|
|
// operands are splats.
|
|
|
|
// TODO: ConstantFoldFixedVectorCall should probably check this too?
|
|
if (!isTriviallyVectorizable(IntrinsicID))
|
|
return nullptr;
|
|
|
|
SmallVector<Constant *, 4> SplatOps;
|
|
for (auto [I, Op] : enumerate(Operands)) {
|
|
if (isVectorIntrinsicWithScalarOpAtArg(IntrinsicID, I, /*TTI=*/nullptr)) {
|
|
SplatOps.push_back(Op);
|
|
continue;
|
|
}
|
|
Constant *Splat = Op->getSplatValue();
|
|
if (!Splat)
|
|
return nullptr;
|
|
SplatOps.push_back(Splat);
|
|
}
|
|
Constant *Folded = ConstantFoldScalarCall(
|
|
Name, IntrinsicID, SVTy->getElementType(), SplatOps, TLI, Call);
|
|
if (!Folded)
|
|
return nullptr;
|
|
return ConstantVector::getSplat(SVTy->getElementCount(), Folded);
|
|
}
|
|
|
|
static std::pair<Constant *, Constant *>
|
|
ConstantFoldScalarFrexpCall(Constant *Op, Type *IntTy) {
|
|
if (isa<PoisonValue>(Op))
|
|
return {Op, PoisonValue::get(IntTy)};
|
|
|
|
auto *ConstFP = dyn_cast<ConstantFP>(Op);
|
|
if (!ConstFP)
|
|
return {};
|
|
|
|
const APFloat &U = ConstFP->getValueAPF();
|
|
int FrexpExp;
|
|
APFloat FrexpMant = frexp(U, FrexpExp, APFloat::rmNearestTiesToEven);
|
|
Constant *Result0 = ConstantFP::get(ConstFP->getType(), FrexpMant);
|
|
|
|
// The exponent is an "unspecified value" for inf/nan. We use zero to avoid
|
|
// using undef.
|
|
Constant *Result1 = FrexpMant.isFinite()
|
|
? ConstantInt::getSigned(IntTy, FrexpExp)
|
|
: ConstantInt::getNullValue(IntTy);
|
|
return {Result0, Result1};
|
|
}
|
|
|
|
/// Handle intrinsics that return tuples, which may be tuples of vectors.
|
|
static Constant *
|
|
ConstantFoldStructCall(StringRef Name, Intrinsic::ID IntrinsicID,
|
|
StructType *StTy, ArrayRef<Constant *> Operands,
|
|
const DataLayout &DL, const TargetLibraryInfo *TLI,
|
|
const CallBase *Call) {
|
|
|
|
switch (IntrinsicID) {
|
|
case Intrinsic::frexp: {
|
|
Type *Ty0 = StTy->getContainedType(0);
|
|
Type *Ty1 = StTy->getContainedType(1)->getScalarType();
|
|
|
|
if (auto *FVTy0 = dyn_cast<FixedVectorType>(Ty0)) {
|
|
SmallVector<Constant *, 4> Results0(FVTy0->getNumElements());
|
|
SmallVector<Constant *, 4> Results1(FVTy0->getNumElements());
|
|
|
|
for (unsigned I = 0, E = FVTy0->getNumElements(); I != E; ++I) {
|
|
Constant *Lane = Operands[0]->getAggregateElement(I);
|
|
std::tie(Results0[I], Results1[I]) =
|
|
ConstantFoldScalarFrexpCall(Lane, Ty1);
|
|
if (!Results0[I])
|
|
return nullptr;
|
|
}
|
|
|
|
return ConstantStruct::get(StTy, ConstantVector::get(Results0),
|
|
ConstantVector::get(Results1));
|
|
}
|
|
|
|
auto [Result0, Result1] = ConstantFoldScalarFrexpCall(Operands[0], Ty1);
|
|
if (!Result0)
|
|
return nullptr;
|
|
return ConstantStruct::get(StTy, Result0, Result1);
|
|
}
|
|
case Intrinsic::sincos: {
|
|
Type *Ty = StTy->getContainedType(0);
|
|
Type *TyScalar = Ty->getScalarType();
|
|
|
|
auto ConstantFoldScalarSincosCall =
|
|
[&](Constant *Op) -> std::pair<Constant *, Constant *> {
|
|
Constant *SinResult =
|
|
ConstantFoldScalarCall(Name, Intrinsic::sin, TyScalar, Op, TLI, Call);
|
|
Constant *CosResult =
|
|
ConstantFoldScalarCall(Name, Intrinsic::cos, TyScalar, Op, TLI, Call);
|
|
return std::make_pair(SinResult, CosResult);
|
|
};
|
|
|
|
if (auto *FVTy = dyn_cast<FixedVectorType>(Ty)) {
|
|
SmallVector<Constant *> SinResults(FVTy->getNumElements());
|
|
SmallVector<Constant *> CosResults(FVTy->getNumElements());
|
|
|
|
for (unsigned I = 0, E = FVTy->getNumElements(); I != E; ++I) {
|
|
Constant *Lane = Operands[0]->getAggregateElement(I);
|
|
std::tie(SinResults[I], CosResults[I]) =
|
|
ConstantFoldScalarSincosCall(Lane);
|
|
if (!SinResults[I] || !CosResults[I])
|
|
return nullptr;
|
|
}
|
|
|
|
return ConstantStruct::get(StTy, ConstantVector::get(SinResults),
|
|
ConstantVector::get(CosResults));
|
|
}
|
|
|
|
auto [SinResult, CosResult] = ConstantFoldScalarSincosCall(Operands[0]);
|
|
if (!SinResult || !CosResult)
|
|
return nullptr;
|
|
return ConstantStruct::get(StTy, SinResult, CosResult);
|
|
}
|
|
case Intrinsic::vector_deinterleave2: {
|
|
auto *Vec = Operands[0];
|
|
auto *VecTy = cast<VectorType>(Vec->getType());
|
|
|
|
if (auto *EltC = Vec->getSplatValue()) {
|
|
ElementCount HalfEC = VecTy->getElementCount().divideCoefficientBy(2);
|
|
auto *HalfVec = ConstantVector::getSplat(HalfEC, EltC);
|
|
return ConstantStruct::get(StTy, HalfVec, HalfVec);
|
|
}
|
|
|
|
if (!isa<FixedVectorType>(Vec->getType()))
|
|
return nullptr;
|
|
|
|
unsigned NumElements = VecTy->getElementCount().getFixedValue() / 2;
|
|
SmallVector<Constant *, 4> Res0(NumElements), Res1(NumElements);
|
|
for (unsigned I = 0; I < NumElements; ++I) {
|
|
Constant *Elt0 = Vec->getAggregateElement(2 * I);
|
|
Constant *Elt1 = Vec->getAggregateElement(2 * I + 1);
|
|
if (!Elt0 || !Elt1)
|
|
return nullptr;
|
|
Res0[I] = Elt0;
|
|
Res1[I] = Elt1;
|
|
}
|
|
return ConstantStruct::get(StTy, ConstantVector::get(Res0),
|
|
ConstantVector::get(Res1));
|
|
}
|
|
default:
|
|
// TODO: Constant folding of vector intrinsics that fall through here does
|
|
// not work (e.g. overflow intrinsics)
|
|
return ConstantFoldScalarCall(Name, IntrinsicID, StTy, Operands, TLI, Call);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
} // end anonymous namespace
|
|
|
|
Constant *llvm::ConstantFoldBinaryIntrinsic(Intrinsic::ID ID, Constant *LHS,
|
|
Constant *RHS, Type *Ty,
|
|
Instruction *FMFSource) {
|
|
auto *Call = dyn_cast_if_present<CallBase>(FMFSource);
|
|
// Ensure we check flags like StrictFP that might prevent this from getting
|
|
// folded before generating a result.
|
|
if (Call && !canConstantFoldCallTo(Call, Call->getCalledFunction()))
|
|
return nullptr;
|
|
return ConstantFoldIntrinsicCall2(ID, Ty, {LHS, RHS}, Call);
|
|
}
|
|
|
|
Constant *llvm::ConstantFoldCall(const CallBase *Call, Function *F,
|
|
ArrayRef<Constant *> Operands,
|
|
const TargetLibraryInfo *TLI,
|
|
bool AllowNonDeterministic) {
|
|
if (Call->isNoBuiltin())
|
|
return nullptr;
|
|
if (!F->hasName())
|
|
return nullptr;
|
|
|
|
// If this is not an intrinsic and not recognized as a library call, bail out.
|
|
Intrinsic::ID IID = F->getIntrinsicID();
|
|
if (IID == Intrinsic::not_intrinsic) {
|
|
if (!TLI)
|
|
return nullptr;
|
|
LibFunc LibF;
|
|
if (!TLI->getLibFunc(*F, LibF))
|
|
return nullptr;
|
|
}
|
|
|
|
// Conservatively assume that floating-point libcalls may be
|
|
// non-deterministic.
|
|
Type *Ty = F->getReturnType();
|
|
if (!AllowNonDeterministic && Ty->isFPOrFPVectorTy())
|
|
return nullptr;
|
|
|
|
StringRef Name = F->getName();
|
|
if (auto *FVTy = dyn_cast<FixedVectorType>(Ty))
|
|
return ConstantFoldFixedVectorCall(
|
|
Name, IID, FVTy, Operands, F->getDataLayout(), TLI, Call);
|
|
|
|
if (auto *SVTy = dyn_cast<ScalableVectorType>(Ty))
|
|
return ConstantFoldScalableVectorCall(
|
|
Name, IID, SVTy, Operands, F->getDataLayout(), TLI, Call);
|
|
|
|
if (auto *StTy = dyn_cast<StructType>(Ty))
|
|
return ConstantFoldStructCall(Name, IID, StTy, Operands,
|
|
F->getDataLayout(), TLI, Call);
|
|
|
|
// TODO: If this is a library function, we already discovered that above,
|
|
// so we should pass the LibFunc, not the name (and it might be better
|
|
// still to separate intrinsic handling from libcalls).
|
|
return ConstantFoldScalarCall(Name, IID, Ty, Operands, TLI, Call);
|
|
}
|
|
|
|
bool llvm::isMathLibCallNoop(const CallBase *Call,
|
|
const TargetLibraryInfo *TLI) {
|
|
// FIXME: Refactor this code; this duplicates logic in LibCallsShrinkWrap
|
|
// (and to some extent ConstantFoldScalarCall).
|
|
if (Call->isNoBuiltin() || Call->isStrictFP())
|
|
return false;
|
|
Function *F = Call->getCalledFunction();
|
|
if (!F)
|
|
return false;
|
|
|
|
LibFunc Func;
|
|
if (!TLI || !TLI->getLibFunc(*F, Func))
|
|
return false;
|
|
|
|
if (Call->arg_size() == 1) {
|
|
if (ConstantFP *OpC = dyn_cast<ConstantFP>(Call->getArgOperand(0))) {
|
|
const APFloat &Op = OpC->getValueAPF();
|
|
switch (Func) {
|
|
case LibFunc_logl:
|
|
case LibFunc_log:
|
|
case LibFunc_logf:
|
|
case LibFunc_log2l:
|
|
case LibFunc_log2:
|
|
case LibFunc_log2f:
|
|
case LibFunc_log10l:
|
|
case LibFunc_log10:
|
|
case LibFunc_log10f:
|
|
return Op.isNaN() || (!Op.isZero() && !Op.isNegative());
|
|
|
|
case LibFunc_ilogb:
|
|
return !Op.isNaN() && !Op.isZero() && !Op.isInfinity();
|
|
|
|
case LibFunc_expl:
|
|
case LibFunc_exp:
|
|
case LibFunc_expf:
|
|
// FIXME: These boundaries are slightly conservative.
|
|
if (OpC->getType()->isDoubleTy())
|
|
return !(Op < APFloat(-745.0) || Op > APFloat(709.0));
|
|
if (OpC->getType()->isFloatTy())
|
|
return !(Op < APFloat(-103.0f) || Op > APFloat(88.0f));
|
|
break;
|
|
|
|
case LibFunc_exp2l:
|
|
case LibFunc_exp2:
|
|
case LibFunc_exp2f:
|
|
// FIXME: These boundaries are slightly conservative.
|
|
if (OpC->getType()->isDoubleTy())
|
|
return !(Op < APFloat(-1074.0) || Op > APFloat(1023.0));
|
|
if (OpC->getType()->isFloatTy())
|
|
return !(Op < APFloat(-149.0f) || Op > APFloat(127.0f));
|
|
break;
|
|
|
|
case LibFunc_sinl:
|
|
case LibFunc_sin:
|
|
case LibFunc_sinf:
|
|
case LibFunc_cosl:
|
|
case LibFunc_cos:
|
|
case LibFunc_cosf:
|
|
return !Op.isInfinity();
|
|
|
|
case LibFunc_tanl:
|
|
case LibFunc_tan:
|
|
case LibFunc_tanf: {
|
|
// FIXME: Stop using the host math library.
|
|
// FIXME: The computation isn't done in the right precision.
|
|
Type *Ty = OpC->getType();
|
|
if (Ty->isDoubleTy() || Ty->isFloatTy() || Ty->isHalfTy())
|
|
return ConstantFoldFP(tan, OpC->getValueAPF(), Ty) != nullptr;
|
|
break;
|
|
}
|
|
|
|
case LibFunc_atan:
|
|
case LibFunc_atanf:
|
|
case LibFunc_atanl:
|
|
// Per POSIX, this MAY fail if Op is denormal. We choose not failing.
|
|
return true;
|
|
|
|
case LibFunc_asinl:
|
|
case LibFunc_asin:
|
|
case LibFunc_asinf:
|
|
case LibFunc_acosl:
|
|
case LibFunc_acos:
|
|
case LibFunc_acosf:
|
|
return !(Op < APFloat::getOne(Op.getSemantics(), true) ||
|
|
Op > APFloat::getOne(Op.getSemantics()));
|
|
|
|
case LibFunc_sinh:
|
|
case LibFunc_cosh:
|
|
case LibFunc_sinhf:
|
|
case LibFunc_coshf:
|
|
case LibFunc_sinhl:
|
|
case LibFunc_coshl:
|
|
// FIXME: These boundaries are slightly conservative.
|
|
if (OpC->getType()->isDoubleTy())
|
|
return !(Op < APFloat(-710.0) || Op > APFloat(710.0));
|
|
if (OpC->getType()->isFloatTy())
|
|
return !(Op < APFloat(-89.0f) || Op > APFloat(89.0f));
|
|
break;
|
|
|
|
case LibFunc_sqrtl:
|
|
case LibFunc_sqrt:
|
|
case LibFunc_sqrtf:
|
|
return Op.isNaN() || Op.isZero() || !Op.isNegative();
|
|
|
|
// FIXME: Add more functions: sqrt_finite, atanh, expm1, log1p,
|
|
// maybe others?
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Call->arg_size() == 2) {
|
|
ConstantFP *Op0C = dyn_cast<ConstantFP>(Call->getArgOperand(0));
|
|
ConstantFP *Op1C = dyn_cast<ConstantFP>(Call->getArgOperand(1));
|
|
if (Op0C && Op1C) {
|
|
const APFloat &Op0 = Op0C->getValueAPF();
|
|
const APFloat &Op1 = Op1C->getValueAPF();
|
|
|
|
switch (Func) {
|
|
case LibFunc_powl:
|
|
case LibFunc_pow:
|
|
case LibFunc_powf: {
|
|
// FIXME: Stop using the host math library.
|
|
// FIXME: The computation isn't done in the right precision.
|
|
Type *Ty = Op0C->getType();
|
|
if (Ty->isDoubleTy() || Ty->isFloatTy() || Ty->isHalfTy()) {
|
|
if (Ty == Op1C->getType())
|
|
return ConstantFoldBinaryFP(pow, Op0, Op1, Ty) != nullptr;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case LibFunc_fmodl:
|
|
case LibFunc_fmod:
|
|
case LibFunc_fmodf:
|
|
case LibFunc_remainderl:
|
|
case LibFunc_remainder:
|
|
case LibFunc_remainderf:
|
|
return Op0.isNaN() || Op1.isNaN() ||
|
|
(!Op0.isInfinity() && !Op1.isZero());
|
|
|
|
case LibFunc_atan2:
|
|
case LibFunc_atan2f:
|
|
case LibFunc_atan2l:
|
|
// Although IEEE-754 says atan2(+/-0.0, +/-0.0) are well-defined, and
|
|
// GLIBC and MSVC do not appear to raise an error on those, we
|
|
// cannot rely on that behavior. POSIX and C11 say that a domain error
|
|
// may occur, so allow for that possibility.
|
|
return !Op0.isZero() || !Op1.isZero();
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void TargetFolder::anchor() {}
|