37#include "llvm/IR/IntrinsicsWebAssembly.h"
42#define DEBUG_TYPE "wasm-fastisel"
46class WebAssemblyFastISel final :
public FastISel {
50 enum BaseKind { RegBase, FrameIndexBase };
53 BaseKind Kind = RegBase;
60 bool IsBaseSet =
false;
69 void setKind(BaseKind
K) {
70 assert(!isSet() &&
"Can't change kind with non-zero base");
73 BaseKind getKind()
const {
return Kind; }
74 bool isRegBase()
const {
return Kind == RegBase; }
75 bool isFIBase()
const {
return Kind == FrameIndexBase; }
76 void setReg(
unsigned Reg) {
77 assert(isRegBase() &&
"Invalid base register access!");
78 assert(!IsBaseSet &&
"Base cannot be reset");
83 assert(isRegBase() &&
"Invalid base register access!");
86 void setFI(
unsigned FI) {
87 assert(isFIBase() &&
"Invalid base frame index access!");
88 assert(!IsBaseSet &&
"Base cannot be reset");
92 unsigned getFI()
const {
93 assert(isFIBase() &&
"Invalid base frame index access!");
97 void setOffset(int64_t NewOffset) {
98 assert(NewOffset >= 0 &&
"Offsets must be non-negative");
103 const GlobalValue *getGlobalValue()
const {
return GV; }
104 bool isSet()
const {
return IsBaseSet; }
115 EVT VT = TLI.getValueType(
DL, Ty,
true);
155 bool computeAddress(
const Value *Obj, Address &Addr);
156 void materializeLoadStoreOperands(Address &Addr);
160 unsigned maskI1Value(
unsigned Reg,
const Value *V);
161 unsigned getRegForI1Value(
const Value *V,
const BasicBlock *BB,
bool &Not);
162 unsigned zeroExtendToI32(
unsigned Reg,
const Value *V,
164 unsigned signExtendToI32(
unsigned Reg,
const Value *V,
170 unsigned getRegForUnsignedValue(
const Value *V);
171 unsigned getRegForSignedValue(
const Value *V);
172 unsigned getRegForPromotedValue(
const Value *V,
bool IsSigned);
173 unsigned notValue(
unsigned Reg);
174 unsigned copyValue(
unsigned Reg);
179 bool fastLowerArguments()
override;
201 :
FastISel(FuncInfo, LibInfo, LibcallLowering,
207 bool fastSelectInstruction(
const Instruction *
I)
override;
211#include "WebAssemblyGenFastISel.inc"
216bool WebAssemblyFastISel::computeAddress(
const Value *Obj, Address &Addr) {
217 const User *
U =
nullptr;
218 unsigned Opcode = Instruction::UserOp1;
222 if (FuncInfo.StaticAllocaMap.count(
static_cast<const AllocaInst *
>(Obj)) ||
223 FuncInfo.getMBB(
I->getParent()) == FuncInfo.MBB) {
224 Opcode =
I->getOpcode();
228 Opcode =
C->getOpcode();
233 if (Ty->getAddressSpace() > 255)
239 if (TLI.isPositionIndependent())
241 if (Addr.getGlobalValue())
243 if (GV->isThreadLocal())
245 Addr.setGlobalValue(GV);
252 case Instruction::BitCast: {
254 return computeAddress(
U->getOperand(0), Addr);
256 case Instruction::IntToPtr: {
258 if (TLI.getValueType(
DL,
U->getOperand(0)->getType()) ==
259 TLI.getPointerTy(
DL))
260 return computeAddress(
U->getOperand(0), Addr);
263 case Instruction::PtrToInt: {
265 if (TLI.getValueType(
DL,
U->getType()) == TLI.getPointerTy(
DL))
266 return computeAddress(
U->getOperand(0), Addr);
269 case Instruction::GetElementPtr: {
271 uint64_t TmpOffset = Addr.getOffset();
274 goto unsupported_gep;
279 const Value *
Op = GTI.getOperand();
280 if (StructType *STy = GTI.getStructTypeOrNull()) {
281 const StructLayout *SL =
DL.getStructLayout(STy);
285 uint64_t S = GTI.getSequentialElementStride(
DL);
289 TmpOffset += CI->getSExtValue() * S;
292 if (S == 1 && Addr.isRegBase() && Addr.getReg() == 0) {
300 if (canFoldAddIntoGEP(U,
Op)) {
303 TmpOffset += CI->getSExtValue() * S;
309 goto unsupported_gep;
314 if (int64_t(TmpOffset) >= 0) {
316 Addr.setOffset(TmpOffset);
317 if (computeAddress(
U->getOperand(0), Addr))
325 case Instruction::Alloca: {
327 auto SI = FuncInfo.StaticAllocaMap.find(AI);
328 if (SI != FuncInfo.StaticAllocaMap.end()) {
332 Addr.setKind(Address::FrameIndexBase);
333 Addr.setFI(
SI->second);
338 case Instruction::Add: {
342 if (!OFBinOp->hasNoUnsignedWrap())
353 uint64_t TmpOffset = Addr.getOffset() + CI->getSExtValue();
354 if (int64_t(TmpOffset) >= 0) {
355 Addr.setOffset(TmpOffset);
356 return computeAddress(
LHS, Addr);
361 if (computeAddress(
LHS, Addr) && computeAddress(
RHS, Addr))
367 case Instruction::Sub: {
371 if (!OFBinOp->hasNoUnsignedWrap())
379 int64_t TmpOffset = Addr.getOffset() - CI->getSExtValue();
380 if (TmpOffset >= 0) {
381 Addr.setOffset(TmpOffset);
382 return computeAddress(
LHS, Addr);
395 return Addr.getReg() != 0;
398void WebAssemblyFastISel::materializeLoadStoreOperands(
Address &Addr) {
399 if (Addr.isRegBase()) {
400 unsigned Reg = Addr.getReg();
402 Reg = createResultReg(Subtarget->
hasAddr64() ? &WebAssembly::I64RegClass
403 : &WebAssembly::I32RegClass);
404 unsigned Opc = Subtarget->
hasAddr64() ? WebAssembly::CONST_I64
405 : WebAssembly::CONST_I32;
413void WebAssemblyFastISel::addLoadStoreOperands(
const Address &Addr,
414 const MachineInstrBuilder &MIB,
415 MachineMemOperand *MMO) {
420 if (
const GlobalValue *GV = Addr.getGlobalValue())
423 MIB.
addImm(Addr.getOffset());
425 if (Addr.isRegBase())
426 MIB.
addReg(Addr.getReg());
433bool WebAssemblyFastISel::emitLoad(
Register ResultReg,
unsigned Opc,
434 const LoadInst *
Load) {
436 if (!computeAddress(
Load->getPointerOperand(), Addr))
439 materializeLoadStoreOperands(Addr);
441 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
TII.get(
Opc), ResultReg);
442 addLoadStoreOperands(Addr, MIB, createMachineMemOperandFor(
Load));
447unsigned WebAssemblyFastISel::maskI1Value(
unsigned Reg,
const Value *V) {
448 return zeroExtendToI32(
Reg, V, MVT::i1);
451unsigned WebAssemblyFastISel::getRegForI1Value(
const Value *V,
452 const BasicBlock *BB,
456 if (ICmp->isEquality() &&
C->isZero() &&
C->getType()->isIntegerTy(32) &&
457 ICmp->getParent() == BB) {
458 Not = ICmp->isTrueWhenEqual();
459 return getRegForValue(ICmp->getOperand(0));
466 return maskI1Value(
Reg, V);
469unsigned WebAssemblyFastISel::zeroExtendToI32(
unsigned Reg,
const Value *V,
480 return copyValue(
Reg);
486 return copyValue(
Reg);
491 Register Imm = createResultReg(&WebAssembly::I32RegClass);
492 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
493 TII.get(WebAssembly::CONST_I32),
Imm)
497 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
TII.get(WebAssembly::AND_I32),
505unsigned WebAssemblyFastISel::signExtendToI32(
unsigned Reg,
const Value *V,
516 return copyValue(
Reg);
522 if (From == MVT::i8 || From == MVT::i16) {
524 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
525 TII.get(From == MVT::i16 ? WebAssembly::I32_EXTEND16_S_I32
526 : WebAssembly::I32_EXTEND8_S_I32),
533 Register Imm = createResultReg(&WebAssembly::I32RegClass);
534 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
535 TII.get(WebAssembly::CONST_I32),
Imm)
536 .
addImm(32 - MVT(From).getSizeInBits());
538 Register Left = createResultReg(&WebAssembly::I32RegClass);
539 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
TII.get(WebAssembly::SHL_I32),
545 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
546 TII.get(WebAssembly::SHR_S_I32),
Right)
553unsigned WebAssemblyFastISel::zeroExtend(
unsigned Reg,
const Value *V,
556 if (To == MVT::i64) {
557 if (From == MVT::i64)
558 return copyValue(
Reg);
560 Reg = zeroExtendToI32(
Reg, V, From);
563 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
564 TII.get(WebAssembly::I64_EXTEND_U_I32), Result)
570 return zeroExtendToI32(
Reg, V, From);
575unsigned WebAssemblyFastISel::signExtend(
unsigned Reg,
const Value *V,
578 if (To == MVT::i64) {
579 if (From == MVT::i64)
580 return copyValue(
Reg);
588 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
589 TII.get(WebAssembly::I64_EXTEND_U_I32), Result)
593 Result = createResultReg(&WebAssembly::I64RegClass);
595 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
596 TII.get(From == MVT::i8 ? WebAssembly::I64_EXTEND8_S_I64
597 : WebAssembly::I64_EXTEND16_S_I64),
603 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
604 TII.get(WebAssembly::I64_EXTEND_S_I32), Result)
612 Reg = signExtendToI32(
Reg, V, From);
616 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
617 TII.get(WebAssembly::I64_EXTEND_S_I32), Result)
623 return signExtendToI32(
Reg, V, From);
628unsigned WebAssemblyFastISel::getRegForUnsignedValue(
const Value *V) {
636 return zeroExtend(VReg, V, From, To);
639unsigned WebAssemblyFastISel::getRegForSignedValue(
const Value *V) {
647 return signExtend(VReg, V, From, To);
650unsigned WebAssemblyFastISel::getRegForPromotedValue(
const Value *V,
652 return IsSigned ? getRegForSignedValue(V) : getRegForUnsignedValue(
V);
655unsigned WebAssemblyFastISel::notValue(
unsigned Reg) {
656 assert(MRI.getRegClass(
Reg) == &WebAssembly::I32RegClass);
658 Register NotReg = createResultReg(&WebAssembly::I32RegClass);
659 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
TII.get(WebAssembly::EQZ_I32),
665unsigned WebAssemblyFastISel::copyValue(
unsigned Reg) {
666 Register ResultReg = createResultReg(MRI.getRegClass(
Reg));
667 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
TII.get(WebAssembly::COPY),
673Register WebAssemblyFastISel::fastMaterializeAlloca(
const AllocaInst *AI) {
674 auto SI = FuncInfo.StaticAllocaMap.find(AI);
676 if (SI != FuncInfo.StaticAllocaMap.end()) {
678 createResultReg(Subtarget->
hasAddr64() ? &WebAssembly::I64RegClass
679 : &WebAssembly::I32RegClass);
681 Subtarget->
hasAddr64() ? WebAssembly::COPY_I64 : WebAssembly::COPY_I32;
682 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
TII.get(
Opc), ResultReg)
690Register WebAssemblyFastISel::fastMaterializeConstant(
const Constant *
C) {
692 if (TLI.isPositionIndependent())
694 if (GV->isThreadLocal())
697 createResultReg(Subtarget->
hasAddr64() ? &WebAssembly::I64RegClass
698 : &WebAssembly::I32RegClass);
699 unsigned Opc = Subtarget->
hasAddr64() ? WebAssembly::CONST_I64
700 : WebAssembly::CONST_I32;
701 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
TII.get(
Opc), ResultReg)
710bool WebAssemblyFastISel::fastLowerArguments() {
711 if (!FuncInfo.CanLowerReturn)
718 if (FuncInfo.Fn->getCallingConv() == CallingConv::Swift)
722 for (
auto const &Arg :
F->args()) {
723 const AttributeList &
Attrs =
F->getAttributes();
724 if (
Attrs.hasParamAttr(
I, Attribute::ByVal) ||
725 Attrs.hasParamAttr(
I, Attribute::SwiftSelf) ||
726 Attrs.hasParamAttr(
I, Attribute::SwiftError) ||
727 Attrs.hasParamAttr(
I, Attribute::InAlloca) ||
728 Attrs.hasParamAttr(
I, Attribute::Nest))
731 Type *ArgTy = Arg.getType();
739 switch (getSimpleType(ArgTy)) {
744 Opc = WebAssembly::ARGUMENT_i32;
745 RC = &WebAssembly::I32RegClass;
748 Opc = WebAssembly::ARGUMENT_i64;
749 RC = &WebAssembly::I64RegClass;
752 Opc = WebAssembly::ARGUMENT_f32;
753 RC = &WebAssembly::F32RegClass;
756 Opc = WebAssembly::ARGUMENT_f64;
757 RC = &WebAssembly::F64RegClass;
760 Opc = WebAssembly::ARGUMENT_v16i8;
761 RC = &WebAssembly::V128RegClass;
764 Opc = WebAssembly::ARGUMENT_v8i16;
765 RC = &WebAssembly::V128RegClass;
768 Opc = WebAssembly::ARGUMENT_v4i32;
769 RC = &WebAssembly::V128RegClass;
772 Opc = WebAssembly::ARGUMENT_v2i64;
773 RC = &WebAssembly::V128RegClass;
776 Opc = WebAssembly::ARGUMENT_v4f32;
777 RC = &WebAssembly::V128RegClass;
780 Opc = WebAssembly::ARGUMENT_v2f64;
781 RC = &WebAssembly::V128RegClass;
784 Opc = WebAssembly::ARGUMENT_funcref;
785 RC = &WebAssembly::FUNCREFRegClass;
788 Opc = WebAssembly::ARGUMENT_externref;
789 RC = &WebAssembly::EXTERNREFRegClass;
792 Opc = WebAssembly::ARGUMENT_exnref;
793 RC = &WebAssembly::EXNREFRegClass;
798 Register ResultReg = createResultReg(RC);
799 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
TII.get(
Opc), ResultReg)
801 updateValueMap(&Arg, ResultReg);
806 MRI.addLiveIn(WebAssembly::ARGUMENTS);
808 auto *MFI = MF->getInfo<WebAssemblyFunctionInfo>();
809 for (
auto const &Arg :
F->args()) {
812 MFI->clearParamsAndResults();
815 MFI->addParam(ArgTy);
818 if (!
F->getReturnType()->isVoidTy()) {
820 getLegalType(getSimpleType(
F->getReturnType()));
822 MFI->clearParamsAndResults();
825 MFI->addResult(RetTy);
831bool WebAssemblyFastISel::selectCall(
const Instruction *
I) {
840 if (Func &&
Func->isIntrinsic())
846 bool IsDirect =
Func !=
nullptr;
851 unsigned Opc = IsDirect ? WebAssembly::CALL : WebAssembly::CALL_INDIRECT;
852 bool IsVoid = FuncTy->getReturnType()->isVoidTy();
864 ResultReg = createResultReg(&WebAssembly::I32RegClass);
867 ResultReg = createResultReg(&WebAssembly::I64RegClass);
870 ResultReg = createResultReg(&WebAssembly::F32RegClass);
873 ResultReg = createResultReg(&WebAssembly::F64RegClass);
876 ResultReg = createResultReg(&WebAssembly::V128RegClass);
879 ResultReg = createResultReg(&WebAssembly::V128RegClass);
882 ResultReg = createResultReg(&WebAssembly::V128RegClass);
885 ResultReg = createResultReg(&WebAssembly::V128RegClass);
888 ResultReg = createResultReg(&WebAssembly::V128RegClass);
891 ResultReg = createResultReg(&WebAssembly::V128RegClass);
894 ResultReg = createResultReg(&WebAssembly::FUNCREFRegClass);
897 ResultReg = createResultReg(&WebAssembly::EXTERNREFRegClass);
900 ResultReg = createResultReg(&WebAssembly::EXNREFRegClass);
907 SmallVector<unsigned, 8>
Args;
915 if (
Attrs.hasParamAttr(
I, Attribute::ByVal) ||
916 Attrs.hasParamAttr(
I, Attribute::SwiftSelf) ||
917 Attrs.hasParamAttr(
I, Attribute::SwiftError) ||
918 Attrs.hasParamAttr(
I, Attribute::InAlloca) ||
919 Attrs.hasParamAttr(
I, Attribute::Nest))
925 Reg = getRegForSignedValue(V);
927 Reg = getRegForUnsignedValue(V);
929 Reg = getRegForValue(V);
937 unsigned CalleeReg = 0;
944 const Value *FuncrefArg =
nullptr;
946 if (Conv->getIntrinsicID() == Intrinsic::wasm_funcref_to_ptr)
947 FuncrefArg = Conv->getArgOperand(0);
949 const bool IsFuncrefCall = FuncrefArg !=
nullptr;
950 MCSymbolWasm *
Table =
nullptr;
953 if (!IsFuncrefCall) {
955 Table = WebAssembly::getOrCreateFunctionTableSymbol(MF->getContext(),
962 Table = WebAssembly::getOrCreateFuncrefCallTableSymbol(MF->getContext(),
964 CalleeReg = getRegForValue(FuncrefArg);
966 unsigned ZeroReg = createResultReg(&WebAssembly::I32RegClass);
967 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
968 TII.get(WebAssembly::CONST_I32), ZeroReg)
970 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
971 TII.get(WebAssembly::TABLE_SET_FUNCREF))
976 CalleeReg = createResultReg(&WebAssembly::I32RegClass);
977 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
978 TII.get(WebAssembly::CONST_I32), CalleeReg)
983 auto MIB =
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
TII.get(
Opc));
986 MIB.
addReg(ResultReg, RegState::Define);
1004 for (
unsigned ArgReg : Args)
1010 if (IsFuncrefCall) {
1012 unsigned ZeroReg = createResultReg(&WebAssembly::I32RegClass);
1013 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
1014 TII.get(WebAssembly::CONST_I32), ZeroReg)
1016 unsigned NullReg = createResultReg(&WebAssembly::FUNCREFRegClass);
1017 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
1018 TII.get(WebAssembly::REF_NULL_FUNCREF), NullReg);
1019 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
1020 TII.get(WebAssembly::TABLE_SET_FUNCREF))
1027 updateValueMap(
Call, ResultReg);
1033bool WebAssemblyFastISel::selectSelect(
const Instruction *
I) {
1038 getRegForI1Value(
Select->getCondition(),
I->getParent(), Not);
1055 switch (getSimpleType(
Select->getType())) {
1060 Opc = WebAssembly::SELECT_I32;
1061 RC = &WebAssembly::I32RegClass;
1064 Opc = WebAssembly::SELECT_I64;
1065 RC = &WebAssembly::I64RegClass;
1068 Opc = WebAssembly::SELECT_F32;
1069 RC = &WebAssembly::F32RegClass;
1072 Opc = WebAssembly::SELECT_F64;
1073 RC = &WebAssembly::F64RegClass;
1076 Opc = WebAssembly::SELECT_FUNCREF;
1077 RC = &WebAssembly::FUNCREFRegClass;
1079 case MVT::externref:
1080 Opc = WebAssembly::SELECT_EXTERNREF;
1081 RC = &WebAssembly::EXTERNREFRegClass;
1084 Opc = WebAssembly::SELECT_EXNREF;
1085 RC = &WebAssembly::EXNREFRegClass;
1091 Register ResultReg = createResultReg(RC);
1092 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
TII.get(
Opc), ResultReg)
1097 updateValueMap(
Select, ResultReg);
1101bool WebAssemblyFastISel::selectTrunc(
const Instruction *
I) {
1104 const Value *
Op = Trunc->getOperand(0);
1112 if (From == MVT::i64) {
1114 return copyValue(
Reg);
1116 if (To == MVT::i1 || To == MVT::i8 || To == MVT::i16 || To == MVT::i32) {
1118 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
1119 TII.get(WebAssembly::I32_WRAP_I64), Result)
1125 if (From == MVT::i32)
1126 return copyValue(
Reg);
1131 unsigned Reg = Truncate(In);
1135 updateValueMap(Trunc,
Reg);
1139bool WebAssemblyFastISel::selectZExt(
const Instruction *
I) {
1142 const Value *
Op = ZExt->getOperand(0);
1148 unsigned Reg = zeroExtend(In,
Op, From, To);
1152 updateValueMap(ZExt,
Reg);
1156bool WebAssemblyFastISel::selectSExt(
const Instruction *
I) {
1159 const Value *
Op = SExt->getOperand(0);
1165 unsigned Reg = signExtend(In,
Op, From, To);
1169 updateValueMap(SExt,
Reg);
1173bool WebAssemblyFastISel::selectICmp(
const Instruction *
I) {
1180 if (ICmp->getOperand(0)->getType()->isVectorTy())
1183 bool I32 = getSimpleType(ICmp->getOperand(0)->getType()) != MVT::i64;
1185 bool IsSigned =
false;
1186 switch (ICmp->getPredicate()) {
1187 case ICmpInst::ICMP_EQ:
1188 Opc =
I32 ? WebAssembly::EQ_I32 : WebAssembly::EQ_I64;
1190 case ICmpInst::ICMP_NE:
1191 Opc =
I32 ? WebAssembly::NE_I32 : WebAssembly::NE_I64;
1193 case ICmpInst::ICMP_UGT:
1194 Opc =
I32 ? WebAssembly::GT_U_I32 : WebAssembly::GT_U_I64;
1196 case ICmpInst::ICMP_UGE:
1197 Opc =
I32 ? WebAssembly::GE_U_I32 : WebAssembly::GE_U_I64;
1199 case ICmpInst::ICMP_ULT:
1200 Opc =
I32 ? WebAssembly::LT_U_I32 : WebAssembly::LT_U_I64;
1202 case ICmpInst::ICMP_ULE:
1203 Opc =
I32 ? WebAssembly::LE_U_I32 : WebAssembly::LE_U_I64;
1205 case ICmpInst::ICMP_SGT:
1206 Opc =
I32 ? WebAssembly::GT_S_I32 : WebAssembly::GT_S_I64;
1209 case ICmpInst::ICMP_SGE:
1210 Opc =
I32 ? WebAssembly::GE_S_I32 : WebAssembly::GE_S_I64;
1213 case ICmpInst::ICMP_SLT:
1214 Opc =
I32 ? WebAssembly::LT_S_I32 : WebAssembly::LT_S_I64;
1217 case ICmpInst::ICMP_SLE:
1218 Opc =
I32 ? WebAssembly::LE_S_I32 : WebAssembly::LE_S_I64;
1225 unsigned LHS = getRegForPromotedValue(ICmp->getOperand(0), IsSigned);
1229 unsigned RHS = getRegForPromotedValue(ICmp->getOperand(1), IsSigned);
1233 Register ResultReg = createResultReg(&WebAssembly::I32RegClass);
1234 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
TII.get(
Opc), ResultReg)
1237 updateValueMap(ICmp, ResultReg);
1241bool WebAssemblyFastISel::selectFCmp(
const Instruction *
I) {
1248 if (FCmp->getOperand(0)->getType()->isVectorTy())
1251 Register LHS = getRegForValue(FCmp->getOperand(0));
1255 Register RHS = getRegForValue(FCmp->getOperand(1));
1259 bool F32 = getSimpleType(FCmp->getOperand(0)->getType()) != MVT::f64;
1262 switch (FCmp->getPredicate()) {
1263 case FCmpInst::FCMP_OEQ:
1264 Opc =
F32 ? WebAssembly::EQ_F32 : WebAssembly::EQ_F64;
1266 case FCmpInst::FCMP_UNE:
1267 Opc =
F32 ? WebAssembly::NE_F32 : WebAssembly::NE_F64;
1269 case FCmpInst::FCMP_OGT:
1270 Opc =
F32 ? WebAssembly::GT_F32 : WebAssembly::GT_F64;
1272 case FCmpInst::FCMP_OGE:
1273 Opc =
F32 ? WebAssembly::GE_F32 : WebAssembly::GE_F64;
1275 case FCmpInst::FCMP_OLT:
1276 Opc =
F32 ? WebAssembly::LT_F32 : WebAssembly::LT_F64;
1278 case FCmpInst::FCMP_OLE:
1279 Opc =
F32 ? WebAssembly::LE_F32 : WebAssembly::LE_F64;
1281 case FCmpInst::FCMP_UGT:
1282 Opc =
F32 ? WebAssembly::LE_F32 : WebAssembly::LE_F64;
1285 case FCmpInst::FCMP_UGE:
1286 Opc =
F32 ? WebAssembly::LT_F32 : WebAssembly::LT_F64;
1289 case FCmpInst::FCMP_ULT:
1290 Opc =
F32 ? WebAssembly::GE_F32 : WebAssembly::GE_F64;
1293 case FCmpInst::FCMP_ULE:
1294 Opc =
F32 ? WebAssembly::GT_F32 : WebAssembly::GT_F64;
1301 Register ResultReg = createResultReg(&WebAssembly::I32RegClass);
1302 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
TII.get(
Opc), ResultReg)
1307 ResultReg = notValue(ResultReg);
1309 updateValueMap(FCmp, ResultReg);
1313bool WebAssemblyFastISel::selectBitCast(
const Instruction *
I) {
1317 EVT VT = TLI.getValueType(
DL,
I->getOperand(0)->getType());
1318 EVT RetVT = TLI.getValueType(
DL,
I->getType());
1328 updateValueMap(
I, In);
1338 assert(Iter->isBitcast());
1339 Iter->setImplicitPhysRegDefsDead();
1340 updateValueMap(
I,
Reg);
1348 return WebAssembly::INSTRUCTION_LIST_END;
1350 return A64 ? WebAssembly::LOAD8_S_I64_A64 : WebAssembly::LOAD8_S_I64_A32;
1352 return A64 ? WebAssembly::LOAD16_S_I64_A64
1353 : WebAssembly::LOAD16_S_I64_A32;
1355 return A64 ? WebAssembly::LOAD32_S_I64_A64
1356 : WebAssembly::LOAD32_S_I64_A32;
1362 return WebAssembly::INSTRUCTION_LIST_END;
1364 return A64 ? WebAssembly::LOAD8_S_I32_A64 : WebAssembly::LOAD8_S_I32_A32;
1366 return A64 ? WebAssembly::LOAD16_S_I32_A64 : WebAssembly::LOAD16_S_I32_A32;
1374 return WebAssembly::INSTRUCTION_LIST_END;
1376 return A64 ? WebAssembly::LOAD8_U_I64_A64 : WebAssembly::LOAD8_U_I64_A32;
1378 return A64 ? WebAssembly::LOAD16_U_I64_A64
1379 : WebAssembly::LOAD16_U_I64_A32;
1381 return A64 ? WebAssembly::LOAD32_U_I64_A64
1382 : WebAssembly::LOAD32_U_I64_A32;
1388 return WebAssembly::INSTRUCTION_LIST_END;
1390 return A64 ? WebAssembly::LOAD8_U_I32_A64 : WebAssembly::LOAD8_U_I32_A32;
1392 return A64 ? WebAssembly::LOAD16_U_I32_A64 : WebAssembly::LOAD16_U_I32_A32;
1400 case WebAssembly::I32_EXTEND8_S_I32:
1401 case WebAssembly::I32_EXTEND16_S_I32:
1402 case WebAssembly::I64_EXTEND8_S_I64:
1403 case WebAssembly::I64_EXTEND16_S_I64:
1404 case WebAssembly::I64_EXTEND32_S_I64:
1405 case WebAssembly::I64_EXTEND_S_I32:
1414 case WebAssembly::I32_EXTEND8_S_I32:
1415 case WebAssembly::I32_EXTEND16_S_I32:
1417 case WebAssembly::I64_EXTEND8_S_I64:
1418 case WebAssembly::I64_EXTEND16_S_I64:
1419 case WebAssembly::I64_EXTEND32_S_I64:
1420 case WebAssembly::I64_EXTEND_S_I32:
1427 unsigned Opc =
MI->getOpcode();
1434 return WebAssembly::INSTRUCTION_LIST_END;
1440 unsigned NarrowOpc) {
1447 case WebAssembly::I64_EXTEND_U_I32:
1448 OuterUserMI = UserMI;
1450 case WebAssembly::I64_EXTEND_S_I32:
1451 OuterUserMI = UserMI;
1469 unsigned Opc =
MI->getOpcode();
1470 unsigned NewOpc = WebAssembly::INSTRUCTION_LIST_END;
1471 if (
Opc != WebAssembly::SHL_I32)
1474 Register DestReg =
MI->getOperand(0).getReg();
1480 if (UserOpc != WebAssembly::SHR_S_I32)
1488 Register ShlAmtReg =
MI->getOperand(2).getReg();
1493 return MI &&
MI->getOpcode() == WebAssembly::CONST_I32 &&
1494 MI->getOperand(1).getImm() == ExpectedShiftAmt;
1496 if (!IsExpectedConst(ShlAmtDef) || !IsExpectedConst(ShrAmtDef))
1501 if (NarrowOpc == WebAssembly::INSTRUCTION_LIST_END)
1502 return WebAssembly::INSTRUCTION_LIST_END;
1505 OuterUserMI, NarrowOpc);
1513 if (
MI->getOpcode() != WebAssembly::I64_EXTEND_U_I32)
1514 return WebAssembly::INSTRUCTION_LIST_END;
1517 Register DestReg =
MI->getOperand(0).getReg();
1519 return WebAssembly::INSTRUCTION_LIST_END;
1524 return WebAssembly::INSTRUCTION_LIST_END;
1525 case WebAssembly::I64_EXTEND8_S_I64:
1527 return WebAssembly::INSTRUCTION_LIST_END;
1529 case WebAssembly::I64_EXTEND16_S_I64:
1531 return WebAssembly::INSTRUCTION_LIST_END;
1539 if (
MI->getOpcode() != WebAssembly::COPY)
1540 return WebAssembly::INSTRUCTION_LIST_END;
1544 return WebAssembly::INSTRUCTION_LIST_END;
1546 Register CopyDst =
MI->getOperand(0).getReg();
1548 return WebAssembly::INSTRUCTION_LIST_END;
1553 return WebAssembly::INSTRUCTION_LIST_END;
1554 case WebAssembly::I64_EXTEND_U_I32:
1556 case WebAssembly::I64_EXTEND_S_I32:
1564 if (
MI->getOpcode() != WebAssembly::AND_I32 &&
1565 MI->getOpcode() != WebAssembly::AND_I64)
1566 return WebAssembly::INSTRUCTION_LIST_END;
1569 bool IsConstant =
false;
1570 for (
unsigned I = 1;
I <= 2; ++
I) {
1573 if (
DefMI && (
DefMI->getOpcode() == WebAssembly::CONST_I32 ||
1574 DefMI->getOpcode() == WebAssembly::CONST_I64)) {
1575 Mask =
DefMI->getOperand(1).getImm();
1582 return WebAssembly::INSTRUCTION_LIST_END;
1586 return WebAssembly::INSTRUCTION_LIST_END;
1588 if (
MI->getOpcode() == WebAssembly::AND_I64)
1592 if (NarrowOpc == WebAssembly::INSTRUCTION_LIST_END)
1593 return WebAssembly::INSTRUCTION_LIST_END;
1596 OuterUserMI, NarrowOpc);
1599bool WebAssemblyFastISel::tryToFoldLoadIntoMI(MachineInstr *
MI,
unsigned OpNo,
1600 const LoadInst *LI) {
1602 MachineRegisterInfo &MRI = FuncInfo.MF->getRegInfo();
1604 MachineInstr *UserMI =
nullptr;
1605 MachineInstr *OuterUserMI =
nullptr;
1606 unsigned NewOpc = WebAssembly::INSTRUCTION_LIST_END;
1608 WebAssembly::INSTRUCTION_LIST_END) {
1610 }
else if ((NewOpc =
1612 WebAssembly::INSTRUCTION_LIST_END) {
1615 WebAssembly::INSTRUCTION_LIST_END) {
1617 :
MI->getOperand(0).getReg();
1619 WebAssembly::INSTRUCTION_LIST_END) {
1620 ResultReg =
MI->getOperand(0).getReg();
1621 }
else if ((NewOpc =
1623 WebAssembly::INSTRUCTION_LIST_END) {
1630 if (!
emitLoad(ResultReg, NewOpc, LI))
1635 removeDeadCode(OuterIter, std::next(OuterIter));
1640 removeDeadCode(UserIter, std::next(UserIter));
1644 removeDeadCode(Iter, std::next(Iter));
1648bool WebAssemblyFastISel::selectLoad(
const Instruction *
I) {
1650 if (
Load->isAtomic())
1652 if (!WebAssembly::isDefaultAddressSpace(
Load->getPointerAddressSpace()))
1662 switch (getSimpleType(
Load->getType())) {
1665 Opc = A64 ? WebAssembly::LOAD8_U_I32_A64 : WebAssembly::LOAD8_U_I32_A32;
1666 RC = &WebAssembly::I32RegClass;
1669 Opc = A64 ? WebAssembly::LOAD16_U_I32_A64 : WebAssembly::LOAD16_U_I32_A32;
1670 RC = &WebAssembly::I32RegClass;
1673 Opc = A64 ? WebAssembly::LOAD_I32_A64 : WebAssembly::LOAD_I32_A32;
1674 RC = &WebAssembly::I32RegClass;
1677 Opc = A64 ? WebAssembly::LOAD_I64_A64 : WebAssembly::LOAD_I64_A32;
1678 RC = &WebAssembly::I64RegClass;
1681 Opc = A64 ? WebAssembly::LOAD_F32_A64 : WebAssembly::LOAD_F32_A32;
1682 RC = &WebAssembly::F32RegClass;
1685 Opc = A64 ? WebAssembly::LOAD_F64_A64 : WebAssembly::LOAD_F64_A32;
1686 RC = &WebAssembly::F64RegClass;
1692 Register ResultReg = createResultReg(RC);
1696 updateValueMap(
Load, ResultReg);
1700bool WebAssemblyFastISel::selectStore(
const Instruction *
I) {
1702 if (
Store->isAtomic())
1704 if (!WebAssembly::isDefaultAddressSpace(
Store->getPointerAddressSpace()))
1707 Store->getValueOperand()->getType()->isVectorTy())
1711 if (!computeAddress(
Store->getPointerOperand(), Addr))
1715 bool VTIsi1 =
false;
1717 switch (getSimpleType(
Store->getValueOperand()->getType())) {
1722 Opc = A64 ? WebAssembly::STORE8_I32_A64 : WebAssembly::STORE8_I32_A32;
1725 Opc = A64 ? WebAssembly::STORE16_I32_A64 : WebAssembly::STORE16_I32_A32;
1728 Opc = A64 ? WebAssembly::STORE_I32_A64 : WebAssembly::STORE_I32_A32;
1731 Opc = A64 ? WebAssembly::STORE_I64_A64 : WebAssembly::STORE_I64_A32;
1734 Opc = A64 ? WebAssembly::STORE_F32_A64 : WebAssembly::STORE_F32_A32;
1737 Opc = A64 ? WebAssembly::STORE_F64_A64 : WebAssembly::STORE_F64_A32;
1743 materializeLoadStoreOperands(Addr);
1745 Register ValueReg = getRegForValue(
Store->getValueOperand());
1749 ValueReg = maskI1Value(ValueReg,
Store->getValueOperand());
1751 auto MIB =
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
TII.get(
Opc));
1753 addLoadStoreOperands(Addr, MIB, createMachineMemOperandFor(
Store));
1759bool WebAssemblyFastISel::selectCondBr(
const Instruction *
I) {
1762 MachineBasicBlock *
TBB = FuncInfo.getMBB(Br->getSuccessor(0));
1763 MachineBasicBlock *FBB = FuncInfo.getMBB(Br->getSuccessor(1));
1766 unsigned CondReg = getRegForI1Value(Br->getCondition(), Br->getParent(), Not);
1770 unsigned Opc = WebAssembly::BR_IF;
1772 Opc = WebAssembly::BR_UNLESS;
1774 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
TII.get(
Opc))
1778 finishCondBranch(Br->getParent(),
TBB, FBB);
1782bool WebAssemblyFastISel::selectRet(
const Instruction *
I) {
1783 if (!FuncInfo.CanLowerReturn)
1788 if (Ret->getNumOperands() == 0) {
1789 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
1790 TII.get(WebAssembly::RETURN));
1795 if (Ret->getNumOperands() > 1)
1798 Value *RV = Ret->getOperand(0);
1802 switch (getSimpleType(RV->
getType())) {
1817 case MVT::externref:
1825 if (FuncInfo.Fn->getAttributes().hasRetAttr(Attribute::SExt))
1826 Reg = getRegForSignedValue(RV);
1827 else if (FuncInfo.Fn->getAttributes().hasRetAttr(Attribute::ZExt))
1828 Reg = getRegForUnsignedValue(RV);
1830 Reg = getRegForValue(RV);
1835 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
TII.get(WebAssembly::RETURN))
1840bool WebAssemblyFastISel::selectUnreachable(
const Instruction *
I) {
1841 BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
1842 TII.get(WebAssembly::UNREACHABLE));
1846bool WebAssemblyFastISel::fastSelectInstruction(
const Instruction *
I) {
1847 switch (
I->getOpcode()) {
1848 case Instruction::Call:
1852 case Instruction::Select:
1853 return selectSelect(
I);
1854 case Instruction::Trunc:
1855 return selectTrunc(
I);
1856 case Instruction::ZExt:
1857 return selectZExt(
I);
1858 case Instruction::SExt:
1859 return selectSExt(
I);
1860 case Instruction::ICmp:
1861 return selectICmp(
I);
1862 case Instruction::FCmp:
1863 return selectFCmp(
I);
1864 case Instruction::BitCast:
1865 return selectBitCast(
I);
1866 case Instruction::Load:
1867 return selectLoad(
I);
1868 case Instruction::Store:
1869 return selectStore(
I);
1870 case Instruction::CondBr:
1871 return selectCondBr(
I);
1872 case Instruction::Ret:
1873 return selectRet(
I);
1874 case Instruction::Unreachable:
1875 return selectUnreachable(
I);
1881 return selectOperator(
I,
I->getOpcode());
1888 return new WebAssemblyFastISel(FuncInfo, LibInfo, LibcallLowering);
MachineInstrBuilder MachineInstrBuilder & DefMI
static void emitLoad(MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator Pos, const TargetInstrInfo &TII, unsigned Reg1, unsigned Reg2, int Offset, bool IsPostDec)
Emit a load-pair instruction for frame-destroy.
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
AMDGPU Register Bank Select
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
This file defines the FastISel class.
const HexagonInstrInfo * TII
This file declares the MachineConstantPool class which is an abstract constant pool to keep track of ...
Promote Memory to Register
static MCRegister getReg(const MCDisassembler *D, unsigned RC, unsigned RegNo)
const SmallVectorImpl< MachineOperand > MachineBasicBlock * TBB
static bool isFoldableSExtOpcode(unsigned Opc)
static unsigned getSExtLoadOpcode(unsigned LoadSize, bool I64Result, bool A64)
static bool isI64SExtResult(unsigned Opc)
static unsigned matchFoldableCopyToI64Ext(MachineInstr *MI, const LoadInst *LI, MachineRegisterInfo &MRI, bool A64, MachineInstr *&OuterUserMI)
static unsigned matchFoldableSExtFromPromotedI32(MachineInstr *MI, const LoadInst *LI, MachineRegisterInfo &MRI, bool A64, MachineInstr *&UserMI)
static unsigned getZExtLoadOpcode(unsigned LoadSize, bool I64Result, bool A64)
static unsigned matchFoldableShift(MachineInstr *MI, const LoadInst *LI, MachineRegisterInfo &MRI, bool A64, MachineInstr *&UserMI, MachineInstr *&OuterUserMI)
Matches a sign-extension pattern (shl + shr_s) to fold it into a signed load.
static unsigned getFoldedI64LoadOpcode(Register DestReg, const LoadInst *LI, MachineRegisterInfo &MRI, bool A64, MachineInstr *&OuterUserMI, unsigned NarrowOpc)
static unsigned getFoldedLoadOpcode(MachineInstr *MI, MachineRegisterInfo &MRI, const LoadInst *LI, bool A64)
static unsigned matchFoldableAnd(MachineInstr *MI, const LoadInst *LI, MachineRegisterInfo &MRI, bool A64, MachineInstr *&OuterUserMI)
This file provides WebAssembly-specific target descriptions.
This file declares WebAssembly-specific per-machine-function information.
This file declares the WebAssembly-specific subclass of TargetSubtarget.
This file contains the declaration of the WebAssembly-specific utility functions.
an instruction to allocate memory on the stack
LLVM Basic Block Representation.
bool isInlineAsm() const
Check if this call is an inline asm statement.
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
CallingConv::ID getCallingConv() const
LLVM_ABI bool paramHasAttr(unsigned ArgNo, Attribute::AttrKind Kind) const
Determine whether the argument or parameter has the given attribute.
Value * getCalledOperand() const
Value * getArgOperand(unsigned i) const
FunctionType * getFunctionType() const
unsigned arg_size() const
AttributeList getAttributes() const
Return the attributes for this call.
bool isMustTailCall() const
This is an important base class in LLVM.
This is a fast-path instruction selection class that generates poor code and doesn't support illegal ...
FunctionLoweringInfo - This contains information that is global to a function that is used when lower...
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
This is an important class for using LLVM in a threaded context.
Tracks which library functions to use for a particular subtarget or function.
An instruction for reading from memory.
@ INVALID_SIMPLE_VALUE_TYPE
MachineInstrBundleIterator< MachineInstr > iterator
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
const MachineInstrBuilder & addReg(Register RegNo, RegState Flags={}, unsigned SubReg=0) const
Add a new virtual register operand.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & addSym(MCSymbol *Sym, unsigned char TargetFlags=0) const
const MachineInstrBuilder & addFrameIndex(int Idx) const
const MachineInstrBuilder & addGlobalAddress(const GlobalValue *GV, int64_t Offset=0, unsigned TargetFlags=0) const
const MachineInstrBuilder & addMBB(MachineBasicBlock *MBB, unsigned TargetFlags=0) const
const MachineInstrBuilder & addMemOperand(MachineMemOperand *MMO) const
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
const MachineOperand & getOperand(unsigned i) const
A description of a memory reference used in the backend.
Register getReg() const
getReg - Returns the register number.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
LLVM_ABI bool hasOneNonDBGUse(Register RegNo) const
hasOneNonDBGUse - Return true if there is exactly one non-Debug use of the specified register.
use_instr_nodbg_iterator use_instr_nodbg_begin(Register RegNo) const
LLVM_ABI LLVM_READONLY MachineInstr * getUniqueVRegDef(Register Reg) const
getUniqueVRegDef - Return the unique machine instr that defines the specified virtual register or nul...
Wrapper class representing virtual and physical registers.
TypeSize getElementOffset(unsigned Idx) const
Provides information about what library functions are available for the current target.
The instances of the Type class are immutable: once they are created, they are never changed.
LLVM_ABI unsigned getIntegerBitWidth() const
bool isVectorTy() const
True if this is an instance of VectorType.
bool isArrayTy() const
True if this is an instance of ArrayType.
bool isStructTy() const
True if this is an instance of StructType.
LLVM_ABI TypeSize getPrimitiveSizeInBits() const LLVM_READONLY
Return the basic size of this type if it is a primitive type.
bool isIntegerTy() const
True if this is an instance of IntegerType.
LLVM Value Representation.
Type * getType() const
All values are typed, get the type of this value.
bool hasCallIndirectOverlong() const
bool hasReferenceTypes() const
bool hasExceptionHandling() const
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
constexpr char Attrs[]
Key for Kernel::Metadata::mAttrs.
Not(const Pred &P) -> Not< Pred >
FastISel * createFastISel(FunctionLoweringInfo &funcInfo, const TargetLibraryInfo *libInfo, const LibcallLoweringInfo *libcallLowering)
@ User
could "use" a pointer
NodeAddr< FuncNode * > Func
This is an optimization pass for GlobalISel generic memory operations.
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
LLVM_ABI void diagnoseDontCall(const CallInst &CI)
@ Load
The value being inserted comes from a load (InsertElement only).
@ Store
The extracted value is stored (ExtractElement only).
gep_type_iterator gep_type_end(const User *GEP)
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
static Error getOffset(const SymbolRef &Sym, SectionRef Sec, uint64_t &Result)
generic_gep_type_iterator<> gep_type_iterator
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...
DWARFExpression::Operation Op
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
gep_type_iterator gep_type_begin(const User *GEP)
constexpr T maskTrailingOnes(unsigned N)
Create a bitmask with the N right-most bits set to 1, and all other bits set to 0.
MCRegisterClass TargetRegisterClass
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
bool isSimple() const
Test if the given EVT is simple (as opposed to being extended).
MVT getSimpleVT() const
Return the SimpleValueType held in the specified simple EVT.