[LV] Simplify creation of vp.load/vp.store/vp.reduce intrinsics (#143804)
The use of VectorBuilder here was simply obscuring what was actually going on. For vp.load and vp.store, the resulting code is significantly more idiomatic. For the vp.reduce cases, we remove several layers of indirection, including passing parameters via implicit state on the builder. In both cases, the code is significantly easier to follow.
This commit is contained in:
@@ -1,120 +0,0 @@
|
||||
//===- llvm/VectorBuilder.h - Builder for VP Intrinsics ---------*- C++ -*-===//
|
||||
//
|
||||
// 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 the VectorBuilder class, which is used as a convenient way
|
||||
// to create VP intrinsics as if they were LLVM instructions with a consistent
|
||||
// and simplified interface.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_IR_VECTORBUILDER_H
|
||||
#define LLVM_IR_VECTORBUILDER_H
|
||||
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
#include <llvm/IR/InstrTypes.h>
|
||||
#include <llvm/IR/Instruction.h>
|
||||
#include <llvm/IR/Value.h>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class VectorBuilder {
|
||||
public:
|
||||
enum class Behavior {
|
||||
// Abort if the requested VP intrinsic could not be created.
|
||||
// This is useful for strict consistency.
|
||||
ReportAndAbort = 0,
|
||||
|
||||
// Return a default-initialized value if the requested VP intrinsic could
|
||||
// not be created.
|
||||
// This is useful for a defensive fallback to non-VP code.
|
||||
SilentlyReturnNone = 1,
|
||||
};
|
||||
|
||||
private:
|
||||
IRBuilderBase &Builder;
|
||||
Behavior ErrorHandling;
|
||||
|
||||
// Explicit mask parameter.
|
||||
Value *Mask;
|
||||
// Explicit vector length parameter.
|
||||
Value *ExplicitVectorLength;
|
||||
// Compile-time vector length.
|
||||
ElementCount StaticVectorLength;
|
||||
|
||||
// Get mask/evl value handles for the current configuration.
|
||||
Value &requestMask();
|
||||
Value &requestEVL();
|
||||
|
||||
LLVM_ABI void handleError(const char *ErrorMsg) const;
|
||||
template <typename RetType>
|
||||
RetType returnWithError(const char *ErrorMsg) const {
|
||||
handleError(ErrorMsg);
|
||||
return RetType();
|
||||
}
|
||||
|
||||
/// Helper function for creating VP intrinsic call.
|
||||
Value *createVectorInstructionImpl(Intrinsic::ID VPID, Type *ReturnTy,
|
||||
ArrayRef<Value *> VecOpArray,
|
||||
const Twine &Name = Twine());
|
||||
|
||||
public:
|
||||
VectorBuilder(IRBuilderBase &Builder,
|
||||
Behavior ErrorHandling = Behavior::ReportAndAbort)
|
||||
: Builder(Builder), ErrorHandling(ErrorHandling), Mask(nullptr),
|
||||
ExplicitVectorLength(nullptr),
|
||||
StaticVectorLength(ElementCount::getFixed(0)) {}
|
||||
|
||||
LLVM_ABI Module &getModule() const;
|
||||
LLVMContext &getContext() const { return Builder.getContext(); }
|
||||
|
||||
// All-true mask for the currently configured explicit vector length.
|
||||
LLVM_ABI Value *getAllTrueMask();
|
||||
|
||||
VectorBuilder &setMask(Value *NewMask) {
|
||||
Mask = NewMask;
|
||||
return *this;
|
||||
}
|
||||
VectorBuilder &setEVL(Value *NewExplicitVectorLength) {
|
||||
ExplicitVectorLength = NewExplicitVectorLength;
|
||||
return *this;
|
||||
}
|
||||
VectorBuilder &setStaticVL(unsigned NewFixedVL) {
|
||||
StaticVectorLength = ElementCount::getFixed(NewFixedVL);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Get the flags to be applied to created floating point ops.
|
||||
const FastMathFlags &getFastMathFlags() const {
|
||||
return Builder.getFastMathFlags();
|
||||
}
|
||||
|
||||
// TODO: setStaticVL(ElementCount) for scalable types.
|
||||
|
||||
// Emit a VP intrinsic call that mimics a regular instruction.
|
||||
// This operation behaves according to the VectorBuilderBehavior.
|
||||
// \p Opcode The functional instruction opcode of the emitted intrinsic.
|
||||
// \p ReturnTy The return type of the operation.
|
||||
// \p VecOpArray The operand list.
|
||||
LLVM_ABI Value *createVectorInstruction(unsigned Opcode, Type *ReturnTy,
|
||||
ArrayRef<Value *> VecOpArray,
|
||||
const Twine &Name = Twine());
|
||||
|
||||
/// Emit a VP reduction intrinsic call for recurrence kind.
|
||||
/// \param RdxID The intrinsic ID of llvm.vector.reduce.*
|
||||
/// \param ValTy The type of operand which the reduction operation is
|
||||
/// performed.
|
||||
/// \param VecOpArray The operand list.
|
||||
LLVM_ABI Value *createSimpleReduction(Intrinsic::ID RdxID, Type *ValTy,
|
||||
ArrayRef<Value *> VecOpArray,
|
||||
const Twine &Name = Twine());
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_IR_VECTORBUILDER_H
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "llvm/Analysis/IVDescriptors.h"
|
||||
#include "llvm/Analysis/LoopAccessAnalysis.h"
|
||||
#include "llvm/Analysis/TargetTransformInfo.h"
|
||||
#include "llvm/IR/VectorBuilder.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Transforms/Utils/ValueMapper.h"
|
||||
|
||||
@@ -423,8 +422,9 @@ LLVM_ABI Value *createSimpleReduction(IRBuilderBase &B, Value *Src,
|
||||
RecurKind RdxKind);
|
||||
/// Overloaded function to generate vector-predication intrinsics for
|
||||
/// reduction.
|
||||
LLVM_ABI Value *createSimpleReduction(VectorBuilder &VB, Value *Src,
|
||||
RecurKind RdxKind);
|
||||
LLVM_ABI Value *createSimpleReduction(IRBuilderBase &B, Value *Src,
|
||||
RecurKind RdxKind, Value *Mask,
|
||||
Value *EVL);
|
||||
|
||||
/// Create a reduction of the given vector \p Src for a reduction of kind
|
||||
/// RecurKind::AnyOf. The start value of the reduction is \p InitVal.
|
||||
@@ -442,8 +442,9 @@ LLVM_ABI Value *createOrderedReduction(IRBuilderBase &B, RecurKind RdxKind,
|
||||
Value *Src, Value *Start);
|
||||
/// Overloaded function to generate vector-predication intrinsics for ordered
|
||||
/// reduction.
|
||||
LLVM_ABI Value *createOrderedReduction(VectorBuilder &VB, RecurKind RdxKind,
|
||||
Value *Src, Value *Start);
|
||||
LLVM_ABI Value *createOrderedReduction(IRBuilderBase &B, RecurKind RdxKind,
|
||||
Value *Src, Value *Start, Value *Mask,
|
||||
Value *EVL);
|
||||
|
||||
/// Get the intersection (logical and) of all of the potential IR flags
|
||||
/// of each scalar operation (VL) that will be converted into a vector (I).
|
||||
|
||||
@@ -74,7 +74,6 @@ add_llvm_component_library(LLVMCore
|
||||
User.cpp
|
||||
Value.cpp
|
||||
ValueSymbolTable.cpp
|
||||
VectorBuilder.cpp
|
||||
VectorTypeUtils.cpp
|
||||
Verifier.cpp
|
||||
VFABIDemangler.cpp
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
//===- VectorBuilder.cpp - Builder for VP Intrinsics ----------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the VectorBuilder class, which is used as a convenient
|
||||
// way to create VP intrinsics as if they were LLVM instructions with a
|
||||
// consistent and simplified interface.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <llvm/ADT/SmallVector.h>
|
||||
#include <llvm/IR/FPEnv.h>
|
||||
#include <llvm/IR/Instructions.h>
|
||||
#include <llvm/IR/IntrinsicInst.h>
|
||||
#include <llvm/IR/Intrinsics.h>
|
||||
#include <llvm/IR/VectorBuilder.h>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
void VectorBuilder::handleError(const char *ErrorMsg) const {
|
||||
if (ErrorHandling == Behavior::SilentlyReturnNone)
|
||||
return;
|
||||
report_fatal_error(ErrorMsg);
|
||||
}
|
||||
|
||||
Module &VectorBuilder::getModule() const {
|
||||
return *Builder.GetInsertBlock()->getModule();
|
||||
}
|
||||
|
||||
Value *VectorBuilder::getAllTrueMask() {
|
||||
return Builder.getAllOnesMask(StaticVectorLength);
|
||||
}
|
||||
|
||||
Value &VectorBuilder::requestMask() {
|
||||
if (Mask)
|
||||
return *Mask;
|
||||
|
||||
return *getAllTrueMask();
|
||||
}
|
||||
|
||||
Value &VectorBuilder::requestEVL() {
|
||||
if (ExplicitVectorLength)
|
||||
return *ExplicitVectorLength;
|
||||
|
||||
assert(!StaticVectorLength.isScalable() && "TODO vscale lowering");
|
||||
auto *IntTy = Builder.getInt32Ty();
|
||||
return *ConstantInt::get(IntTy, StaticVectorLength.getFixedValue());
|
||||
}
|
||||
|
||||
Value *VectorBuilder::createVectorInstruction(unsigned Opcode, Type *ReturnTy,
|
||||
ArrayRef<Value *> InstOpArray,
|
||||
const Twine &Name) {
|
||||
auto VPID = VPIntrinsic::getForOpcode(Opcode);
|
||||
if (VPID == Intrinsic::not_intrinsic)
|
||||
return returnWithError<Value *>("No VPIntrinsic for this opcode");
|
||||
return createVectorInstructionImpl(VPID, ReturnTy, InstOpArray, Name);
|
||||
}
|
||||
|
||||
Value *VectorBuilder::createSimpleReduction(Intrinsic::ID RdxID,
|
||||
Type *ValTy,
|
||||
ArrayRef<Value *> InstOpArray,
|
||||
const Twine &Name) {
|
||||
auto VPID = VPIntrinsic::getForIntrinsic(RdxID);
|
||||
assert(VPReductionIntrinsic::isVPReduction(VPID) &&
|
||||
"No VPIntrinsic for this reduction");
|
||||
return createVectorInstructionImpl(VPID, ValTy, InstOpArray, Name);
|
||||
}
|
||||
|
||||
Value *VectorBuilder::createVectorInstructionImpl(Intrinsic::ID VPID,
|
||||
Type *ReturnTy,
|
||||
ArrayRef<Value *> InstOpArray,
|
||||
const Twine &Name) {
|
||||
auto MaskPosOpt = VPIntrinsic::getMaskParamPos(VPID);
|
||||
auto VLenPosOpt = VPIntrinsic::getVectorLengthParamPos(VPID);
|
||||
size_t NumInstParams = InstOpArray.size();
|
||||
size_t NumVPParams =
|
||||
NumInstParams + MaskPosOpt.has_value() + VLenPosOpt.has_value();
|
||||
|
||||
SmallVector<Value *, 6> IntrinParams;
|
||||
|
||||
// Whether the mask and vlen parameter are at the end of the parameter list.
|
||||
bool TrailingMaskAndVLen =
|
||||
std::min<size_t>(MaskPosOpt.value_or(NumInstParams),
|
||||
VLenPosOpt.value_or(NumInstParams)) >= NumInstParams;
|
||||
|
||||
if (TrailingMaskAndVLen) {
|
||||
// Fast path for trailing mask, vector length.
|
||||
IntrinParams.append(InstOpArray.begin(), InstOpArray.end());
|
||||
IntrinParams.resize(NumVPParams);
|
||||
} else {
|
||||
IntrinParams.resize(NumVPParams);
|
||||
// Insert mask and evl operands in between the instruction operands.
|
||||
for (size_t VPParamIdx = 0, ParamIdx = 0; VPParamIdx < NumVPParams;
|
||||
++VPParamIdx) {
|
||||
if (MaskPosOpt == VPParamIdx || VLenPosOpt == VPParamIdx)
|
||||
continue;
|
||||
assert(ParamIdx < NumInstParams);
|
||||
IntrinParams[VPParamIdx] = InstOpArray[ParamIdx++];
|
||||
}
|
||||
}
|
||||
|
||||
if (MaskPosOpt)
|
||||
IntrinParams[*MaskPosOpt] = &requestMask();
|
||||
if (VLenPosOpt)
|
||||
IntrinParams[*VLenPosOpt] = &requestEVL();
|
||||
|
||||
auto *VPDecl = VPIntrinsic::getOrInsertDeclarationForParams(
|
||||
&getModule(), VPID, ReturnTy, IntrinParams);
|
||||
return Builder.CreateCall(VPDecl, IntrinParams, Name);
|
||||
}
|
||||
|
||||
} // namespace llvm
|
||||
@@ -1319,18 +1319,19 @@ Value *llvm::createSimpleReduction(IRBuilderBase &Builder, Value *Src,
|
||||
}
|
||||
}
|
||||
|
||||
Value *llvm::createSimpleReduction(VectorBuilder &VBuilder, Value *Src,
|
||||
RecurKind Kind) {
|
||||
Value *llvm::createSimpleReduction(IRBuilderBase &Builder, Value *Src,
|
||||
RecurKind Kind, Value *Mask, Value *EVL) {
|
||||
assert(!RecurrenceDescriptor::isAnyOfRecurrenceKind(Kind) &&
|
||||
!RecurrenceDescriptor::isFindLastIVRecurrenceKind(Kind) &&
|
||||
"AnyOf or FindLastIV reductions are not supported.");
|
||||
Intrinsic::ID Id = getReductionIntrinsicID(Kind);
|
||||
auto *SrcTy = cast<VectorType>(Src->getType());
|
||||
Type *SrcEltTy = SrcTy->getElementType();
|
||||
Value *Iden =
|
||||
getRecurrenceIdentity(Kind, SrcEltTy, VBuilder.getFastMathFlags());
|
||||
Value *Ops[] = {Iden, Src};
|
||||
return VBuilder.createSimpleReduction(Id, SrcTy, Ops);
|
||||
auto VPID = VPIntrinsic::getForIntrinsic(Id);
|
||||
assert(VPReductionIntrinsic::isVPReduction(VPID) &&
|
||||
"No VPIntrinsic for this reduction");
|
||||
auto *EltTy = cast<VectorType>(Src->getType())->getElementType();
|
||||
Value *Iden = getRecurrenceIdentity(Kind, EltTy, Builder.getFastMathFlags());
|
||||
Value *Ops[] = {Iden, Src, Mask, EVL};
|
||||
return Builder.CreateIntrinsic(EltTy, VPID, Ops);
|
||||
}
|
||||
|
||||
Value *llvm::createOrderedReduction(IRBuilderBase &B, RecurKind Kind,
|
||||
@@ -1343,17 +1344,21 @@ Value *llvm::createOrderedReduction(IRBuilderBase &B, RecurKind Kind,
|
||||
return B.CreateFAddReduce(Start, Src);
|
||||
}
|
||||
|
||||
Value *llvm::createOrderedReduction(VectorBuilder &VBuilder, RecurKind Kind,
|
||||
Value *Src, Value *Start) {
|
||||
Value *llvm::createOrderedReduction(IRBuilderBase &Builder, RecurKind Kind,
|
||||
Value *Src, Value *Start, Value *Mask,
|
||||
Value *EVL) {
|
||||
assert((Kind == RecurKind::FAdd || Kind == RecurKind::FMulAdd) &&
|
||||
"Unexpected reduction kind");
|
||||
assert(Src->getType()->isVectorTy() && "Expected a vector type");
|
||||
assert(!Start->getType()->isVectorTy() && "Expected a scalar type");
|
||||
|
||||
Intrinsic::ID Id = getReductionIntrinsicID(RecurKind::FAdd);
|
||||
auto *SrcTy = cast<VectorType>(Src->getType());
|
||||
Value *Ops[] = {Start, Src};
|
||||
return VBuilder.createSimpleReduction(Id, SrcTy, Ops);
|
||||
auto VPID = VPIntrinsic::getForIntrinsic(Id);
|
||||
assert(VPReductionIntrinsic::isVPReduction(VPID) &&
|
||||
"No VPIntrinsic for this reduction");
|
||||
auto *EltTy = cast<VectorType>(Src->getType())->getElementType();
|
||||
Value *Ops[] = {Start, Src, Mask, EVL};
|
||||
return Builder.CreateIntrinsic(EltTy, VPID, Ops);
|
||||
}
|
||||
|
||||
void llvm::propagateIRFlags(Value *I, ArrayRef<Value *> VL, Value *OpValue,
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "llvm/IR/Intrinsics.h"
|
||||
#include "llvm/IR/Type.h"
|
||||
#include "llvm/IR/Value.h"
|
||||
#include "llvm/IR/VectorBuilder.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
@@ -2524,21 +2523,17 @@ void VPReductionEVLRecipe::execute(VPTransformState &State) {
|
||||
Value *VecOp = State.get(getVecOp());
|
||||
Value *EVL = State.get(getEVL(), VPLane(0));
|
||||
|
||||
VectorBuilder VBuilder(Builder);
|
||||
VBuilder.setEVL(EVL);
|
||||
Value *Mask;
|
||||
// TODO: move the all-true mask generation into VectorBuilder.
|
||||
if (VPValue *CondOp = getCondOp())
|
||||
Mask = State.get(CondOp);
|
||||
else
|
||||
Mask = Builder.CreateVectorSplat(State.VF, Builder.getTrue());
|
||||
VBuilder.setMask(Mask);
|
||||
|
||||
Value *NewRed;
|
||||
if (isOrdered()) {
|
||||
NewRed = createOrderedReduction(VBuilder, Kind, VecOp, Prev);
|
||||
NewRed = createOrderedReduction(Builder, Kind, VecOp, Prev, Mask, EVL);
|
||||
} else {
|
||||
NewRed = createSimpleReduction(VBuilder, VecOp, Kind);
|
||||
NewRed = createSimpleReduction(Builder, VecOp, Kind, Mask, EVL);
|
||||
if (RecurrenceDescriptor::isMinMaxRecurrenceKind(Kind))
|
||||
NewRed = createMinMaxOp(Builder, Kind, NewRed, Prev);
|
||||
else
|
||||
@@ -3086,10 +3081,8 @@ void VPWidenLoadEVLRecipe::execute(VPTransformState &State) {
|
||||
Builder.CreateIntrinsic(DataTy, Intrinsic::vp_gather, {Addr, Mask, EVL},
|
||||
nullptr, "wide.masked.gather");
|
||||
} else {
|
||||
VectorBuilder VBuilder(Builder);
|
||||
VBuilder.setEVL(EVL).setMask(Mask);
|
||||
NewLI = cast<CallInst>(VBuilder.createVectorInstruction(
|
||||
Instruction::Load, DataTy, Addr, "vp.op.load"));
|
||||
NewLI = Builder.CreateIntrinsic(DataTy, Intrinsic::vp_load,
|
||||
{Addr, Mask, EVL}, nullptr, "vp.op.load");
|
||||
}
|
||||
NewLI->addParamAttr(
|
||||
0, Attribute::getWithAlignment(NewLI->getContext(), Alignment));
|
||||
@@ -3204,11 +3197,9 @@ void VPWidenStoreEVLRecipe::execute(VPTransformState &State) {
|
||||
Intrinsic::vp_scatter,
|
||||
{StoredVal, Addr, Mask, EVL});
|
||||
} else {
|
||||
VectorBuilder VBuilder(Builder);
|
||||
VBuilder.setEVL(EVL).setMask(Mask);
|
||||
NewSI = cast<CallInst>(VBuilder.createVectorInstruction(
|
||||
Instruction::Store, Type::getVoidTy(EVL->getContext()),
|
||||
{StoredVal, Addr}));
|
||||
NewSI = Builder.CreateIntrinsic(Type::getVoidTy(EVL->getContext()),
|
||||
Intrinsic::vp_store,
|
||||
{StoredVal, Addr, Mask, EVL});
|
||||
}
|
||||
NewSI->addParamAttr(
|
||||
1, Attribute::getWithAlignment(NewSI->getContext(), Alignment));
|
||||
|
||||
@@ -50,7 +50,6 @@ add_llvm_unittest(IRTests
|
||||
ValueHandleTest.cpp
|
||||
ValueMapTest.cpp
|
||||
ValueTest.cpp
|
||||
VectorBuilderTest.cpp
|
||||
VectorTypeUtilsTest.cpp
|
||||
VectorTypesTest.cpp
|
||||
VerifierTest.cpp
|
||||
|
||||
@@ -1,279 +0,0 @@
|
||||
//===--------- VectorBuilderTest.cpp - VectorBuilder unit tests -----------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/IR/VectorBuilder.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
static unsigned VectorNumElements = 8;
|
||||
|
||||
class VectorBuilderTest : public testing::Test {
|
||||
protected:
|
||||
LLVMContext Context;
|
||||
|
||||
VectorBuilderTest() : Context() {}
|
||||
|
||||
std::unique_ptr<Module> createBuilderModule(Function *&Func, BasicBlock *&BB,
|
||||
Value *&Mask, Value *&EVL) {
|
||||
auto Mod = std::make_unique<Module>("TestModule", Context);
|
||||
auto *Int32Ty = Type::getInt32Ty(Context);
|
||||
auto *Mask8Ty =
|
||||
FixedVectorType::get(Type::getInt1Ty(Context), VectorNumElements);
|
||||
auto *VoidFuncTy =
|
||||
FunctionType::get(Type::getVoidTy(Context), {Mask8Ty, Int32Ty}, false);
|
||||
Func =
|
||||
Function::Create(VoidFuncTy, GlobalValue::ExternalLinkage, "bla", *Mod);
|
||||
Mask = Func->getArg(0);
|
||||
EVL = Func->getArg(1);
|
||||
BB = BasicBlock::Create(Context, "entry", Func);
|
||||
|
||||
return Mod;
|
||||
}
|
||||
};
|
||||
|
||||
/// Check that creating binary arithmetic VP intrinsics works.
|
||||
TEST_F(VectorBuilderTest, TestCreateBinaryInstructions) {
|
||||
Function *F;
|
||||
BasicBlock *BB;
|
||||
Value *Mask, *EVL;
|
||||
auto Mod = createBuilderModule(F, BB, Mask, EVL);
|
||||
|
||||
IRBuilder<> Builder(BB);
|
||||
VectorBuilder VBuild(Builder);
|
||||
VBuild.setMask(Mask).setEVL(EVL);
|
||||
|
||||
auto *FloatVecTy =
|
||||
FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);
|
||||
auto *IntVecTy =
|
||||
FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements);
|
||||
|
||||
#define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS) \
|
||||
{ \
|
||||
auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE); \
|
||||
bool IsFP = (#INSTCLASS)[0] == 'F'; \
|
||||
auto *ValueTy = IsFP ? FloatVecTy : IntVecTy; \
|
||||
Value *Op = PoisonValue::get(ValueTy); \
|
||||
auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy, \
|
||||
{Op, Op}); \
|
||||
ASSERT_TRUE(isa<VPIntrinsic>(I)); \
|
||||
auto *VPIntrin = cast<VPIntrinsic>(I); \
|
||||
ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID); \
|
||||
ASSERT_EQ(VPIntrin->getMaskParam(), Mask); \
|
||||
ASSERT_EQ(VPIntrin->getVectorLengthParam(), EVL); \
|
||||
}
|
||||
#include "llvm/IR/Instruction.def"
|
||||
}
|
||||
|
||||
static bool isAllTrueMask(Value *Val, unsigned NumElements) {
|
||||
auto *ConstMask = dyn_cast<Constant>(Val);
|
||||
if (!ConstMask)
|
||||
return false;
|
||||
|
||||
// Structure check.
|
||||
if (!ConstMask->isAllOnesValue())
|
||||
return false;
|
||||
|
||||
// Type check.
|
||||
auto *MaskVecTy = cast<FixedVectorType>(ConstMask->getType());
|
||||
if (MaskVecTy->getNumElements() != NumElements)
|
||||
return false;
|
||||
|
||||
return MaskVecTy->getElementType()->isIntegerTy(1);
|
||||
}
|
||||
|
||||
/// Check that creating binary arithmetic VP intrinsics works.
|
||||
TEST_F(VectorBuilderTest, TestCreateBinaryInstructions_FixedVector_NoMask) {
|
||||
Function *F;
|
||||
BasicBlock *BB;
|
||||
Value *Mask, *EVL;
|
||||
auto Mod = createBuilderModule(F, BB, Mask, EVL);
|
||||
|
||||
IRBuilder<> Builder(BB);
|
||||
VectorBuilder VBuild(Builder);
|
||||
VBuild.setEVL(EVL).setStaticVL(VectorNumElements);
|
||||
|
||||
auto *FloatVecTy =
|
||||
FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);
|
||||
auto *IntVecTy =
|
||||
FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements);
|
||||
|
||||
#define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS) \
|
||||
{ \
|
||||
auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE); \
|
||||
bool IsFP = (#INSTCLASS)[0] == 'F'; \
|
||||
Type *ValueTy = IsFP ? FloatVecTy : IntVecTy; \
|
||||
Value *Op = PoisonValue::get(ValueTy); \
|
||||
auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy, \
|
||||
{Op, Op}); \
|
||||
ASSERT_TRUE(isa<VPIntrinsic>(I)); \
|
||||
auto *VPIntrin = cast<VPIntrinsic>(I); \
|
||||
ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID); \
|
||||
ASSERT_TRUE(isAllTrueMask(VPIntrin->getMaskParam(), VectorNumElements)); \
|
||||
ASSERT_EQ(VPIntrin->getVectorLengthParam(), EVL); \
|
||||
}
|
||||
#include "llvm/IR/Instruction.def"
|
||||
}
|
||||
|
||||
static bool isLegalConstEVL(Value *Val, unsigned ExpectedEVL) {
|
||||
auto *ConstEVL = dyn_cast<ConstantInt>(Val);
|
||||
if (!ConstEVL)
|
||||
return false;
|
||||
|
||||
// Value check.
|
||||
if (ConstEVL->getZExtValue() != ExpectedEVL)
|
||||
return false;
|
||||
|
||||
// Type check.
|
||||
return ConstEVL->getType()->isIntegerTy(32);
|
||||
}
|
||||
|
||||
/// Check that creating binary arithmetic VP intrinsics works.
|
||||
TEST_F(VectorBuilderTest, TestCreateBinaryInstructions_FixedVector_NoEVL) {
|
||||
Function *F;
|
||||
BasicBlock *BB;
|
||||
Value *Mask, *EVL;
|
||||
auto Mod = createBuilderModule(F, BB, Mask, EVL);
|
||||
|
||||
IRBuilder<> Builder(BB);
|
||||
VectorBuilder VBuild(Builder);
|
||||
VBuild.setMask(Mask).setStaticVL(VectorNumElements);
|
||||
|
||||
auto *FloatVecTy =
|
||||
FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);
|
||||
auto *IntVecTy =
|
||||
FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements);
|
||||
|
||||
#define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS) \
|
||||
{ \
|
||||
auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE); \
|
||||
bool IsFP = (#INSTCLASS)[0] == 'F'; \
|
||||
Type *ValueTy = IsFP ? FloatVecTy : IntVecTy; \
|
||||
Value *Op = PoisonValue::get(ValueTy); \
|
||||
auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy, \
|
||||
{Op, Op}); \
|
||||
ASSERT_TRUE(isa<VPIntrinsic>(I)); \
|
||||
auto *VPIntrin = cast<VPIntrinsic>(I); \
|
||||
ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID); \
|
||||
ASSERT_EQ(VPIntrin->getMaskParam(), Mask); \
|
||||
ASSERT_TRUE( \
|
||||
isLegalConstEVL(VPIntrin->getVectorLengthParam(), VectorNumElements)); \
|
||||
}
|
||||
#include "llvm/IR/Instruction.def"
|
||||
}
|
||||
|
||||
/// Check that creating binary arithmetic VP intrinsics works.
|
||||
TEST_F(VectorBuilderTest,
|
||||
TestCreateBinaryInstructions_FixedVector_NoMask_NoEVL) {
|
||||
Function *F;
|
||||
BasicBlock *BB;
|
||||
Value *Mask, *EVL;
|
||||
auto Mod = createBuilderModule(F, BB, Mask, EVL);
|
||||
|
||||
IRBuilder<> Builder(BB);
|
||||
VectorBuilder VBuild(Builder);
|
||||
VBuild.setStaticVL(VectorNumElements);
|
||||
|
||||
auto *FloatVecTy =
|
||||
FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);
|
||||
auto *IntVecTy =
|
||||
FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements);
|
||||
|
||||
#define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS) \
|
||||
{ \
|
||||
auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE); \
|
||||
bool IsFP = (#INSTCLASS)[0] == 'F'; \
|
||||
Type *ValueTy = IsFP ? FloatVecTy : IntVecTy; \
|
||||
Value *Op = PoisonValue::get(ValueTy); \
|
||||
auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy, \
|
||||
{Op, Op}); \
|
||||
ASSERT_TRUE(isa<VPIntrinsic>(I)); \
|
||||
auto *VPIntrin = cast<VPIntrinsic>(I); \
|
||||
ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID); \
|
||||
ASSERT_TRUE(isAllTrueMask(VPIntrin->getMaskParam(), VectorNumElements)); \
|
||||
ASSERT_TRUE( \
|
||||
isLegalConstEVL(VPIntrin->getVectorLengthParam(), VectorNumElements)); \
|
||||
}
|
||||
#include "llvm/IR/Instruction.def"
|
||||
}
|
||||
/// Check that creating vp.load/vp.store works.
|
||||
TEST_F(VectorBuilderTest, TestCreateLoadStore) {
|
||||
Function *F;
|
||||
BasicBlock *BB;
|
||||
Value *Mask, *EVL;
|
||||
auto Mod = createBuilderModule(F, BB, Mask, EVL);
|
||||
|
||||
IRBuilder<> Builder(BB);
|
||||
VectorBuilder VBuild(Builder);
|
||||
VBuild.setMask(Mask).setEVL(EVL);
|
||||
|
||||
auto *FloatVecTy =
|
||||
FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);
|
||||
|
||||
Value *FloatVecPtr = PoisonValue::get(Builder.getPtrTy(0));
|
||||
Value *FloatVec = PoisonValue::get(FloatVecTy);
|
||||
|
||||
// vp.load
|
||||
auto LoadVPID = VPIntrinsic::getForOpcode(Instruction::Load);
|
||||
auto *LoadIntrin = VBuild.createVectorInstruction(Instruction::Load,
|
||||
FloatVecTy, {FloatVecPtr});
|
||||
ASSERT_TRUE(isa<VPIntrinsic>(LoadIntrin));
|
||||
auto *VPLoad = cast<VPIntrinsic>(LoadIntrin);
|
||||
ASSERT_EQ(VPLoad->getIntrinsicID(), LoadVPID);
|
||||
ASSERT_EQ(VPLoad->getMemoryPointerParam(), FloatVecPtr);
|
||||
|
||||
// vp.store
|
||||
auto *VoidTy = Builder.getVoidTy();
|
||||
auto StoreVPID = VPIntrinsic::getForOpcode(Instruction::Store);
|
||||
auto *StoreIntrin = VBuild.createVectorInstruction(Instruction::Store, VoidTy,
|
||||
{FloatVec, FloatVecPtr});
|
||||
ASSERT_TRUE(isa<VPIntrinsic>(LoadIntrin));
|
||||
auto *VPStore = cast<VPIntrinsic>(StoreIntrin);
|
||||
ASSERT_EQ(VPStore->getIntrinsicID(), StoreVPID);
|
||||
ASSERT_EQ(VPStore->getMemoryPointerParam(), FloatVecPtr);
|
||||
ASSERT_EQ(VPStore->getMemoryDataParam(), FloatVec);
|
||||
}
|
||||
|
||||
/// Check that the SilentlyReturnNone error handling mode works.
|
||||
TEST_F(VectorBuilderTest, TestFail_SilentlyReturnNone) {
|
||||
Function *F;
|
||||
BasicBlock *BB;
|
||||
Value *Mask, *EVL;
|
||||
auto Mod = createBuilderModule(F, BB, Mask, EVL);
|
||||
|
||||
IRBuilder<> Builder(BB);
|
||||
auto *VoidTy = Builder.getVoidTy();
|
||||
VectorBuilder VBuild(Builder, VectorBuilder::Behavior::SilentlyReturnNone);
|
||||
VBuild.setMask(Mask).setEVL(EVL);
|
||||
auto *Val = VBuild.createVectorInstruction(Instruction::Br, VoidTy, {});
|
||||
ASSERT_EQ(Val, nullptr);
|
||||
}
|
||||
|
||||
/// Check that the ReportAndFail error handling mode aborts as advertised.
|
||||
TEST_F(VectorBuilderTest, TestFail_ReportAndAbort) {
|
||||
Function *F;
|
||||
BasicBlock *BB;
|
||||
Value *Mask, *EVL;
|
||||
auto Mod = createBuilderModule(F, BB, Mask, EVL);
|
||||
|
||||
IRBuilder<> Builder(BB);
|
||||
auto *VoidTy = Builder.getVoidTy();
|
||||
VectorBuilder VBuild(Builder, VectorBuilder::Behavior::ReportAndAbort);
|
||||
VBuild.setMask(Mask).setEVL(EVL);
|
||||
ASSERT_DEATH({ VBuild.createVectorInstruction(Instruction::Br, VoidTy, {}); },
|
||||
"No VPIntrinsic for this opcode");
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
Reference in New Issue
Block a user