17#include "llvm/Analysis/LoopInfo.h"
18#include "llvm/Analysis/RegionInfo.h"
19#include "llvm/Analysis/ScalarEvolution.h"
20#include "llvm/Analysis/ScalarEvolutionExpressions.h"
21#include "llvm/Transforms/Utils/BasicBlockUtils.h"
22#include "llvm/Transforms/Utils/LoopUtils.h"
23#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h"
29#define DEBUG_TYPE "polly-scop-helper"
33 cl::desc(
"Allow calls to the specified functions in SCoPs even if their "
34 "side-effects are unknown. This can be used to do debug output in "
35 "Polly-transformed code."),
43 BasicBlock *EnteringBB = R->getEnteringBlock();
44 BasicBlock *
Entry = R->getEntry();
58 SmallVector<BasicBlock *, 4> Preds;
59 for (BasicBlock *P : predecessors(
Entry))
63 BasicBlock *NewEntering =
64 SplitBlockPredecessors(
Entry, Preds,
".region_entering", DT, LI);
68 for (BasicBlock *ExitPred : predecessors(NewEntering)) {
69 Region *RegionOfPred = RI->getRegionFor(ExitPred);
70 if (RegionOfPred->getExit() !=
Entry)
73 while (!RegionOfPred->isTopLevelRegion() &&
74 RegionOfPred->getExit() ==
Entry) {
75 RegionOfPred->replaceExit(NewEntering);
76 RegionOfPred = RegionOfPred->getParent();
81 Region *AncestorR = R->getParent();
82 RI->setRegionFor(NewEntering, AncestorR);
83 while (!AncestorR->isTopLevelRegion() && AncestorR->getEntry() ==
Entry) {
84 AncestorR->replaceEntry(NewEntering);
85 AncestorR = AncestorR->getParent();
89 EnteringBB = NewEntering;
91 assert(R->getEnteringBlock() == EnteringBB);
107 BasicBlock *ExitBB = R->getExit();
108 BasicBlock *ExitingBB = R->getExitingBlock();
118 SmallVector<BasicBlock *, 4> Preds;
119 for (BasicBlock *P : predecessors(ExitBB))
128 SplitBlockPredecessors(ExitBB, Preds,
".region_exiting", DT, LI);
136 RI->setRegionFor(ExitingBB, R);
139 R->replaceExitRecursive(ExitingBB);
140 R->replaceExit(ExitBB);
142 assert(ExitingBB == R->getExitingBlock());
155 assert(R && !R->isTopLevelRegion());
156 assert(!RI || RI == R->getRegionInfo());
158 "RegionInfo requires DominatorTree to be updated as well");
168static BasicBlock *
splitBlock(BasicBlock *Old, Instruction *SplitPt,
169 DominatorTree *DT, llvm::LoopInfo *LI,
179 BasicBlock *NewBlock = llvm::SplitBlock(Old, SplitPt, DT, LI);
182 Region *R = RI->getRegionFor(Old);
183 RI->setRegionFor(NewBlock, R);
198 LoopInfo *LI, RegionInfo *RI) {
201 BasicBlock::iterator I = EntryBlock->begin();
202 while (isa<AllocaInst>(I))
210 auto *DTWP = P->getAnalysisIfAvailable<DominatorTreeWrapperPass>();
211 auto *DT = DTWP ? &DTWP->getDomTree() :
nullptr;
212 auto *LIWP = P->getAnalysisIfAvailable<LoopInfoWrapperPass>();
213 auto *LI = LIWP ? &LIWP->getLoopInfo() :
nullptr;
214 RegionInfoPass *RIP = P->getAnalysisIfAvailable<RegionInfoPass>();
215 RegionInfo *RI = RIP ? &RIP->getRegionInfo() :
nullptr;
224 BasicBlock *BB,
bool RTC) {
226 "Assumptions without a basic block must be parameter sets");
227 if (RecordedAssumptions)
228 RecordedAssumptions->push_back({
Kind, Sign, Set, Loc, BB, RTC});
257 ScalarEvolution &
GenSE,
const DataLayout &DL,
266 "ScopExpander assumes to be applied to generated code region");
267 const SCEV *GenE =
visit(E);
268 return Expander.expandCodeFor(GenE, Ty, IP);
277 const SCEV *Result = SCEVVisitor::visit(E);
297 Function *Fn =
R.getEntry()->getParent();
300 "Instruction expected to be either in the SCoP or the translated "
312 assert(!Inst->mayThrow() && !Inst->mayReadOrWriteMemory() &&
313 !isa<PHINode>(Inst));
315 auto *InstClone = Inst->clone();
316 for (
auto &Op : Inst->operands()) {
318 auto *OpSCEV =
GenSE.getSCEV(Op);
320 InstClone->replaceUsesOfWith(Op, OpClone);
323 InstClone->setName(
Name + Inst->getName());
324 InstClone->insertBefore(IP);
325 return GenSE.getSCEV(InstClone);
331 Value *NewVal =
VMap ?
VMap->lookup(E->getValue()) :
nullptr;
333 auto *NewE =
GenSE.getSCEV(NewVal);
343 Instruction *Inst = dyn_cast<Instruction>(E->getValue());
347 else if (
R.getEntry()->getParent() !=
GenFn) {
352 IP =
GenFn->getEntryBlock().getTerminator();
353 }
else if (Inst &&
RTCBB->getParent() == Inst->getFunction())
354 IP =
RTCBB->getTerminator();
356 IP =
RTCBB->getParent()->getEntryBlock().getTerminator();
358 if (!Inst || (Inst->getOpcode() != Instruction::SRem &&
359 Inst->getOpcode() != Instruction::SDiv))
362 const SCEV *LHSScev =
GenSE.getSCEV(Inst->getOperand(0));
363 const SCEV *RHSScev =
GenSE.getSCEV(Inst->getOperand(1));
365 if (!
GenSE.isKnownNonZero(RHSScev))
366 RHSScev =
GenSE.getUMaxExpr(RHSScev,
GenSE.getConstant(E->getType(), 1));
372 BinaryOperator::Create((Instruction::BinaryOps)Inst->getOpcode(), LHS,
373 RHS, Inst->getName() +
Name, IP->getIterator());
374 return GenSE.getSCEV(Inst);
384 return GenSE.getPtrToIntExpr(
visit(E->getOperand()), E->getType());
387 return GenSE.getTruncateExpr(
visit(E->getOperand()), E->getType());
390 return GenSE.getZeroExtendExpr(
visit(E->getOperand()), E->getType());
393 return GenSE.getSignExtendExpr(
visit(E->getOperand()), E->getType());
396 auto *RHSScev =
visit(E->getRHS());
397 if (!
GenSE.isKnownNonZero(RHSScev))
398 RHSScev =
GenSE.getUMaxExpr(RHSScev,
GenSE.getConstant(E->getType(), 1));
399 return GenSE.getUDivExpr(
visit(E->getLHS()), RHSScev);
402 SmallVector<const SCEV *, 4> NewOps;
403 for (
const SCEV *Op : E->operands())
404 NewOps.push_back(
visit(Op));
405 return GenSE.getAddExpr(NewOps);
408 SmallVector<const SCEV *, 4> NewOps;
409 for (
const SCEV *Op : E->operands())
410 NewOps.push_back(
visit(Op));
411 return GenSE.getMulExpr(NewOps);
414 SmallVector<const SCEV *, 4> NewOps;
415 for (
const SCEV *Op : E->operands())
416 NewOps.push_back(
visit(Op));
417 return GenSE.getUMaxExpr(NewOps);
420 SmallVector<const SCEV *, 4> NewOps;
421 for (
const SCEV *Op : E->operands())
422 NewOps.push_back(
visit(Op));
423 return GenSE.getSMaxExpr(NewOps);
426 SmallVector<const SCEV *, 4> NewOps;
427 for (
const SCEV *Op : E->operands())
428 NewOps.push_back(
visit(Op));
429 return GenSE.getUMinExpr(NewOps);
432 SmallVector<const SCEV *, 4> NewOps;
433 for (
const SCEV *Op : E->operands())
434 NewOps.push_back(
visit(Op));
435 return GenSE.getSMinExpr(NewOps);
438 SmallVector<const SCEV *, 4> NewOps;
439 for (
const SCEV *Op : E->operands())
440 NewOps.push_back(
visit(Op));
441 return GenSE.getUMinExpr(NewOps,
true);
444 SmallVector<const SCEV *, 4> NewOps;
445 for (
const SCEV *Op : E->operands())
446 NewOps.push_back(
visit(Op));
448 const Loop *L = E->getLoop();
451 return GenSE.getAddRecExpr(NewOps, L, E->getNoWrapFlags());
454 const SCEV *Evaluated =
455 SCEVAddRecExpr::evaluateAtIteration(NewOps, GenLRepl,
GenSE);
460 return visit(Evaluated);
466 llvm::Function *GenFn, ScalarEvolution &GenSE,
467 const DataLayout &DL,
const char *Name,
468 const SCEV *E, Type *Ty, Instruction *IP,
471 ScopExpander Expander(
S.getRegion(), SE, GenFn, GenSE, DL, Name, VMap,
473 return Expander.expandCodeFor(E, Ty, IP);
477 if (BranchInst *BR = dyn_cast<BranchInst>(TI)) {
478 if (BR->isUnconditional())
479 return ConstantInt::getTrue(Type::getInt1Ty(TI->getContext()));
481 return BR->getCondition();
484 if (SwitchInst *SI = dyn_cast<SwitchInst>(TI))
485 return SI->getCondition();
496 Loop *L = LI.getLoopFor(
S.getEntry());
498 bool AllContained =
true;
499 for (
auto *BB :
S.blocks())
500 AllContained &= L->contains(BB);
503 L = L->getParentLoop();
506 return L ? (
S.contains(L) ? L->getParentLoop() : L) :
nullptr;
510 unsigned NumBlocks = L->getNumBlocks();
511 SmallVector<BasicBlock *, 4> ExitBlocks;
512 L->getExitBlocks(ExitBlocks);
514 for (
auto ExitBlock : ExitBlocks) {
515 if (isa<UnreachableInst>(ExitBlock->getTerminator()))
522 if (!RN->isSubRegion())
525 Region *R = RN->getNodeAs<Region>();
526 return std::distance(R->block_begin(), R->block_end());
530 if (!RN->isSubRegion()) {
531 BasicBlock *BB = RN->getNodeAs<BasicBlock>();
532 Loop *L = LI.getLoopFor(BB);
552 if (!L && isa<UnreachableInst>(BB->getTerminator()) && BB->getPrevNode())
553 L = LI.getLoopFor(BB->getPrevNode());
557 Region *NonAffineSubRegion = RN->getNodeAs<Region>();
558 Loop *L = LI.getLoopFor(NonAffineSubRegion->getEntry());
559 while (L && NonAffineSubRegion->contains(L))
560 L = L->getParentLoop();
565 ScalarEvolution &SE) {
566 for (
const Use &Val : llvm::drop_begin(Gep->operands(), 1)) {
567 const SCEV *PtrSCEV = SE.getSCEVAtScope(Val, L);
568 Loop *OuterLoop = R.outermostLoopInRegion(L);
569 if (!SE.isLoopInvariant(PtrSCEV, OuterLoop))
576 ScalarEvolution &SE,
const DominatorTree &DT,
578 Loop *L = LI.getLoopFor(LInst->getParent());
579 auto *Ptr = LInst->getPointerOperand();
588 if (
auto *GepInst = dyn_cast<GetElementPtrInst>(Ptr)) {
590 if (
auto *DecidingLoad =
591 dyn_cast<LoadInst>(GepInst->getPointerOperand())) {
592 if (KnownInvariantLoads.count(DecidingLoad))
598 const SCEV *PtrSCEV = SE.getSCEVAtScope(Ptr, L);
599 while (L && R.contains(L)) {
600 if (!SE.isLoopInvariant(PtrSCEV, L))
602 L = L->getParentLoop();
605 for (
auto *User : Ptr->users()) {
606 auto *UserI = dyn_cast<Instruction>(User);
607 if (!UserI || !R.contains(UserI))
609 if (!UserI->mayWriteToMemory())
612 auto &BB = *UserI->getParent();
613 if (DT.dominates(&BB, LInst->getParent()))
616 bool DominatesAllPredecessors =
true;
617 if (R.isTopLevelRegion()) {
618 for (BasicBlock &I : *R.getEntry()->getParent())
619 if (isa<ReturnInst>(I.getTerminator()) && !DT.dominates(&BB, &I))
620 DominatesAllPredecessors =
false;
622 for (
auto Pred : predecessors(R.getExit()))
623 if (R.contains(Pred) && !DT.dominates(&BB, Pred))
624 DominatesAllPredecessors =
false;
627 if (!DominatesAllPredecessors)
637 if (
auto *IT = dyn_cast<IntrinsicInst>(V)) {
638 switch (IT->getIntrinsicID()) {
640 case llvm::Intrinsic::lifetime_start:
641 case llvm::Intrinsic::lifetime_end:
643 case llvm::Intrinsic::invariant_start:
644 case llvm::Intrinsic::invariant_end:
646 case llvm::Intrinsic::var_annotation:
647 case llvm::Intrinsic::ptr_annotation:
648 case llvm::Intrinsic::annotation:
649 case llvm::Intrinsic::donothing:
650 case llvm::Intrinsic::assume:
652 case llvm::Intrinsic::dbg_value:
653 case llvm::Intrinsic::dbg_declare:
664 if (!V || !SE->isSCEVable(V->getType()))
668 if (
const SCEV *Scev = SE->getSCEVAtScope(
const_cast<Value *
>(V), Scope))
669 if (!isa<SCEVCouldNotCompute>(Scev))
677 Instruction *UI = dyn_cast<Instruction>(U.getUser());
681 if (PHINode *
PHI = dyn_cast<PHINode>(UI))
682 return PHI->getIncomingBlock(U);
684 return UI->getParent();
689 while (BoxedLoops.count(L))
690 L = L->getParentLoop();
697 Loop *L = LI.getLoopFor(BB);
702 auto *CI = dyn_cast<CallInst>(Inst);
706 Function *CF = CI->getCalledFunction();
715 for (Instruction &Inst : *BB) {
735 for (BasicBlock *RBB : Stmt->
getRegion()->blocks())
747 for (
const MDOperand &
X : drop_begin(LoopMD->operands(), 1)) {
748 auto *OpNode = dyn_cast<MDNode>(
X.get());
752 auto *OpName = dyn_cast<MDString>(OpNode->getOperand(0));
755 if (OpName->getString() == Name)
766 switch (MD->getNumOperands()) {
770 return &MD->getOperand(1);
772 llvm_unreachable(
"loop metadata has 0 or 1 operand");
781 switch (MD->getNumOperands()) {
785 return MD->getOperand(1).get();
787 llvm_unreachable(
"loop metadata must have 0 or 1 operands");
796 switch (MD->getNumOperands()) {
800 if (ConstantInt *IntMD =
801 mdconst::extract_or_null<ConstantInt>(MD->getOperand(1).get()))
802 return IntMD->getZExtValue();
805 llvm_unreachable(
"unexpected number of options");
814 const MDOperand *AttrMD =
819 ConstantInt *IntMD = mdconst::extract_or_null<ConstantInt>(AttrMD->get());
823 return IntMD->getSExtValue();
827 return llvm::hasDisableAllTransformsHint(L);
835 assert(Attr &&
"Must be a valid BandAttr");
842 BandAttr *Attr = reinterpret_cast<BandAttr *>(Ptr);
853 MDNode *LoopID = L->getLoopID();
868 return Id.
get_name() ==
"Loop with Metadata";
polly dump Polly Dump Function
llvm::cl::OptionCategory PollyCategory
static RegisterPass< ScopViewerWrapperPass > X("view-scops", "Polly - View Scops of function")
static std::optional< bool > getOptionalBoolLoopAttribute(MDNode *LoopID, StringRef Name)
static BasicBlock * splitBlock(BasicBlock *Old, Instruction *SplitPt, DominatorTree *DT, llvm::LoopInfo *LI, RegionInfo *RI)
static bool hasDebugCall(BasicBlock *BB)
static void simplifyRegionEntry(Region *R, DominatorTree *DT, LoopInfo *LI, RegionInfo *RI)
static std::optional< const MDOperand * > findNamedMetadataArg(MDNode *LoopID, StringRef Name)
static cl::list< std::string > DebugFunctions("polly-debug-func", cl::desc("Allow calls to the specified functions in SCoPs even if their " "side-effects are unknown. This can be used to do debug output in " "Polly-transformed code."), cl::Hidden, cl::CommaSeparated, cl::cat(PollyCategory))
static MDNode * findNamedMetadataNode(MDNode *LoopMD, StringRef Name)
Find a property in a LoopID.
static void simplifyRegionExit(Region *R, DominatorTree *DT, LoopInfo *LI, RegionInfo *RI)
static bool hasVariantIndex(GetElementPtrInst *Gep, Loop *L, Region &R, ScalarEvolution &SE)
__isl_give isl_id * release()
std::string get_name() const
static isl::id alloc(isl::ctx ctx, const std::string &name, void *user)
boolean is_params() const
BasicBlock * getEntryBlock() const
Return a BasicBlock from this statement.
const std::vector< Instruction * > & getInstructions() const
Region * getRegion() const
Get the region represented by this ScopStmt (if any).
bool isRegionStmt() const
Return true if this statement represents a whole region.
__isl_give isl_id * isl_id_set_free_user(__isl_take isl_id *id, void(*free_user)(void *user))
boolean manage(isl_bool val)
This file contains the declaration of the PolyhedralInfo class, which will provide an interface to ex...
llvm::Loop * getRegionNodeLoop(llvm::RegionNode *RN, llvm::LoopInfo &LI)
Return the smallest loop surrounding RN.
llvm::Value * getConditionFromTerminator(llvm::Instruction *TI)
Return the condition for the terminator TI.
bool isLoopAttr(const isl::id &Id)
Is Id representing a loop?
std::optional< llvm::Metadata * > findMetadataOperand(llvm::MDNode *LoopMD, llvm::StringRef Name)
Find a property value in a LoopID.
unsigned getNumBlocksInRegionNode(llvm::RegionNode *RN)
Get the number of blocks in RN.
llvm::Loop * getFirstNonBoxedLoopFor(llvm::Loop *L, llvm::LoopInfo &LI, const BoxedLoopsSetTy &BoxedLoops)
AssumptionSign
Enum to distinguish between assumptions and restrictions.
@ Value
MemoryKind::Value: Models an llvm::Value.
@ PHI
MemoryKind::PHI: Models PHI nodes within the SCoP.
bool hasDisableAllTransformsHint(llvm::Loop *L)
Does the loop's LoopID contain a 'llvm.loop.disable_heuristics' property?
std::optional< int > getOptionalIntLoopAttribute(llvm::MDNode *LoopID, llvm::StringRef Name)
Find an integers property value in a LoopID.
llvm::SmallVector< Assumption, 8 > RecordedAssumptionsTy
bool isDebugCall(llvm::Instruction *Inst)
Is the given instruction a call to a debug function?
bool hasDebugCall(ScopStmt *Stmt)
Does the statement contain a call to a debug function?
BandAttr * getLoopAttr(const isl::id &Id)
Return the BandAttr of a loop's isl::id.
llvm::BasicBlock * getUseBlock(const llvm::Use &U)
Return the block in which a value is used.
llvm::Value * expandCodeFor(Scop &S, llvm::ScalarEvolution &SE, llvm::Function *GenFn, llvm::ScalarEvolution &GenSE, const llvm::DataLayout &DL, const char *Name, const llvm::SCEV *E, llvm::Type *Ty, llvm::Instruction *IP, ValueMapT *VMap, LoopToScevMapT *LoopMap, llvm::BasicBlock *RTCBB)
Wrapper for SCEVExpander extended to all Polly features.
llvm::Loop * getLoopSurroundingScop(Scop &S, llvm::LoopInfo &LI)
Get the smallest loop that contains S but is not in S.
llvm::SetVector< llvm::AssertingVH< llvm::LoadInst > > InvariantLoadsSetTy
Type for a set of invariant loads.
void recordAssumption(RecordedAssumptionsTy *RecordedAssumptions, AssumptionKind Kind, isl::set Set, llvm::DebugLoc Loc, AssumptionSign Sign, llvm::BasicBlock *BB=nullptr, bool RTC=true)
Record an assumption for later addition to the assumed context.
llvm::SetVector< const llvm::Loop * > BoxedLoopsSetTy
Set of loops (used to remember loops in non-affine subregions).
isl::id getIslLoopAttr(isl::ctx Ctx, BandAttr *Attr)
Get an isl::id representing a loop.
bool isHoistableLoad(llvm::LoadInst *LInst, llvm::Region &R, llvm::LoopInfo &LI, llvm::ScalarEvolution &SE, const llvm::DominatorTree &DT, const InvariantLoadsSetTy &KnownInvariantLoads)
Check if LInst can be hoisted in R.
void splitEntryBlockForAlloca(llvm::BasicBlock *EntryBlock, llvm::Pass *P)
Split the entry block of a function to store the newly inserted allocations outside of all Scops.
isl::id createIslLoopAttr(isl::ctx Ctx, llvm::Loop *L)
Create an isl::id that identifies an original loop.
bool hasScalarDepsInsideRegion(const llvm::SCEV *Expr, const llvm::Region *R, llvm::Loop *Scope, bool AllowLoops, const InvariantLoadsSetTy &ILS)
Returns true when the SCEV contains references to instructions within the region.
llvm::DenseMap< llvm::AssertingVH< llvm::Value >, llvm::AssertingVH< llvm::Value > > ValueMapT
Type to remap values.
AssumptionKind
Enumeration of assumptions Polly can take.
llvm::DenseMap< const llvm::Loop *, const llvm::SCEV * > LoopToScevMapT
Same as llvm/Analysis/ScalarEvolutionExpressions.h.
bool isIgnoredIntrinsic(const llvm::Value *V)
Return true iff V is an intrinsic that we ignore during code generation.
void simplifyRegion(llvm::Region *R, llvm::DominatorTree *DT, llvm::LoopInfo *LI, llvm::RegionInfo *RI)
Simplify the region to have a single unconditional entry edge and a single exit edge.
bool canSynthesize(const llvm::Value *V, const Scop &S, llvm::ScalarEvolution *SE, llvm::Loop *Scope)
Check whether a value an be synthesized by the code generator.
bool getBooleanLoopAttribute(llvm::MDNode *LoopID, llvm::StringRef Name)
Find a boolean property value in a LoopID.
unsigned getNumBlocksInLoop(llvm::Loop *L)
Get the number of blocks in L.
ScopExpander generates IR the the value of a SCEV that represents a value from a SCoP.
const SCEV * visit(const SCEV *E)
const SCEV * visitAddExpr(const SCEVAddExpr *E)
const SCEV * visitUMaxExpr(const SCEVUMaxExpr *E)
const SCEV * visitUMinExpr(const SCEVUMinExpr *E)
const SCEV * visitUnknown(const SCEVUnknown *E)
const SCEV * visitGenericInst(const SCEVUnknown *E, Instruction *Inst, Instruction *IP)
const SCEV * visitAddRecExpr(const SCEVAddRecExpr *E)
const SCEV * visitPtrToIntExpr(const SCEVPtrToIntExpr *E)
const SCEV * visitSMaxExpr(const SCEVSMaxExpr *E)
const SCEV * visitConstant(const SCEVConstant *E)
The following functions will just traverse the SCEV and rebuild it using GenSE and the new operands r...
ScopExpander(const Region &R, ScalarEvolution &SE, Function *GenFn, ScalarEvolution &GenSE, const DataLayout &DL, const char *Name, ValueMapT *VMap, LoopToScevMapT *LoopMap, BasicBlock *RTCBB)
const SCEV * visitSequentialUMinExpr(const SCEVSequentialUMinExpr *E)
const SCEV * visitZeroExtendExpr(const SCEVZeroExtendExpr *E)
const SCEV * visitUDivExpr(const SCEVUDivExpr *E)
DenseMap< const SCEV *, const SCEV * > SCEVCache
const SCEV * visitSignExtendExpr(const SCEVSignExtendExpr *E)
bool isInOrigRegion(Instruction *Inst)
Is the instruction part of the original SCoP (in contrast to be located in the code-generated region)...
const SCEV * visitSMinExpr(const SCEVSMinExpr *E)
Value * expandCodeFor(const SCEV *E, Type *Ty, Instruction *IP)
const SCEV * visitMulExpr(const SCEVMulExpr *E)
const SCEV * visitVScale(const SCEVVScale *E)
bool isInGenRegion(Instruction *Inst)
const SCEV * visitTruncateExpr(const SCEVTruncateExpr *E)
Represent the attributes of a loop.
llvm::MDNode * Metadata
LoopID which stores the properties of the loop, such as transformations to apply and the metadata of ...
llvm::Loop * OriginalLoop
The LoopInfo reference for this loop.