1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-02-01 05:01:59 +01:00

[IntrinsicEmitter] Extend argument overloading with forward references.

Extend the mechanism to overload intrinsic arguments by using either
backward or forward references to the overloadable arguments.

In for example:

  def int_something : Intrinsic<[LLVMPointerToElt<0>],
                                [llvm_anyvector_ty], []>;

LLVMPointerToElt<0> is a forward reference to the overloadable operand
of type 'llvm_anyvector_ty' and would allow intrinsics such as:

  declare i32* @llvm.something.v4i32(<4 x i32>);
  declare i64* @llvm.something.v2i64(<2 x i64>);

where the result pointer type is deduced from the element type of the
first argument.

If the returned pointer is not a pointer to the element type, LLVM will
give an error:

  Intrinsic has incorrect return type!
  i64* (<4 x i32>)* @llvm.something.v4i32

Reviewers: RKSimon, arsenm, rnk, greened

Reviewed By: arsenm

Differential Revision: https://reviews.llvm.org/D62995

llvm-svn: 363233
This commit is contained in:
Sander de Smalen 2019-06-13 08:19:33 +00:00
parent 13c49f48e7
commit dfbd44a9f0
6 changed files with 193 additions and 73 deletions

View File

@ -116,7 +116,8 @@ namespace Intrinsic {
AK_AnyInteger,
AK_AnyFloat,
AK_AnyVector,
AK_AnyPointer
AK_AnyPointer,
AK_MatchType = 7
};
unsigned getArgumentNumber() const {
@ -161,14 +162,21 @@ namespace Intrinsic {
/// of IITDescriptors.
void getIntrinsicInfoTableEntries(ID id, SmallVectorImpl<IITDescriptor> &T);
/// Match the specified type (which comes from an intrinsic argument or return
/// value) with the type constraints specified by the .td file. If the given
/// type is an overloaded type it is pushed to the ArgTys vector.
enum MatchIntrinsicTypesResult {
MatchIntrinsicTypes_Match = 0,
MatchIntrinsicTypes_NoMatchRet = 1,
MatchIntrinsicTypes_NoMatchArg = 2,
};
/// Match the specified function type with the type constraints specified by
/// the .td file. If the given type is an overloaded type it is pushed to the
/// ArgTys vector.
///
/// Returns false if the given type matches with the constraints, true
/// otherwise.
bool matchIntrinsicType(Type *Ty, ArrayRef<IITDescriptor> &Infos,
SmallVectorImpl<Type*> &ArgTys);
MatchIntrinsicTypesResult
matchIntrinsicSignature(FunctionType *FTy, ArrayRef<IITDescriptor> &Infos,
SmallVectorImpl<Type *> &ArgTys);
/// Verify if the intrinsic has variable arguments. This method is intended to
/// be called after all the fixed arguments have been matched first.

View File

@ -1047,12 +1047,26 @@ Function *Intrinsic::getDeclaration(Module *M, ID id, ArrayRef<Type*> Tys) {
#include "llvm/IR/IntrinsicImpl.inc"
#undef GET_LLVM_INTRINSIC_FOR_MS_BUILTIN
bool Intrinsic::matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor> &Infos,
SmallVectorImpl<Type*> &ArgTys) {
using DeferredIntrinsicMatchPair =
std::pair<Type *, ArrayRef<Intrinsic::IITDescriptor>>;
static bool matchIntrinsicType(
Type *Ty, ArrayRef<Intrinsic::IITDescriptor> &Infos,
SmallVectorImpl<Type *> &ArgTys,
SmallVectorImpl<DeferredIntrinsicMatchPair> &DeferredChecks,
bool IsDeferredCheck) {
using namespace Intrinsic;
// If we ran out of descriptors, there are too many arguments.
if (Infos.empty()) return true;
// Do this before slicing off the 'front' part
auto InfosRef = Infos;
auto DeferCheck = [&DeferredChecks, &InfosRef](Type *T) {
DeferredChecks.emplace_back(T, InfosRef);
return false;
};
IITDescriptor D = Infos.front();
Infos = Infos.slice(1);
@ -1070,12 +1084,14 @@ bool Intrinsic::matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor>
case IITDescriptor::Vector: {
VectorType *VT = dyn_cast<VectorType>(Ty);
return !VT || VT->getNumElements() != D.Vector_Width ||
matchIntrinsicType(VT->getElementType(), Infos, ArgTys);
matchIntrinsicType(VT->getElementType(), Infos, ArgTys,
DeferredChecks, IsDeferredCheck);
}
case IITDescriptor::Pointer: {
PointerType *PT = dyn_cast<PointerType>(Ty);
return !PT || PT->getAddressSpace() != D.Pointer_AddressSpace ||
matchIntrinsicType(PT->getElementType(), Infos, ArgTys);
matchIntrinsicType(PT->getElementType(), Infos, ArgTys,
DeferredChecks, IsDeferredCheck);
}
case IITDescriptor::Struct: {
@ -1084,20 +1100,24 @@ bool Intrinsic::matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor>
return true;
for (unsigned i = 0, e = D.Struct_NumElements; i != e; ++i)
if (matchIntrinsicType(ST->getElementType(i), Infos, ArgTys))
if (matchIntrinsicType(ST->getElementType(i), Infos, ArgTys,
DeferredChecks, IsDeferredCheck))
return true;
return false;
}
case IITDescriptor::Argument:
// Two cases here - If this is the second occurrence of an argument, verify
// that the later instance matches the previous instance.
// If this is the second occurrence of an argument,
// verify that the later instance matches the previous instance.
if (D.getArgumentNumber() < ArgTys.size())
return Ty != ArgTys[D.getArgumentNumber()];
// Otherwise, if this is the first instance of an argument, record it and
// verify the "Any" kind.
assert(D.getArgumentNumber() == ArgTys.size() && "Table consistency error");
if (D.getArgumentNumber() > ArgTys.size() ||
D.getArgumentKind() == IITDescriptor::AK_MatchType)
return IsDeferredCheck || DeferCheck(Ty);
assert(D.getArgumentNumber() == ArgTys.size() && !IsDeferredCheck &&
"Table consistency error");
ArgTys.push_back(Ty);
switch (D.getArgumentKind()) {
@ -1106,13 +1126,14 @@ bool Intrinsic::matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor>
case IITDescriptor::AK_AnyFloat: return !Ty->isFPOrFPVectorTy();
case IITDescriptor::AK_AnyVector: return !isa<VectorType>(Ty);
case IITDescriptor::AK_AnyPointer: return !isa<PointerType>(Ty);
default: break;
}
llvm_unreachable("all argument kinds not covered");
case IITDescriptor::ExtendArgument: {
// This may only be used when referring to a previous vector argument.
// If this is a forward reference, defer the check for later.
if (D.getArgumentNumber() >= ArgTys.size())
return true;
return IsDeferredCheck || DeferCheck(Ty);
Type *NewTy = ArgTys[D.getArgumentNumber()];
if (VectorType *VTy = dyn_cast<VectorType>(NewTy))
@ -1125,9 +1146,9 @@ bool Intrinsic::matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor>
return Ty != NewTy;
}
case IITDescriptor::TruncArgument: {
// This may only be used when referring to a previous vector argument.
// If this is a forward reference, defer the check for later.
if (D.getArgumentNumber() >= ArgTys.size())
return true;
return IsDeferredCheck || DeferCheck(Ty);
Type *NewTy = ArgTys[D.getArgumentNumber()];
if (VectorType *VTy = dyn_cast<VectorType>(NewTy))
@ -1140,14 +1161,17 @@ bool Intrinsic::matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor>
return Ty != NewTy;
}
case IITDescriptor::HalfVecArgument:
// This may only be used when referring to a previous vector argument.
// If this is a forward reference, defer the check for later.
return D.getArgumentNumber() >= ArgTys.size() ||
!isa<VectorType>(ArgTys[D.getArgumentNumber()]) ||
VectorType::getHalfElementsVectorType(
cast<VectorType>(ArgTys[D.getArgumentNumber()])) != Ty;
case IITDescriptor::SameVecWidthArgument: {
if (D.getArgumentNumber() >= ArgTys.size())
return true;
if (D.getArgumentNumber() >= ArgTys.size()) {
// Defer check and subsequent check for the vector element type.
Infos = Infos.slice(1);
return IsDeferredCheck || DeferCheck(Ty);
}
auto *ReferenceType = dyn_cast<VectorType>(ArgTys[D.getArgumentNumber()]);
auto *ThisArgType = dyn_cast<VectorType>(Ty);
// Both must be vectors of the same number of elements or neither.
@ -1160,18 +1184,19 @@ bool Intrinsic::matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor>
return true;
EltTy = ThisArgType->getVectorElementType();
}
return matchIntrinsicType(EltTy, Infos, ArgTys);
return matchIntrinsicType(EltTy, Infos, ArgTys, DeferredChecks,
IsDeferredCheck);
}
case IITDescriptor::PtrToArgument: {
if (D.getArgumentNumber() >= ArgTys.size())
return true;
return IsDeferredCheck || DeferCheck(Ty);
Type * ReferenceType = ArgTys[D.getArgumentNumber()];
PointerType *ThisArgType = dyn_cast<PointerType>(Ty);
return (!ThisArgType || ThisArgType->getElementType() != ReferenceType);
}
case IITDescriptor::PtrToElt: {
if (D.getArgumentNumber() >= ArgTys.size())
return true;
return IsDeferredCheck || DeferCheck(Ty);
VectorType * ReferenceType =
dyn_cast<VectorType> (ArgTys[D.getArgumentNumber()]);
PointerType *ThisArgType = dyn_cast<PointerType>(Ty);
@ -1181,15 +1206,20 @@ bool Intrinsic::matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor>
}
case IITDescriptor::VecOfAnyPtrsToElt: {
unsigned RefArgNumber = D.getRefArgNumber();
if (RefArgNumber >= ArgTys.size()) {
if (IsDeferredCheck)
return true;
// If forward referencing, already add the pointer-vector type and
// defer the checks for later.
ArgTys.push_back(Ty);
return DeferCheck(Ty);
}
// This may only be used when referring to a previous argument.
if (RefArgNumber >= ArgTys.size())
return true;
// Record the overloaded type
assert(D.getOverloadArgNumber() == ArgTys.size() &&
"Table consistency error");
ArgTys.push_back(Ty);
if (!IsDeferredCheck){
assert(D.getOverloadArgNumber() == ArgTys.size() &&
"Table consistency error");
ArgTys.push_back(Ty);
}
// Verify the overloaded type "matches" the Ref type.
// i.e. Ty is a vector with the same width as Ref.
@ -1211,6 +1241,32 @@ bool Intrinsic::matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor>
llvm_unreachable("unhandled");
}
Intrinsic::MatchIntrinsicTypesResult
Intrinsic::matchIntrinsicSignature(FunctionType *FTy,
ArrayRef<Intrinsic::IITDescriptor> &Infos,
SmallVectorImpl<Type *> &ArgTys) {
SmallVector<DeferredIntrinsicMatchPair, 2> DeferredChecks;
if (matchIntrinsicType(FTy->getReturnType(), Infos, ArgTys, DeferredChecks,
false))
return MatchIntrinsicTypes_NoMatchRet;
unsigned NumDeferredReturnChecks = DeferredChecks.size();
for (auto Ty : FTy->params())
if (matchIntrinsicType(Ty, Infos, ArgTys, DeferredChecks, false))
return MatchIntrinsicTypes_NoMatchArg;
for (unsigned I = 0, E = DeferredChecks.size(); I != E; ++I) {
DeferredIntrinsicMatchPair &Check = DeferredChecks[I];
if (matchIntrinsicType(Check.first, Check.second, ArgTys, DeferredChecks,
true))
return I < NumDeferredReturnChecks ? MatchIntrinsicTypes_NoMatchRet
: MatchIntrinsicTypes_NoMatchArg;
}
return MatchIntrinsicTypes_Match;
}
bool
Intrinsic::matchIntrinsicVarArg(bool isVarArg,
ArrayRef<Intrinsic::IITDescriptor> &Infos) {
@ -1244,13 +1300,8 @@ Optional<Function*> Intrinsic::remangleIntrinsicFunction(Function *F) {
getIntrinsicInfoTableEntries(ID, Table);
ArrayRef<Intrinsic::IITDescriptor> TableRef = Table;
// If we encounter any problems matching the signature with the descriptor
// just give up remangling. It's up to verifier to report the discrepancy.
if (Intrinsic::matchIntrinsicType(FTy->getReturnType(), TableRef, ArgTys))
if (Intrinsic::matchIntrinsicSignature(FTy, TableRef, ArgTys))
return None;
for (auto Ty : FTy->params())
if (Intrinsic::matchIntrinsicType(Ty, TableRef, ArgTys))
return None;
if (Intrinsic::matchIntrinsicVarArg(FTy->isVarArg(), TableRef))
return None;
}

View File

@ -4154,14 +4154,14 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
getIntrinsicInfoTableEntries(ID, Table);
ArrayRef<Intrinsic::IITDescriptor> TableRef = Table;
// Walk the descriptors to extract overloaded types.
SmallVector<Type *, 4> ArgTys;
Assert(!Intrinsic::matchIntrinsicType(IFTy->getReturnType(),
TableRef, ArgTys),
Intrinsic::MatchIntrinsicTypesResult Res =
Intrinsic::matchIntrinsicSignature(IFTy, TableRef, ArgTys);
Assert(Res != Intrinsic::MatchIntrinsicTypes_NoMatchRet,
"Intrinsic has incorrect return type!", IF);
for (unsigned i = 0, e = IFTy->getNumParams(); i != e; ++i)
Assert(!Intrinsic::matchIntrinsicType(IFTy->getParamType(i),
TableRef, ArgTys),
"Intrinsic has incorrect argument type!", IF);
Assert(Res != Intrinsic::MatchIntrinsicTypes_NoMatchArg,
"Intrinsic has incorrect argument type!", IF);
// Verify if the intrinsic call matches the vararg property.
if (IsVarArg)

View File

@ -1019,13 +1019,12 @@ Value *InstCombiner::simplifyAMDGCNMemoryIntrinsicDemanded(IntrinsicInst *II,
getIntrinsicInfoTableEntries(IID, Table);
ArrayRef<Intrinsic::IITDescriptor> TableRef = Table;
// Validate function argument and return types, extracting overloaded types
// along the way.
FunctionType *FTy = II->getCalledFunction()->getFunctionType();
SmallVector<Type *, 6> OverloadTys;
Intrinsic::matchIntrinsicType(FTy->getReturnType(), TableRef, OverloadTys);
for (unsigned i = 0, e = FTy->getNumParams(); i != e; ++i)
Intrinsic::matchIntrinsicType(FTy->getParamType(i), TableRef, OverloadTys);
Intrinsic::matchIntrinsicSignature(FTy, TableRef, OverloadTys);
// Get the new return type overload of the intrinsic.
Module *M = II->getParent()->getParent()->getParent();
Type *EltTy = II->getType()->getVectorElementType();
Type *NewTy = (NewNumElts == 1) ? EltTy : VectorType::get(EltTy, NewNumElts);

View File

@ -591,9 +591,29 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) {
TargetPrefix + ".'!");
}
// Parse the list of return types.
ListInit *RetTypes = R->getValueAsListInit("RetTypes");
ListInit *ParamTypes = R->getValueAsListInit("ParamTypes");
// First collate a list of overloaded types.
std::vector<MVT::SimpleValueType> OverloadedVTs;
ListInit *TypeList = R->getValueAsListInit("RetTypes");
for (ListInit *TypeList : {RetTypes, ParamTypes}) {
for (unsigned i = 0, e = TypeList->size(); i != e; ++i) {
Record *TyEl = TypeList->getElementAsRecord(i);
assert(TyEl->isSubClassOf("LLVMType") && "Expected a type!");
if (TyEl->isSubClassOf("LLVMMatchType"))
continue;
MVT::SimpleValueType VT = getValueType(TyEl->getValueAsDef("VT"));
if (MVT(VT).isOverloaded()) {
OverloadedVTs.push_back(VT);
isOverloaded = true;
}
}
}
// Parse the list of return types.
ListInit *TypeList = RetTypes;
for (unsigned i = 0, e = TypeList->size(); i != e; ++i) {
Record *TyEl = TypeList->getElementAsRecord(i);
assert(TyEl->isSubClassOf("LLVMType") && "Expected a type!");
@ -613,10 +633,6 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) {
} else {
VT = getValueType(TyEl->getValueAsDef("VT"));
}
if (MVT(VT).isOverloaded()) {
OverloadedVTs.push_back(VT);
isOverloaded = true;
}
// Reject invalid types.
if (VT == MVT::isVoid)
@ -628,7 +644,7 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) {
}
// Parse the list of parameter types.
TypeList = R->getValueAsListInit("ParamTypes");
TypeList = ParamTypes;
for (unsigned i = 0, e = TypeList->size(); i != e; ++i) {
Record *TyEl = TypeList->getElementAsRecord(i);
assert(TyEl->isSubClassOf("LLVMType") && "Expected a type!");
@ -654,11 +670,6 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) {
} else
VT = getValueType(TyEl->getValueAsDef("VT"));
if (MVT(VT).isOverloaded()) {
OverloadedVTs.push_back(VT);
isOverloaded = true;
}
// Reject invalid types.
if (VT == MVT::isVoid && i != e-1 /*void at end means varargs*/)
PrintFatalError(DefLoc, "Intrinsic '" + DefName +

View File

@ -258,10 +258,12 @@ static void EncodeFixedValueType(MVT::SimpleValueType VT,
#endif
static void EncodeFixedType(Record *R, std::vector<unsigned char> &ArgCodes,
std::vector<unsigned char> &Sig) {
unsigned &NextArgCode,
std::vector<unsigned char> &Sig,
ArrayRef<unsigned char> Mapping) {
if (R->isSubClassOf("LLVMMatchType")) {
unsigned Number = R->getValueAsInt("Number");
unsigned Number = Mapping[R->getValueAsInt("Number")];
assert(Number < ArgCodes.size() && "Invalid matching number!");
if (R->isSubClassOf("LLVMExtendedType"))
Sig.push_back(IIT_EXTEND_ARG);
@ -280,10 +282,8 @@ static void EncodeFixedType(Record *R, std::vector<unsigned char> &ArgCodes,
Sig.push_back(IIT_PTR_TO_ARG);
else if (R->isSubClassOf("LLVMVectorOfAnyPointersToElt")) {
Sig.push_back(IIT_VEC_OF_ANYPTRS_TO_ELT);
unsigned ArgNo = ArgCodes.size();
ArgCodes.push_back(3 /*vAny*/);
// Encode overloaded ArgNo
Sig.push_back(ArgNo);
Sig.push_back(NextArgCode++);
// Encode LLVMMatchType<Number> ArgNo
Sig.push_back(Number);
return;
@ -291,7 +291,7 @@ static void EncodeFixedType(Record *R, std::vector<unsigned char> &ArgCodes,
Sig.push_back(IIT_PTR_TO_ELT);
else
Sig.push_back(IIT_ARG);
return Sig.push_back((Number << 3) | ArgCodes[Number]);
return Sig.push_back((Number << 3) | 7 /*IITDescriptor::AK_MatchType*/);
}
MVT::SimpleValueType VT = getValueType(R->getValueAsDef("VT"));
@ -309,8 +309,9 @@ static void EncodeFixedType(Record *R, std::vector<unsigned char> &ArgCodes,
Sig.push_back(IIT_ARG);
// Figure out what arg # this is consuming, and remember what kind it was.
unsigned ArgNo = ArgCodes.size();
ArgCodes.push_back(Tmp);
assert(NextArgCode < ArgCodes.size() && ArgCodes[NextArgCode] == Tmp &&
"Invalid or no ArgCode associated with overloaded VT!");
unsigned ArgNo = NextArgCode++;
// Encode what sort of argument it must be in the low 3 bits of the ArgNo.
return Sig.push_back((ArgNo << 3) | Tmp);
@ -328,7 +329,8 @@ static void EncodeFixedType(Record *R, std::vector<unsigned char> &ArgCodes,
} else {
Sig.push_back(IIT_PTR);
}
return EncodeFixedType(R->getValueAsDef("ElTy"), ArgCodes, Sig);
return EncodeFixedType(R->getValueAsDef("ElTy"), ArgCodes, NextArgCode, Sig,
Mapping);
}
}
@ -353,6 +355,42 @@ static void EncodeFixedType(Record *R, std::vector<unsigned char> &ArgCodes,
EncodeFixedValueType(VT, Sig);
}
static void UpdateArgCodes(Record *R, std::vector<unsigned char> &ArgCodes,
unsigned int &NumInserted,
SmallVectorImpl<unsigned char> &Mapping) {
if (R->isSubClassOf("LLVMMatchType")) {
if (R->isSubClassOf("LLVMVectorOfAnyPointersToElt")) {
ArgCodes.push_back(3 /*vAny*/);
++NumInserted;
}
return;
}
unsigned Tmp = 0;
switch (getValueType(R->getValueAsDef("VT"))) {
default: break;
case MVT::iPTRAny:
++Tmp;
LLVM_FALLTHROUGH;
case MVT::vAny:
++Tmp;
LLVM_FALLTHROUGH;
case MVT::fAny:
++Tmp;
LLVM_FALLTHROUGH;
case MVT::iAny:
++Tmp;
LLVM_FALLTHROUGH;
case MVT::Any:
unsigned OriginalIdx = ArgCodes.size() - NumInserted;
assert(OriginalIdx >= Mapping.size());
Mapping.resize(OriginalIdx+1);
Mapping[OriginalIdx] = ArgCodes.size();
ArgCodes.push_back(Tmp);
break;
}
}
#if defined(_MSC_VER) && !defined(__clang__)
#pragma optimize("",on)
#endif
@ -363,6 +401,17 @@ static void ComputeFixedEncoding(const CodeGenIntrinsic &Int,
std::vector<unsigned char> &TypeSig) {
std::vector<unsigned char> ArgCodes;
// Add codes for any overloaded result VTs.
unsigned int NumInserted = 0;
SmallVector<unsigned char, 8> ArgMapping;
for (unsigned i = 0, e = Int.IS.RetVTs.size(); i != e; ++i)
UpdateArgCodes(Int.IS.RetTypeDefs[i], ArgCodes, NumInserted, ArgMapping);
// Add codes for any overloaded operand VTs.
for (unsigned i = 0, e = Int.IS.ParamTypeDefs.size(); i != e; ++i)
UpdateArgCodes(Int.IS.ParamTypeDefs[i], ArgCodes, NumInserted, ArgMapping);
unsigned NextArgCode = 0;
if (Int.IS.RetVTs.empty())
TypeSig.push_back(IIT_Done);
else if (Int.IS.RetVTs.size() == 1 &&
@ -382,11 +431,13 @@ static void ComputeFixedEncoding(const CodeGenIntrinsic &Int,
}
for (unsigned i = 0, e = Int.IS.RetVTs.size(); i != e; ++i)
EncodeFixedType(Int.IS.RetTypeDefs[i], ArgCodes, TypeSig);
EncodeFixedType(Int.IS.RetTypeDefs[i], ArgCodes, NextArgCode, TypeSig,
ArgMapping);
}
for (unsigned i = 0, e = Int.IS.ParamTypeDefs.size(); i != e; ++i)
EncodeFixedType(Int.IS.ParamTypeDefs[i], ArgCodes, TypeSig);
EncodeFixedType(Int.IS.ParamTypeDefs[i], ArgCodes, NextArgCode, TypeSig,
ArgMapping);
}
static void printIITEntry(raw_ostream &OS, unsigned char X) {