26#include "llvm/IR/IntrinsicsSPIRV.h"
40 if (MIRBuilder.
getMF()
47 if (IndirectCalls.size() > 0) {
48 produceIndirectPtrTypes(MIRBuilder);
49 IndirectCalls.clear();
58 return MIRBuilder.
buildInstr(SPIRV::OpReturnValue)
61 *STI.getRegBankInfo());
74 if (
F.hasFnAttribute(Attribute::AttrKind::NoInline))
75 FuncControl |=
static_cast<uint32_t>(SPIRV::FunctionControl::DontInline);
76 else if (
F.hasFnAttribute(Attribute::AttrKind::AlwaysInline))
77 FuncControl |=
static_cast<uint32_t>(SPIRV::FunctionControl::Inline);
80 FuncControl |=
static_cast<uint32_t>(SPIRV::FunctionControl::Pure);
82 FuncControl |=
static_cast<uint32_t>(SPIRV::FunctionControl::Const);
84 if (ST->canUseExtension(SPIRV::Extension::SPV_INTEL_optnone) ||
85 ST->canUseExtension(SPIRV::Extension::SPV_EXT_optnone))
86 if (
F.hasFnAttribute(Attribute::OptimizeNone))
87 FuncControl |=
static_cast<uint32_t>(SPIRV::FunctionControl::OptNoneEXT);
110 bool hasArgPtrs =
false;
111 for (
auto &Arg :
F.args()) {
113 if (Arg.getType()->isPointerTy()) {
119 Type *RetTy = FTy->getReturnType();
129 for (
auto SArgTy : SArgTys)
138 auto *NamedMD =
F.getParent()->getNamedMetadata(
"spv.cloned_funcs");
139 if (NamedMD ==
nullptr)
140 return F.getFunctionType();
142 Type *RetTy =
F.getFunctionType()->getReturnType();
144 for (
auto &Arg :
F.args())
148 std::find_if(NamedMD->op_begin(), NamedMD->op_end(), [&
F](
MDNode *
N) {
149 return isa<MDString>(N->getOperand(0)) &&
150 cast<MDString>(N->getOperand(0))->getString() == F.getName();
154 if (ThisFuncMDIt != NamedMD->op_end()) {
155 auto *ThisFuncMD = *ThisFuncMDIt;
157 assert(MD &&
"MDNode operand is expected");
161 assert(CMeta &&
"ConstantAsMetadata operand is expected");
162 assert(Const->getSExtValue() >= -1);
165 if (Const->getSExtValue() == -1)
166 RetTy = CMeta->getType();
168 ArgTypes[Const->getSExtValue()] = CMeta->getType();
175static SPIRV::AccessQualifier::AccessQualifier
178 return SPIRV::AccessQualifier::ReadWrite;
182 return SPIRV::AccessQualifier::ReadWrite;
184 if (ArgAttribute->
getString() ==
"read_only")
185 return SPIRV::AccessQualifier::ReadOnly;
186 if (ArgAttribute->
getString() ==
"write_only")
187 return SPIRV::AccessQualifier::WriteOnly;
188 return SPIRV::AccessQualifier::ReadWrite;
191static std::vector<SPIRV::Decoration::Decoration>
194 if (ArgAttribute && ArgAttribute->
getString() ==
"volatile")
195 return {SPIRV::Decoration::Volatile};
204 SPIRV::AccessQualifier::AccessQualifier ArgAccessQual =
240 if (
II &&
II->getIntrinsicID() == Intrinsic::spv_assign_type) {
244 assert(BuiltinType->isTargetExtTy() &&
"Expected TargetExtType");
250 if (!
II ||
II->getIntrinsicID() != Intrinsic::spv_assign_ptr_type)
257 ElementTy, MIRBuilder,
265 ArgAccessQual,
true);
268static SPIRV::ExecutionModel::ExecutionModel
271 return SPIRV::ExecutionModel::Kernel;
274 auto attribute =
F.getFnAttribute(
"hlsl.shader");
275 if (!attribute.isValid()) {
277 "This entry point lacks mandatory hlsl.shader attribute.");
280 const auto value = attribute.getValueAsString();
281 if (value ==
"compute")
282 return SPIRV::ExecutionModel::GLCompute;
283 if (value ==
"vertex")
284 return SPIRV::ExecutionModel::Vertex;
285 if (value ==
"pixel")
286 return SPIRV::ExecutionModel::Fragment;
289 "This HLSL entry point is not supported by this backend.");
299 auto attribute =
F.getFnAttribute(
"hlsl.shader");
300 if (!attribute.isValid()) {
302 return SPIRV::ExecutionModel::Kernel;
306 const auto value = attribute.getValueAsString();
307 if (value ==
"compute")
308 return SPIRV::ExecutionModel::GLCompute;
309 if (value ==
"vertex")
310 return SPIRV::ExecutionModel::Vertex;
311 if (value ==
"pixel")
312 return SPIRV::ExecutionModel::Fragment;
325 assert(GR &&
"Must initialize the SPIRV type registry before lowering args.");
326 GR->setCurrentFunc(MIRBuilder.
getMF());
334 if (VRegs.size() > 0) {
336 for (
const auto &Arg :
F.args()) {
339 if (VRegs[i].
size() > 1)
342 GR->assignSPIRVTypeToVReg(SpirvTy, VRegs[i][0], MIRBuilder.
getMF());
346 buildOpName(VRegs[i][0], Arg.getName(), MIRBuilder);
348 auto DerefBytes =
static_cast<unsigned>(Arg.getDereferenceableBytes());
351 SPIRV::Decoration::MaxByteOffset, {DerefBytes});
353 if (Arg.hasAttribute(Attribute::Alignment) && !ST->isShader()) {
354 auto Alignment =
static_cast<unsigned>(
355 Arg.getAttribute(Attribute::Alignment).getValueAsInt());
359 if (Arg.hasAttribute(Attribute::ReadOnly)) {
361 static_cast<unsigned>(SPIRV::FunctionParameterAttribute::NoWrite);
363 SPIRV::Decoration::FuncParamAttr, {Attr});
365 if (Arg.hasAttribute(Attribute::ZExt)) {
367 static_cast<unsigned>(SPIRV::FunctionParameterAttribute::Zext);
369 SPIRV::Decoration::FuncParamAttr, {Attr});
371 if (Arg.hasAttribute(Attribute::NoAlias)) {
373 static_cast<unsigned>(SPIRV::FunctionParameterAttribute::NoAlias);
375 SPIRV::Decoration::FuncParamAttr, {Attr});
377 if (Arg.hasAttribute(Attribute::ByVal)) {
379 static_cast<unsigned>(SPIRV::FunctionParameterAttribute::ByVal);
381 SPIRV::Decoration::FuncParamAttr, {Attr});
383 if (Arg.hasAttribute(Attribute::StructRet)) {
385 static_cast<unsigned>(SPIRV::FunctionParameterAttribute::Sret);
387 SPIRV::Decoration::FuncParamAttr, {Attr});
391 std::vector<SPIRV::Decoration::Decoration> ArgTypeQualDecs =
393 for (SPIRV::Decoration::Decoration Decoration : ArgTypeQualDecs)
397 MDNode *
Node =
F.getMetadata(
"spirv.ParameterDecorations");
398 if (
Node && i < Node->getNumOperands() &&
403 assert(MD2 &&
"Metadata operand is expected");
405 assert(Const &&
"MDOperand should be ConstantInt");
407 static_cast<SPIRV::Decoration::Decoration
>(Const->getZExtValue());
408 std::vector<uint32_t> DecVec;
411 assert(Const &&
"MDOperand should be ConstantInt");
412 DecVec.push_back(
static_cast<uint32_t>(Const->getZExtValue()));
423 MRI->setRegClass(FuncVReg, &SPIRV::iIDRegClass);
427 if (
Type *FRetElemTy = GR->findDeducedElementType(&
F)) {
430 GR->addReturnType(&
F, DerivedTy);
434 SPIRVType *RetTy = GR->getOrCreateSPIRVType(
435 FRetTy, MIRBuilder, SPIRV::AccessQualifier::ReadWrite,
true);
437 SPIRVType *FuncTy = GR->getOrCreateOpTypeFunctionWithArgs(
438 FTy, RetTy, ArgTypeVRegs, MIRBuilder);
444 .
addUse(GR->getSPIRVTypeID(RetTy))
446 .
addUse(GR->getSPIRVTypeID(FuncTy));
448 GR->addGlobalObject(&
F, &MIRBuilder.
getMF(), FuncVReg);
449 if (
F.isDeclaration())
454 for (
const auto &Arg :
F.args()) {
455 assert(VRegs[i].
size() == 1 &&
"Formal arg has multiple vregs");
457 MRI->setRegClass(ArgReg, GR->getRegClass(ArgTypeVRegs[i]));
458 MRI->setType(ArgReg, GR->getRegType(ArgTypeVRegs[i]));
459 auto MIB = MIRBuilder.
buildInstr(SPIRV::OpFunctionParameter)
461 .
addUse(GR->getSPIRVTypeID(ArgTypeVRegs[i]));
462 if (
F.isDeclaration())
464 GR->addGlobalObject(&Arg, &MIRBuilder.
getMF(), ArgReg);
478 auto MIB = MIRBuilder.
buildInstr(SPIRV::OpEntryPoint)
485 SPIRV::LinkageType::LinkageType LnkTy =
487 ? SPIRV::LinkageType::Import
490 SPIRV::Extension::SPV_KHR_linkonce_odr)
491 ? SPIRV::LinkageType::LinkOnceODR
492 : SPIRV::LinkageType::Export);
493 buildOpDecorate(FuncVReg, MIRBuilder, SPIRV::Decoration::LinkageAttributes,
494 {
static_cast<uint32_t>(LnkTy)},
F.getName());
498 bool hasFunctionPointers =
499 ST->canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers);
500 if (hasFunctionPointers) {
501 if (
F.hasFnAttribute(
"referenced-indirectly")) {
503 "Unexpected 'referenced-indirectly' attribute of the kernel "
506 SPIRV::Decoration::ReferencedIndirectlyINTEL, {});
519void SPIRVCallLowering::produceIndirectPtrTypes(
523 for (
auto const &IC : IndirectCalls) {
525 IC.RetTy, MIRBuilder, SPIRV::AccessQualifier::ReadWrite,
true);
527 for (
size_t i = 0; i < IC.ArgTys.size(); ++i) {
529 IC.ArgTys[i], MIRBuilder, SPIRV::AccessQualifier::ReadWrite,
true);
538 FTy, SpirvRetTy, SpirvArgTypes, MIRBuilder);
541 SpirvFuncTy, MIRBuilder, SPIRV::StorageClass::Function);
551 if (Info.OrigRet.Regs.size() > 1)
554 GR->setCurrentFunc(MF);
556 std::string DemangledName;
557 const Type *OrigRetTy = Info.OrigRet.Ty;
562 if (Info.Callee.isGlobal()) {
563 std::string FuncName = Info.Callee.getGlobal()->getName().str();
570 OrigRetTy = FTy->getReturnType();
572 if (
auto *DerivedRetTy = GR->findReturnType(CF))
573 OrigRetTy = DerivedRetTy;
580 Info.OrigRet.Regs.empty() ?
Register(0) : Info.OrigRet.Regs[0];
584 if (isFunctionDecl && !DemangledName.empty()) {
586 if (!GR->getSPIRVTypeForVReg(ResVReg)) {
587 const Type *RetTy = OrigRetTy;
589 const Value *OrigValue = Info.OrigRet.OrigValue;
593 if (
Type *ElemTy = GR->findDeducedElementType(OrigValue))
598 SPIRV::AccessQualifier::ReadWrite,
true);
602 SPIRV::AccessQualifier::ReadWrite,
true);
605 for (
auto Arg : Info.OrigArgs) {
606 assert(Arg.Regs.size() == 1 &&
"Call arg has multiple VRegs");
609 SPIRVType *SpvType = GR->getSPIRVTypeForVReg(ArgReg);
611 Type *ArgTy =
nullptr;
619 if (
Type *ElemTy = GR->findDeducedElementType(Arg.OrigValue))
626 SpvType = GR->getOrCreateSPIRVType(
627 ArgTy, MIRBuilder, SPIRV::AccessQualifier::ReadWrite,
true);
628 GR->assignSPIRVTypeToVReg(SpvType, ArgReg, MF);
631 if (!
MRI->getRegClassOrNull(ArgReg)) {
635 MRI->setRegClass(ArgReg, SpvType ? GR->getRegClass(SpvType)
636 : &SPIRV::pIDRegClass);
639 SpvType ? GR->getRegType(SpvType)
641 GR->getPointerSize()));
646 MIRBuilder, ResVReg, OrigRetTy, ArgVRegs, GR))
650 if (isFunctionDecl && !GR->find(CF, &MF).isValid()) {
654 FirstBlockBuilder.
setMF(MF);
663 MRI->setRegClass(Reg, &SPIRV::iIDRegClass);
673 if (MIRBuilder.
getMF()
683 if (Info.CB->isIndirectCall()) {
684 if (!ST->canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers))
686 "extensions does not support it",
689 CallOp = SPIRV::OpFunctionPointerCallINTEL;
692 Register CalleeReg = Info.Callee.getReg();
697 for (
const auto &Arg : Info.OrigArgs) {
698 assert(Arg.Regs.size() == 1 &&
"Call arg has multiple VRegs");
706 CallOp = SPIRV::OpFunctionCall;
712 SPIRVType *RetType = GR->assignTypeToVReg(
713 OrigRetTy, ResVReg, MIRBuilder, SPIRV::AccessQualifier::ReadWrite,
true);
718 .
addUse(GR->getSPIRVTypeID(RetType))
721 for (
const auto &Arg : Info.OrigArgs) {
723 if (Arg.Regs.size() > 1)
725 MIB.addUse(Arg.Regs[0]);
728 if (ST->canUseExtension(SPIRV::Extension::SPV_INTEL_memory_access_aliasing)) {
733 GR->buildMemAliasingOpDecorate(ResVReg, MIRBuilder,
734 SPIRV::Decoration::AliasScopeINTEL, MD);
736 GR->buildMemAliasingOpDecorate(ResVReg, MIRBuilder,
737 SPIRV::Decoration::NoAliasINTEL, MD);
742 *ST->getRegBankInfo());
unsigned const MachineRegisterInfo * MRI
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Promote Memory to Register
uint64_t IntrinsicInst * II
static ConstantInt * getConstInt(MDNode *MD, unsigned NumOp)
static SPIRV::ExecutionModel::ExecutionModel getExecutionModel(const SPIRVSubtarget &STI, const Function &F)
static FunctionType * getOriginalFunctionType(const Function &F)
static uint32_t getFunctionControl(const Function &F, const SPIRVSubtarget *ST)
static SPIRV::AccessQualifier::AccessQualifier getArgAccessQual(const Function &F, unsigned ArgIdx)
static SPIRVType * getArgSPIRVType(const Function &F, unsigned ArgIdx, SPIRVGlobalRegistry *GR, MachineIRBuilder &MIRBuilder, const SPIRVSubtarget &ST)
static FunctionType * fixFunctionTypeIfPtrArgs(SPIRVGlobalRegistry *GR, const Function &F, FunctionType *FTy, const SPIRVType *SRetTy, const SmallVector< SPIRVType *, 4 > &SArgTys)
static std::vector< SPIRV::Decoration::Decoration > getKernelArgTypeQual(const Function &F, unsigned ArgIdx)
#define SPIRV_BACKEND_SERVICE_FUN_NAME
This class represents an incoming formal argument to a Function.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
size_t size() const
size - Get the array size.
bool isValid() const
Return true if the attribute is any kind of attribute.
Base class for all callable instructions (InvokeInst and CallInst) Holds everything related to callin...
CallLowering(const TargetLowering *TLI)
This is the shared class of boolean and integer constants.
TypeSize getTypeStoreSize(Type *Ty) const
Returns the maximum number of bytes that may be overwritten by storing the specified type.
FunctionLoweringInfo - This contains information that is global to a function that is used when lower...
Class to represent function types.
Type * getParamType(unsigned i) const
Parameter type accessors.
Type * getReturnType() const
static LLVM_ABI FunctionType * get(Type *Result, ArrayRef< Type * > Params, bool isVarArg)
This static method is the primary way of constructing a FunctionType.
iterator_range< arg_iterator > args()
Attribute getFnAttribute(Attribute::AttrKind Kind) const
Return the attribute for the given attribute kind.
LLVM_ABI bool isDeclaration() const
Return true if the primary definition of this global value is outside of the current translation unit...
@ HiddenVisibility
The GV is hidden.
@ PrivateLinkage
Like Internal, but omit from symbol table.
@ InternalLinkage
Rename collisions when linking (static functions).
@ LinkOnceODRLinkage
Same, but only replaced by something equivalent.
bool hasMetadata() const
Return true if this instruction has any metadata attached to it.
MDNode * getMetadata(unsigned KindID) const
Get the metadata of given kind attached to this Instruction.
static constexpr LLT scalar(unsigned SizeInBits)
Get a low-level scalar or aggregate "bag of bits".
static constexpr LLT pointer(unsigned AddressSpace, unsigned SizeInBits)
Get a low-level pointer in the given address space.
const MDOperand & getOperand(unsigned I) const
ArrayRef< MDOperand > operands() const
unsigned getNumOperands() const
Return number of MDNode operands.
Tracking metadata reference owned by Metadata.
LLVM_ABI StringRef getString() const
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MachineBasicBlock * getBlockNumbered(unsigned N) const
getBlockNumbered - MachineBasicBlocks are automatically numbered when they are inserted into the mach...
Function & getFunction()
Return the LLVM function that this machine code represents.
Helper class to build MachineInstr.
const TargetInstrInfo & getTII()
MachineInstrBuilder buildInstr(unsigned Opcode)
Build and insert <empty> = Opcode <empty>.
MachineFunction & getMF()
Getter for the function we currently build.
void setMBB(MachineBasicBlock &MBB)
Set the insertion point to the end of MBB.
MachineInstrBuilder buildTrap(bool Debug=false)
Build and insert G_TRAP or G_DEBUGTRAP.
MachineRegisterInfo * getMRI()
Getter for MRI.
const DataLayout & getDataLayout() const
void setMF(MachineFunction &MF)
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & add(const MachineOperand &MO) const
bool constrainAllUses(const TargetInstrInfo &TII, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI) const
const MachineInstrBuilder & addUse(Register RegNo, unsigned Flags=0, unsigned SubReg=0) const
Add a virtual register use operand.
MachineInstr * getInstr() const
If conversion operators fail, use this method to get the MachineInstr explicitly.
const MachineInstrBuilder & addDef(Register RegNo, unsigned Flags=0, unsigned SubReg=0) const
Add a virtual register definition operand.
const MachineOperand & getOperand(unsigned i) const
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
LLVM_ABI Register createVirtualRegister(const TargetRegisterClass *RegClass, StringRef Name="")
createVirtualRegister - Create and return a new virtual register in the function with the specified r...
bool doesNotAccessMemory() const
Whether this function accesses no memory.
bool onlyReadsMemory() const
Whether this function only (at most) reads memory.
Wrapper class representing virtual and physical registers.
constexpr bool isValid() const
bool lowerCall(MachineIRBuilder &MIRBuilder, CallLoweringInfo &Info) const override
This hook must be implemented to lower the given call instruction, including argument and return valu...
bool lowerReturn(MachineIRBuilder &MIRBuiler, const Value *Val, ArrayRef< Register > VRegs, FunctionLoweringInfo &FLI, Register SwiftErrorVReg) const override
This hook must be implemented to lower outgoing return values, described by Val, into the specified v...
SPIRVCallLowering(const SPIRVTargetLowering &TLI, SPIRVGlobalRegistry *GR)
bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F, ArrayRef< ArrayRef< Register > > VRegs, FunctionLoweringInfo &FLI) const override
This hook must be implemented to lower the incoming (formal) arguments, described by VRegs,...
SPIRVType * getSPIRVTypeForVReg(Register VReg, const MachineFunction *MF=nullptr) const
void assignSPIRVTypeToVReg(SPIRVType *Type, Register VReg, const MachineFunction &MF)
const Type * getTypeForSPIRVType(const SPIRVType *Ty) const
SPIRVType * getOrCreateSPIRVType(const Type *Type, MachineInstr &I, SPIRV::AccessQualifier::AccessQualifier AQ, bool EmitIR)
SPIRVType * getOrCreateSPIRVPointerType(const Type *BaseType, MachineIRBuilder &MIRBuilder, SPIRV::StorageClass::StorageClass SC)
SPIRVType * getOrCreateOpTypeFunctionWithArgs(const Type *Ty, SPIRVType *RetType, const SmallVectorImpl< SPIRVType * > &ArgTypes, MachineIRBuilder &MIRBuilder)
SPIRVEnvType getEnv() const
void setEnv(SPIRVEnvType E)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
The instances of the Type class are immutable: once they are created, they are never changed.
bool isPointerTy() const
True if this is an instance of PointerType.
A few GPU targets, such as DXIL and SPIR-V, have typed pointers.
static LLVM_ABI TypedPointerType * get(Type *ElementType, unsigned AddressSpace)
This constructs a pointer to an object of the specified type in a numbered address space.
LLVM Value Representation.
Type * getType() const
All values are typed, get the type of this value.
iterator_range< user_iterator > users()
constexpr bool isZero() const
@ SPIR_KERNEL
Used for SPIR kernel functions.
std::optional< bool > lowerBuiltin(const StringRef DemangledCall, SPIRV::InstructionSet::InstructionSet Set, MachineIRBuilder &MIRBuilder, const Register OrigRet, const Type *OrigRetTy, const SmallVectorImpl< Register > &Args, SPIRVGlobalRegistry *GR)
This is an optimization pass for GlobalISel generic memory operations.
void buildOpName(Register Target, const StringRef &Name, MachineIRBuilder &MIRBuilder)
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
unsigned getPointerAddressSpace(const Type *T)
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
MemoryEffectsBase< IRMemLocation > MemoryEffects
Summary of how a function affects memory in the program.
MDString * getOCLKernelArgAccessQual(const Function &F, unsigned ArgIdx)
std::string getOclOrSpirvBuiltinDemangledName(StringRef Name)
bool isTypedPointerTy(const Type *T)
auto dyn_cast_or_null(const Y &Val)
void buildOpDecorate(Register Reg, MachineIRBuilder &MIRBuilder, SPIRV::Decoration::Decoration Dec, const std::vector< uint32_t > &DecArgs, StringRef StrImm)
Register createVirtualRegister(SPIRVType *SpvType, SPIRVGlobalRegistry *GR, MachineRegisterInfo *MRI, const MachineFunction &MF)
Type * toTypedPointer(Type *Ty)
void setRegClassType(Register Reg, SPIRVType *SpvType, SPIRVGlobalRegistry *GR, MachineRegisterInfo *MRI, const MachineFunction &MF, bool Force)
bool isPointerTy(const Type *T)
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
const MachineInstr SPIRVType
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
bool isEntryPoint(const Function &F)
SPIRV::StorageClass::StorageClass addressSpaceToStorageClass(unsigned AddrSpace, const SPIRVSubtarget &STI)
MDString * getOCLKernelArgTypeQual(const Function &F, unsigned ArgIdx)
Type * getPointeeTypeByAttr(Argument *Arg)
bool hasPointeeTypeAttr(Argument *Arg)
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
bool isPointerTyOrWrapper(const Type *Ty)
void addStringImm(const StringRef &Str, MCInst &Inst)
bool isUntypedPointerTy(const Type *T)