LLVM: lib/CodeGen/Analysis.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

27

28using namespace llvm;

29

30

31

32

34 const unsigned *Indices,

35 const unsigned *IndicesEnd,

36 unsigned CurIndex) {

37

38 if (Indices && Indices == IndicesEnd)

39 return CurIndex;

40

41

42 if (StructType *STy = dyn_cast(Ty)) {

44 Type *ET = I.value();

45 if (Indices && *Indices == I.index())

48 }

49 assert(!Indices && "Unexpected out of bound");

50 return CurIndex;

51 }

52

53 else if (ArrayType *ATy = dyn_cast(Ty)) {

54 Type *EltTy = ATy->getElementType();

55 unsigned NumElts = ATy->getNumElements();

56

57 unsigned EltLinearOffset = ComputeLinearIndex(EltTy, nullptr, nullptr, 0);

58 if (Indices) {

59 assert(*Indices < NumElts && "Unexpected out of bound");

60

61

62 CurIndex += EltLinearOffset* *Indices;

64 }

65 CurIndex += EltLinearOffset*NumElts;

66 return CurIndex;

67 }

68

69 return CurIndex + 1;

70}

71

72

73

74

75

76

77

78

85 StartingOffset.isZero()) &&

86 "Offset/TypeSize mismatch!");

87

88 if (StructType *STy = dyn_cast(Ty)) {

89

90

91

92 const StructLayout *SL = Offsets ? DL.getStructLayout(STy) : nullptr;

94 EI = EB,

95 EE = STy->element_end();

96 EI != EE; ++EI) {

97

101 StartingOffset + EltOffset);

102 }

103 return;

104 }

105

106 if (ArrayType *ATy = dyn_cast(Ty)) {

107 Type *EltTy = ATy->getElementType();

108 TypeSize EltSize = DL.getTypeAllocSize(EltTy);

109 for (unsigned i = 0, e = ATy->getNumElements(); i != e; ++i)

111 StartingOffset + i * EltSize);

112 return;

113 }

114

116 return;

117

119 if (MemVTs)

121 if (Offsets)

122 Offsets->push_back(StartingOffset);

123}

124

131 if (FixedOffsets) {

136 } else {

138 }

139}

140

145

146 if (StructType *STy = dyn_cast(&Ty)) {

147

148

149

150 const StructLayout *SL = Offsets ? DL.getStructLayout(STy) : nullptr;

151 for (unsigned I = 0, E = STy->getNumElements(); I != E; ++I) {

154 StartingOffset + EltOffset);

155 }

156 return;

157 }

158

159 if (ArrayType *ATy = dyn_cast(&Ty)) {

160 Type *EltTy = ATy->getElementType();

161 uint64_t EltSize = DL.getTypeAllocSize(EltTy).getFixedValue();

162 for (unsigned i = 0, e = ATy->getNumElements(); i != e; ++i)

164 StartingOffset + i * EltSize);

165 return;

166 }

167

169 return;

170

172 if (Offsets != nullptr)

173 Offsets->push_back(StartingOffset * 8);

174}

175

176

178 V = V->stripPointerCasts();

179 GlobalValue *GV = dyn_cast(V);

181

182 if (Var && Var->getName() == "llvm.eh.catch.all.value") {

184 "The EH catch-all value must have an initializer");

186 GV = dyn_cast(Init);

187 if (!GV) V = cast(Init);

188 }

189

190 assert((GV || isa(V)) &&

191 "TypeInfo must be a global variable or NULL");

192 return GV;

193}

194

195

196

197

198

200 switch (Pred) {

202 case FCmpInst::FCMP_OEQ: return ISD::SETOEQ;

203 case FCmpInst::FCMP_OGT: return ISD::SETOGT;

204 case FCmpInst::FCMP_OGE: return ISD::SETOGE;

205 case FCmpInst::FCMP_OLT: return ISD::SETOLT;

206 case FCmpInst::FCMP_OLE: return ISD::SETOLE;

207 case FCmpInst::FCMP_ONE: return ISD::SETONE;

208 case FCmpInst::FCMP_ORD: return ISD::SETO;

209 case FCmpInst::FCMP_UNO: return ISD::SETUO;

210 case FCmpInst::FCMP_UEQ: return ISD::SETUEQ;

211 case FCmpInst::FCMP_UGT: return ISD::SETUGT;

212 case FCmpInst::FCMP_UGE: return ISD::SETUGE;

213 case FCmpInst::FCMP_ULT: return ISD::SETULT;

214 case FCmpInst::FCMP_ULE: return ISD::SETULE;

215 case FCmpInst::FCMP_UNE: return ISD::SETUNE;

216 case FCmpInst::FCMP_TRUE: return ISD::SETTRUE;

218 }

219}

220

222 switch (CC) {

229 default: return CC;

230 }

231}

232

234 switch (Pred) {

235 case ICmpInst::ICMP_EQ: return ISD::SETEQ;

236 case ICmpInst::ICMP_NE: return ISD::SETNE;

237 case ICmpInst::ICMP_SLE: return ISD::SETLE;

238 case ICmpInst::ICMP_ULE: return ISD::SETULE;

239 case ICmpInst::ICMP_SGE: return ISD::SETGE;

240 case ICmpInst::ICMP_UGE: return ISD::SETUGE;

241 case ICmpInst::ICMP_SLT: return ISD::SETLT;

242 case ICmpInst::ICMP_ULT: return ISD::SETULT;

243 case ICmpInst::ICMP_SGT: return ISD::SETGT;

244 case ICmpInst::ICMP_UGT: return ISD::SETUGT;

245 default:

247 }

248}

249

251 switch (Pred) {

253 return ICmpInst::ICMP_EQ;

255 return ICmpInst::ICMP_NE;

257 return ICmpInst::ICMP_SLE;

259 return ICmpInst::ICMP_ULE;

261 return ICmpInst::ICMP_SGE;

263 return ICmpInst::ICMP_UGE;

265 return ICmpInst::ICMP_SLT;

267 return ICmpInst::ICMP_ULT;

269 return ICmpInst::ICMP_SGT;

271 return ICmpInst::ICMP_UGT;

272 default:

274 }

275}

276

279 return T1 == T2 || (T1->isPointerTy() && T2->isPointerTy()) ||

280 (isa(T1) && isa(T2) &&

282}

283

284

285

286

287

288

289

290

291

292

293

294

297 unsigned &DataBits,

300 while (true) {

301

302

303 const Instruction *I = dyn_cast(V);

304 if (I || I->getNumOperands() == 0) return V;

305 const Value *NoopInput = nullptr;

306

307 Value *Op = I->getOperand(0);

308 if (isa(I)) {

309

311 NoopInput = Op;

312 } else if (isa(I)) {

313

314 if (cast(I)->hasAllZeroIndices())

315 NoopInput = Op;

316 } else if (isa(I)) {

317

318

319

320 if (!isa(I->getType()) &&

321 DL.getPointerSizeInBits() ==

322 cast(Op->getType())->getBitWidth())

323 NoopInput = Op;

324 } else if (isa(I)) {

325

326

327

328 if (!isa(I->getType()) &&

329 DL.getPointerSizeInBits() ==

330 cast(I->getType())->getBitWidth())

331 NoopInput = Op;

332 } else if (isa(I) &&

334 DataBits =

336 I->getType()->getPrimitiveSizeInBits().getFixedValue());

337 NoopInput = Op;

338 } else if (auto *CB = dyn_cast(I)) {

339 const Value *ReturnedOp = CB->getReturnedArgOperand();

341 NoopInput = ReturnedOp;

342 } else if (const InsertValueInst *IVI = dyn_cast(V)) {

343

345 if (ValLoc.size() >= InsertLoc.size() &&

346 std::equal(InsertLoc.begin(), InsertLoc.end(), ValLoc.rbegin())) {

347

348

349

351 NoopInput = IVI->getInsertedValueOperand();

352 } else {

353

354

355 NoopInput = Op;

356 }

357 } else if (const ExtractValueInst *EVI = dyn_cast(V)) {

358

359

360

363 NoopInput = Op;

364 }

365

366 if (!NoopInput)

367 return V;

368

369 V = NoopInput;

370 }

371}

372

373

374

375

376

380 bool AllowDifferingSizes,

383

384

385

386

387

388 unsigned BitsRequired = UINT_MAX;

389 RetVal = getNoopInput(RetVal, RetIndices, BitsRequired, TLI, DL);

390

391

392

393 if (isa(RetVal))

394 return true;

395

396

397

398

399

400 unsigned BitsProvided = UINT_MAX;

401 CallVal = getNoopInput(CallVal, CallIndices, BitsProvided, TLI, DL);

402

403

404

405 if (CallVal != RetVal || CallIndices != RetIndices)

406 return false;

407

408

409

410

411

412 if (BitsProvided < BitsRequired ||

413 (!AllowDifferingSizes && BitsProvided != BitsRequired))

414 return false;

415

416 return true;

417}

418

419

420

422 if (ArrayType *AT = dyn_cast(T))

423 return Idx < AT->getNumElements();

424

425 return Idx < cast(T)->getNumElements();

426}

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

447

448

449 while (!Path.empty() && indexReallyValid(SubTypes.back(), Path.back() + 1)) {

450 Path.pop_back();

452 }

453

454

455 if (Path.empty())

456 return false;

457

458

459

460 ++Path.back();

461 Type *DeeperType =

465 return true;

466

468 Path.push_back(0);

469

471 }

472

473 return true;

474}

475

476

477

478

479

480

481

482

483

484

485

488

489

490

493 Path.push_back(0);

494 Next = FirstInner;

495 }

496

497

498

499 if (Path.empty())

500 return true;

501

502

503

507 return false;

508 }

509

510 return true;

511}

512

513

514

517 do {

519 return false;

520

521 assert(!Path.empty() && "found a leaf but didn't set the path?");

524

525 return true;

526}

527

528

529

530

531

532

533

534

536 bool ReturnsFirstArg) {

537 const BasicBlock *ExitBB = Call.getParent();

539 const ReturnInst *Ret = dyn_cast(Term);

540

541

542

543

544

545

546

547

548

549 if (!Ret && ((!TM.Options.GuaranteedTailCallOpt &&

552 !isa(Term)))

553 return false;

554

555

556

557

559 if (&*BBI == &Call)

560 break;

561

562

563 if (BBI->isDebugOrPseudoInst())

564 continue;

565

566

567 if (const IntrinsicInst *II = dyn_cast(BBI))

568 if (II->getIntrinsicID() == Intrinsic::lifetime_end ||

569 II->getIntrinsicID() == Intrinsic::assume ||

570 II->getIntrinsicID() == Intrinsic::experimental_noalias_scope_decl ||

571 II->getIntrinsicID() == Intrinsic::fake_use)

572 continue;

573 if (BBI->mayHaveSideEffects() || BBI->mayReadFromMemory() ||

575 return false;

576 }

577

580 F, &Call, Ret, *TM.getSubtargetImpl(*F)->getTargetLowering(),

581 ReturnsFirstArg);

582}

583

587 bool *AllowDifferingSizes) {

588

589 bool DummyADS;

590 bool &ADS = AllowDifferingSizes ? *AllowDifferingSizes : DummyADS;

591 ADS = true;

592

593 AttrBuilder CallerAttrs(F->getContext(), F->getAttributes().getRetAttrs());

595 cast(I)->getAttributes().getRetAttrs());

596

597

598

599 for (const auto &Attr : {Attribute::Alignment, Attribute::Dereferenceable,

600 Attribute::DereferenceableOrNull, Attribute::NoAlias,

601 Attribute::NonNull, Attribute::NoUndef,

602 Attribute::Range, Attribute::NoFPClass}) {

605 }

606

607 if (CallerAttrs.contains(Attribute::ZExt)) {

608 if (!CalleeAttrs.contains(Attribute::ZExt))

609 return false;

610

611 ADS = false;

614 } else if (CallerAttrs.contains(Attribute::SExt)) {

615 if (!CalleeAttrs.contains(Attribute::SExt))

616 return false;

617

618 ADS = false;

621 }

622

623

624

625

626

627

628

629

630

631

632

633 if (I->use_empty()) {

636 }

637

638

639

640

641 return CallerAttrs == CalleeAttrs;

642}

643

648 bool ReturnsFirstArg) {

649

650

651 if (!Ret || Ret->getNumOperands() == 0) return true;

652

653

654

655 if (isa(Ret->getOperand(0))) return true;

656

657

658 bool AllowDifferingSizes;

660 return false;

661

662

663 if (ReturnsFirstArg)

664 return true;

665

666 const Value *RetVal = Ret->getOperand(0), *CallVal = I;

669

671 bool CallEmpty = firstRealType(CallVal->getType(), CallSubTypes, CallPath);

672

673

674

675 if (RetEmpty)

676 return true;

677

678

679

680

681

682

683

684

685

686 do {

687 if (CallEmpty) {

688

689

690

691 Type *SlotType =

694 }

695

696

697

698

701

702

703

705 AllowDifferingSizes, TLI,

706 F->getDataLayout()))

707 return false;

708

709 CallEmpty = nextRealType(CallSubTypes, CallPath);

711

712 return true;

713}

714

716 const ReturnInst *Ret = dyn_cast(CI.getParent()->getTerminator());

717 Value *RetVal = Ret ? Ret->getReturnValue() : nullptr;

718 bool ReturnsFirstArg = false;

720 ReturnsFirstArg = true;

721 return ReturnsFirstArg;

722}

723

728 while (!Worklist.empty()) {

730

731 if (Visiting->isEHPad() && Visiting != MBB)

732 continue;

733

734

735 auto P = EHScopeMembership.insert(std::make_pair(Visiting, EHScope));

736

737

738 if (P.second) {

739 assert(P.first->second == EHScope && "MBB is part of two scopes!");

740 continue;

741 }

742

743

744

746 continue;

747

749 }

750}

751

755

756

758 return EHScopeMembership;

759

763

776 }

777

779

780

781

782 if (MBBI == MBB.end() || MBBI->getOpcode() != TII->getCatchReturnOpcode())

783 continue;

784

785

786

791 }

792

793

794 if (EHScopeBlocks.empty())

795 return EHScopeMembership;

796

797

799

802

805

808

809 for (std::pair<const MachineBasicBlock *, int> CatchRetPair :

810 CatchRetSuccessors)

812 CatchRetPair.first);

813 return EHScopeMembership;

814}

MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL

MachineBasicBlock MachineBasicBlock::iterator MBBI

static bool isNoopBitcast(Type *T1, Type *T2, const TargetLoweringBase &TLI)

static bool firstRealType(Type *Next, SmallVectorImpl< Type * > &SubTypes, SmallVectorImpl< unsigned > &Path)

Find the first non-empty, scalar-like type in Next and setup the iterator components.

static bool slotOnlyDiscardsData(const Value *RetVal, const Value *CallVal, SmallVectorImpl< unsigned > &RetIndices, SmallVectorImpl< unsigned > &CallIndices, bool AllowDifferingSizes, const TargetLoweringBase &TLI, const DataLayout &DL)

Return true if this scalar return value only has bits discarded on its path from the "tail call" to t...

static void collectEHScopeMembers(DenseMap< const MachineBasicBlock *, int > &EHScopeMembership, int EHScope, const MachineBasicBlock *MBB)

static bool indexReallyValid(Type *T, unsigned Idx)

For an aggregate type, determine whether a given index is within bounds or not.

static bool nextRealType(SmallVectorImpl< Type * > &SubTypes, SmallVectorImpl< unsigned > &Path)

Set the iterator data-structures to the next non-empty, non-aggregate subtype.

static bool advanceToNextLeafType(SmallVectorImpl< Type * > &SubTypes, SmallVectorImpl< unsigned > &Path)

Move the given iterators to the next leaf type in depth first traversal.

static const Value * getNoopInput(const Value *V, SmallVectorImpl< unsigned > &ValLoc, unsigned &DataBits, const TargetLoweringBase &TLI, const DataLayout &DL)

Look through operations that will be free to find the earliest source of this value.

Returns the sub type a function will return at a given Idx Should correspond to the result type of an ExtractValue instruction executed with just that one unsigned Idx

const HexagonInstrInfo * TII

Module.h This file contains the declarations for the Module class.

uint64_t IntrinsicInst * II

assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())

This file describes how to lower LLVM code to machine code.

ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...

reverse_iterator rend() const

size_t size() const

size - Get the array size.

reverse_iterator rbegin() const

Class to represent array types.

bool contains(Attribute::AttrKind A) const

Return true if the builder has the specified attribute.

AttrBuilder & removeAttribute(Attribute::AttrKind Val)

Remove an attribute from the builder.

LLVM Basic Block Representation.

InstListType::const_iterator const_iterator

const Function * getParent() const

Return the enclosing method, or null if none.

const Instruction * getTerminator() const LLVM_READONLY

Returns the terminator instruction if the block is well formed or null if the block is not well forme...

Base class for all callable instructions (InvokeInst and CallInst) Holds everything related to callin...

Value * getArgOperand(unsigned i) const

This class represents a function call, abstracting a target machine's calling convention.

Predicate

This enumeration lists the possible predicates for CmpInst subclasses.

This class represents an Operation in the Expression.

A parsed version of the target data layout string in and methods for querying it.

std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)

Constant * getPersonalityFn() const

Get the personality function associated with this function.

const Constant * getInitializer() const

getInitializer - Return the initializer for this global variable.

bool hasInitializer() const

Definitions have initializers, declarations don't.

This instruction inserts a struct field of array element value into an aggregate value.

A wrapper class for inspecting calls to intrinsic functions.

bool isEHPad() const

Returns true if the block is a landing pad.

int getNumber() const

MachineBasicBlocks are uniquely numbered at the function level, unless they're not in a MachineFuncti...

iterator getFirstTerminator()

Returns an iterator to the first terminator instruction of this basic block.

bool isEHScopeEntry() const

Returns true if this is the entry block of an EH scope, i.e., the block that used to have a catchpad ...

iterator_range< succ_iterator > successors()

bool isEHScopeReturnBlock() const

Convenience function that returns true if the bock ends in a EH scope return instruction.

const TargetSubtargetInfo & getSubtarget() const

getSubtarget - Return the subtarget for which this machine code is being compiled.

Function & getFunction()

Return the LLVM function that this machine code represents.

const MachineBasicBlock & front() const

Return a value (possibly void), from a function.

This class consists of common code factored out of the SmallVector class to reduce code duplication b...

void append(ItTy in_start, ItTy in_end)

Add the specified range to the end of the SmallVector.

void push_back(const T &Elt)

reverse_iterator rbegin()

This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.

Used to lazily calculate structure layout information for a target machine, based on the DataLayout s...

TypeSize getElementOffset(unsigned Idx) const

Class to represent struct types.

Type::subtype_iterator element_iterator

TargetInstrInfo - Interface to description of machine instruction set.

This base class for TargetLowering contains the SelectionDAG-independent parts that can be used from ...

EVT getMemValueType(const DataLayout &DL, Type *Ty, bool AllowUnknown=false) const

EVT getValueType(const DataLayout &DL, Type *Ty, bool AllowUnknown=false) const

Return the EVT corresponding to this LLVM type.

virtual bool allowTruncateForTailCall(Type *FromTy, Type *ToTy) const

Return true if a truncation from FromTy to ToTy is permitted when deciding whether a call is in tail ...

bool isTypeLegal(EVT VT) const

Return true if the target has native support for the specified value type.

This class defines information used to lower LLVM code to legal SelectionDAG operators that the targe...

Primary interface to the complete machine description for the target machine.

virtual const TargetInstrInfo * getInstrInfo() const

static constexpr TypeSize getFixed(ScalarTy ExactSize)

static constexpr TypeSize getZero()

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.

bool isAggregateType() const

Return true if the type is an aggregate type.

bool isScalableTy(SmallPtrSetImpl< const Type * > &Visited) const

Return true if this is a type whose size is a known multiple of vscale.

bool isVoidTy() const

Return true if this is 'void'.

static UndefValue * get(Type *T)

Static factory methods - Return an 'undef' object of the specified type.

LLVM Value Representation.

Type * getType() const

All values are typed, get the type of this value.

StringRef getName() const

Return a constant reference to the value's name.

constexpr bool isScalable() const

Returns whether the quantity is scaled by a runtime quantity (vscale).

constexpr bool isZero() const

const ParentTy * getParent() const

#define llvm_unreachable(msg)

Marks that the current location is not supposed to be reachable.

@ Tail

Attemps to make calls as fast as possible while guaranteeing that tail call optimization can always b...

@ SwiftTail

This follows the Swift calling convention in how arguments are passed but guarantees tail calls will ...

CondCode

ISD::CondCode enum - These are ordered carefully to make the bitfields below work out,...

This is an optimization pass for GlobalISel generic memory operations.

ISD::CondCode getICmpCondCode(ICmpInst::Predicate Pred)

getICmpCondCode - Return the ISD condition code corresponding to the given LLVM IR integer condition ...

auto enumerate(FirstRange &&First, RestRanges &&...Rest)

Given two or more input ranges, returns a new range whose values are tuples (A, B,...

void append_range(Container &C, Range &&R)

Wrapper function to append range R to container C.

auto reverse(ContainerTy &&C)

bool returnTypeIsEligibleForTailCall(const Function *F, const Instruction *I, const ReturnInst *Ret, const TargetLoweringBase &TLI, bool ReturnsFirstArg=false)

Test if given that the input instruction is in the tail call position if the return type or any attri...

void computeValueLLTs(const DataLayout &DL, Type &Ty, SmallVectorImpl< LLT > &ValueTys, SmallVectorImpl< uint64_t > *Offsets=nullptr, uint64_t StartingOffset=0)

computeValueLLTs - Given an LLVM IR type, compute a sequence of LLTs that represent all the individua...

bool isSafeToSpeculativelyExecute(const Instruction *I, const Instruction *CtxI=nullptr, AssumptionCache *AC=nullptr, const DominatorTree *DT=nullptr, const TargetLibraryInfo *TLI=nullptr, bool UseVariableInfo=true)

Return true if the instruction does not have any effects besides calculating the result and does not ...

ISD::CondCode getFCmpCondCode(FCmpInst::Predicate Pred)

getFCmpCondCode - Return the ISD condition code corresponding to the given LLVM IR floating-point con...

EHPersonality classifyEHPersonality(const Value *Pers)

See if the given exception handling personality function is one that we understand.

bool attributesPermitTailCall(const Function *F, const Instruction *I, const ReturnInst *Ret, const TargetLoweringBase &TLI, bool *AllowDifferingSizes=nullptr)

Test if given that the input instruction is in the tail call position, if there is an attribute misma...

bool isInTailCallPosition(const CallBase &Call, const TargetMachine &TM, bool ReturnsFirstArg=false)

Test if the given instruction is in a position to be optimized with a tail-call.

DWARFExpression::Operation Op

ISD::CondCode getFCmpCodeWithoutNaN(ISD::CondCode CC)

getFCmpCodeWithoutNaN - Given an ISD condition code comparing floats, return the equivalent code if w...

void ComputeValueVTs(const TargetLowering &TLI, const DataLayout &DL, Type *Ty, SmallVectorImpl< EVT > &ValueVTs, SmallVectorImpl< EVT > *MemVTs, SmallVectorImpl< TypeSize > *Offsets=nullptr, TypeSize StartingOffset=TypeSize::getZero())

ComputeValueVTs - Given an LLVM IR type, compute a sequence of EVTs that represent all the individual...

bool isAsynchronousEHPersonality(EHPersonality Pers)

Returns true if this personality function catches asynchronous exceptions.

bool funcReturnsFirstArgOfCall(const CallInst &CI)

Returns true if the parent of CI returns CI's first argument after calling CI.

GlobalValue * ExtractTypeInfo(Value *V)

ExtractTypeInfo - Returns the type info, possibly bitcast, encoded in V.

unsigned ComputeLinearIndex(Type *Ty, const unsigned *Indices, const unsigned *IndicesEnd, unsigned CurIndex=0)

Compute the linearized index of a member in a nested aggregate/struct/array.

DenseMap< const MachineBasicBlock *, int > getEHScopeMembership(const MachineFunction &MF)

LLT getLLTForType(Type &Ty, const DataLayout &DL)

Construct a low-level type based on an LLVM type.

static EVT getEVT(Type *Ty, bool HandleUnknown=false)

Return the value type corresponding to the specified type.