| //===-- WebAssemblyFastISel.cpp - WebAssembly FastISel implementation -----===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// This file defines the WebAssembly-specific support for the FastISel |
| /// class. Some of the target-specific code is generated by tablegen in the file |
| /// WebAssemblyGenFastISel.inc, which is #included here. |
| /// |
| /// TODO: kill flags |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" |
| #include "Utils/WebAssemblyUtilities.h" |
| #include "WebAssembly.h" |
| #include "WebAssemblyMachineFunctionInfo.h" |
| #include "WebAssemblySubtarget.h" |
| #include "WebAssemblyTargetMachine.h" |
| #include "llvm/Analysis/BranchProbabilityInfo.h" |
| #include "llvm/CodeGen/FastISel.h" |
| #include "llvm/CodeGen/FunctionLoweringInfo.h" |
| #include "llvm/CodeGen/MachineConstantPool.h" |
| #include "llvm/CodeGen/MachineFrameInfo.h" |
| #include "llvm/CodeGen/MachineInstrBuilder.h" |
| #include "llvm/CodeGen/MachineModuleInfo.h" |
| #include "llvm/CodeGen/MachineRegisterInfo.h" |
| #include "llvm/IR/DataLayout.h" |
| #include "llvm/IR/DerivedTypes.h" |
| #include "llvm/IR/Function.h" |
| #include "llvm/IR/GetElementPtrTypeIterator.h" |
| #include "llvm/IR/GlobalAlias.h" |
| #include "llvm/IR/GlobalVariable.h" |
| #include "llvm/IR/Instructions.h" |
| #include "llvm/IR/IntrinsicInst.h" |
| #include "llvm/IR/Operator.h" |
| #include "llvm/IR/PatternMatch.h" |
| |
| using namespace llvm; |
| using namespace PatternMatch; |
| |
| #define DEBUG_TYPE "wasm-fastisel" |
| |
| namespace { |
| |
| class WebAssemblyFastISel final : public FastISel { |
| // All possible address modes. |
| class Address { |
| public: |
| using BaseKind = enum { RegBase, FrameIndexBase }; |
| |
| private: |
| BaseKind Kind = RegBase; |
| union { |
| unsigned Reg; |
| int FI; |
| } Base; |
| |
| // Whether the base has been determined yet |
| bool IsBaseSet = false; |
| |
| int64_t Offset = 0; |
| |
| const GlobalValue *GV = nullptr; |
| |
| public: |
| // Innocuous defaults for our address. |
| Address() { Base.Reg = 0; } |
| void setKind(BaseKind K) { |
| assert(!isSet() && "Can't change kind with non-zero base"); |
| Kind = K; |
| } |
| BaseKind getKind() const { return Kind; } |
| bool isRegBase() const { return Kind == RegBase; } |
| bool isFIBase() const { return Kind == FrameIndexBase; } |
| void setReg(unsigned Reg) { |
| assert(isRegBase() && "Invalid base register access!"); |
| assert(!IsBaseSet && "Base cannot be reset"); |
| Base.Reg = Reg; |
| IsBaseSet = true; |
| } |
| unsigned getReg() const { |
| assert(isRegBase() && "Invalid base register access!"); |
| return Base.Reg; |
| } |
| void setFI(unsigned FI) { |
| assert(isFIBase() && "Invalid base frame index access!"); |
| assert(!IsBaseSet && "Base cannot be reset"); |
| Base.FI = FI; |
| IsBaseSet = true; |
| } |
| unsigned getFI() const { |
| assert(isFIBase() && "Invalid base frame index access!"); |
| return Base.FI; |
| } |
| |
| void setOffset(int64_t NewOffset) { |
| assert(NewOffset >= 0 && "Offsets must be non-negative"); |
| Offset = NewOffset; |
| } |
| int64_t getOffset() const { return Offset; } |
| void setGlobalValue(const GlobalValue *G) { GV = G; } |
| const GlobalValue *getGlobalValue() const { return GV; } |
| bool isSet() const { return IsBaseSet; } |
| }; |
| |
| /// Keep a pointer to the WebAssemblySubtarget around so that we can make the |
| /// right decision when generating code for different targets. |
| const WebAssemblySubtarget *Subtarget; |
| LLVMContext *Context; |
| |
| private: |
| // Utility helper routines |
| MVT::SimpleValueType getSimpleType(Type *Ty) { |
| EVT VT = TLI.getValueType(DL, Ty, /*AllowUnknown=*/true); |
| return VT.isSimple() ? VT.getSimpleVT().SimpleTy |
| : MVT::INVALID_SIMPLE_VALUE_TYPE; |
| } |
| MVT::SimpleValueType getLegalType(MVT::SimpleValueType VT) { |
| switch (VT) { |
| case MVT::i1: |
| case MVT::i8: |
| case MVT::i16: |
| return MVT::i32; |
| case MVT::i32: |
| case MVT::i64: |
| case MVT::f32: |
| case MVT::f64: |
| return VT; |
| case MVT::funcref: |
| case MVT::externref: |
| if (Subtarget->hasReferenceTypes()) |
| return VT; |
| break; |
| case MVT::f16: |
| return MVT::f32; |
| case MVT::v16i8: |
| case MVT::v8i16: |
| case MVT::v4i32: |
| case MVT::v4f32: |
| case MVT::v2i64: |
| case MVT::v2f64: |
| if (Subtarget->hasSIMD128()) |
| return VT; |
| break; |
| default: |
| break; |
| } |
| return MVT::INVALID_SIMPLE_VALUE_TYPE; |
| } |
| bool computeAddress(const Value *Obj, Address &Addr); |
| void materializeLoadStoreOperands(Address &Addr); |
| void addLoadStoreOperands(const Address &Addr, const MachineInstrBuilder &MIB, |
| MachineMemOperand *MMO); |
| unsigned maskI1Value(unsigned Reg, const Value *V); |
| unsigned getRegForI1Value(const Value *V, const BasicBlock *BB, bool &Not); |
| unsigned zeroExtendToI32(unsigned Reg, const Value *V, |
| MVT::SimpleValueType From); |
| unsigned signExtendToI32(unsigned Reg, const Value *V, |
| MVT::SimpleValueType From); |
| unsigned zeroExtend(unsigned Reg, const Value *V, MVT::SimpleValueType From, |
| MVT::SimpleValueType To); |
| unsigned signExtend(unsigned Reg, const Value *V, MVT::SimpleValueType From, |
| MVT::SimpleValueType To); |
| unsigned getRegForUnsignedValue(const Value *V); |
| unsigned getRegForSignedValue(const Value *V); |
| unsigned getRegForPromotedValue(const Value *V, bool IsSigned); |
| unsigned notValue(unsigned Reg); |
| unsigned copyValue(unsigned Reg); |
| |
| // Backend specific FastISel code. |
| unsigned fastMaterializeAlloca(const AllocaInst *AI) override; |
| unsigned fastMaterializeConstant(const Constant *C) override; |
| bool fastLowerArguments() override; |
| |
| // Selection routines. |
| bool selectCall(const Instruction *I); |
| bool selectSelect(const Instruction *I); |
| bool selectTrunc(const Instruction *I); |
| bool selectZExt(const Instruction *I); |
| bool selectSExt(const Instruction *I); |
| bool selectICmp(const Instruction *I); |
| bool selectFCmp(const Instruction *I); |
| bool selectBitCast(const Instruction *I); |
| bool selectLoad(const Instruction *I); |
| bool selectStore(const Instruction *I); |
| bool selectBr(const Instruction *I); |
| bool selectRet(const Instruction *I); |
| bool selectUnreachable(const Instruction *I); |
| |
| public: |
| // Backend specific FastISel code. |
| WebAssemblyFastISel(FunctionLoweringInfo &FuncInfo, |
| const TargetLibraryInfo *LibInfo) |
| : FastISel(FuncInfo, LibInfo, /*SkipTargetIndependentISel=*/true) { |
| Subtarget = &FuncInfo.MF->getSubtarget<WebAssemblySubtarget>(); |
| Context = &FuncInfo.Fn->getContext(); |
| } |
| |
| bool fastSelectInstruction(const Instruction *I) override; |
| |
| #include "WebAssemblyGenFastISel.inc" |
| }; |
| |
| } // end anonymous namespace |
| |
| bool WebAssemblyFastISel::computeAddress(const Value *Obj, Address &Addr) { |
| const User *U = nullptr; |
| unsigned Opcode = Instruction::UserOp1; |
| if (const auto *I = dyn_cast<Instruction>(Obj)) { |
| // Don't walk into other basic blocks unless the object is an alloca from |
| // another block, otherwise it may not have a virtual register assigned. |
| if (FuncInfo.StaticAllocaMap.count(static_cast<const AllocaInst *>(Obj)) || |
| FuncInfo.MBBMap[I->getParent()] == FuncInfo.MBB) { |
| Opcode = I->getOpcode(); |
| U = I; |
| } |
| } else if (const auto *C = dyn_cast<ConstantExpr>(Obj)) { |
| Opcode = C->getOpcode(); |
| U = C; |
| } |
| |
| if (auto *Ty = dyn_cast<PointerType>(Obj->getType())) |
| if (Ty->getAddressSpace() > 255) |
| // Fast instruction selection doesn't support the special |
| // address spaces. |
| return false; |
| |
| if (const auto *GV = dyn_cast<GlobalValue>(Obj)) { |
| if (TLI.isPositionIndependent()) |
| return false; |
| if (Addr.getGlobalValue()) |
| return false; |
| if (GV->isThreadLocal()) |
| return false; |
| Addr.setGlobalValue(GV); |
| return true; |
| } |
| |
| switch (Opcode) { |
| default: |
| break; |
| case Instruction::BitCast: { |
| // Look through bitcasts. |
| return computeAddress(U->getOperand(0), Addr); |
| } |
| case Instruction::IntToPtr: { |
| // Look past no-op inttoptrs. |
| if (TLI.getValueType(DL, U->getOperand(0)->getType()) == |
| TLI.getPointerTy(DL)) |
| return computeAddress(U->getOperand(0), Addr); |
| break; |
| } |
| case Instruction::PtrToInt: { |
| // Look past no-op ptrtoints. |
| if (TLI.getValueType(DL, U->getType()) == TLI.getPointerTy(DL)) |
| return computeAddress(U->getOperand(0), Addr); |
| break; |
| } |
| case Instruction::GetElementPtr: { |
| Address SavedAddr = Addr; |
| uint64_t TmpOffset = Addr.getOffset(); |
| // Non-inbounds geps can wrap; wasm's offsets can't. |
| if (!cast<GEPOperator>(U)->isInBounds()) |
| goto unsupported_gep; |
| // Iterate through the GEP folding the constants into offsets where |
| // we can. |
| for (gep_type_iterator GTI = gep_type_begin(U), E = gep_type_end(U); |
| GTI != E; ++GTI) { |
| const Value *Op = GTI.getOperand(); |
| if (StructType *STy = GTI.getStructTypeOrNull()) { |
| const StructLayout *SL = DL.getStructLayout(STy); |
| unsigned Idx = cast<ConstantInt>(Op)->getZExtValue(); |
| TmpOffset += SL->getElementOffset(Idx); |
| } else { |
| uint64_t S = DL.getTypeAllocSize(GTI.getIndexedType()); |
| for (;;) { |
| if (const auto *CI = dyn_cast<ConstantInt>(Op)) { |
| // Constant-offset addressing. |
| TmpOffset += CI->getSExtValue() * S; |
| break; |
| } |
| if (S == 1 && Addr.isRegBase() && Addr.getReg() == 0) { |
| // An unscaled add of a register. Set it as the new base. |
| unsigned Reg = getRegForValue(Op); |
| if (Reg == 0) |
| return false; |
| Addr.setReg(Reg); |
| break; |
| } |
| if (canFoldAddIntoGEP(U, Op)) { |
| // A compatible add with a constant operand. Fold the constant. |
| auto *CI = cast<ConstantInt>(cast<AddOperator>(Op)->getOperand(1)); |
| TmpOffset += CI->getSExtValue() * S; |
| // Iterate on the other operand. |
| Op = cast<AddOperator>(Op)->getOperand(0); |
| continue; |
| } |
| // Unsupported |
| goto unsupported_gep; |
| } |
| } |
| } |
| // Don't fold in negative offsets. |
| if (int64_t(TmpOffset) >= 0) { |
| // Try to grab the base operand now. |
| Addr.setOffset(TmpOffset); |
| if (computeAddress(U->getOperand(0), Addr)) |
| return true; |
| } |
| // We failed, restore everything and try the other options. |
| Addr = SavedAddr; |
| unsupported_gep: |
| break; |
| } |
| case Instruction::Alloca: { |
| const auto *AI = cast<AllocaInst>(Obj); |
| DenseMap<const AllocaInst *, int>::iterator SI = |
| FuncInfo.StaticAllocaMap.find(AI); |
| if (SI != FuncInfo.StaticAllocaMap.end()) { |
| if (Addr.isSet()) { |
| return false; |
| } |
| Addr.setKind(Address::FrameIndexBase); |
| Addr.setFI(SI->second); |
| return true; |
| } |
| break; |
| } |
| case Instruction::Add: { |
| // Adds of constants are common and easy enough. |
| const Value *LHS = U->getOperand(0); |
| const Value *RHS = U->getOperand(1); |
| |
| if (isa<ConstantInt>(LHS)) |
| std::swap(LHS, RHS); |
| |
| if (const auto *CI = dyn_cast<ConstantInt>(RHS)) { |
| uint64_t TmpOffset = Addr.getOffset() + CI->getSExtValue(); |
| if (int64_t(TmpOffset) >= 0) { |
| Addr.setOffset(TmpOffset); |
| return computeAddress(LHS, Addr); |
| } |
| } |
| |
| Address Backup = Addr; |
| if (computeAddress(LHS, Addr) && computeAddress(RHS, Addr)) |
| return true; |
| Addr = Backup; |
| |
| break; |
| } |
| case Instruction::Sub: { |
| // Subs of constants are common and easy enough. |
| const Value *LHS = U->getOperand(0); |
| const Value *RHS = U->getOperand(1); |
| |
| if (const auto *CI = dyn_cast<ConstantInt>(RHS)) { |
| int64_t TmpOffset = Addr.getOffset() - CI->getSExtValue(); |
| if (TmpOffset >= 0) { |
| Addr.setOffset(TmpOffset); |
| return computeAddress(LHS, Addr); |
| } |
| } |
| break; |
| } |
| } |
| if (Addr.isSet()) { |
| return false; |
| } |
| unsigned Reg = getRegForValue(Obj); |
| if (Reg == 0) |
| return false; |
| Addr.setReg(Reg); |
| return Addr.getReg() != 0; |
| } |
| |
| void WebAssemblyFastISel::materializeLoadStoreOperands(Address &Addr) { |
| if (Addr.isRegBase()) { |
| unsigned Reg = Addr.getReg(); |
| if (Reg == 0) { |
| Reg = createResultReg(Subtarget->hasAddr64() ? &WebAssembly::I64RegClass |
| : &WebAssembly::I32RegClass); |
| unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CONST_I64 |
| : WebAssembly::CONST_I32; |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), Reg) |
| .addImm(0); |
| Addr.setReg(Reg); |
| } |
| } |
| } |
| |
| void WebAssemblyFastISel::addLoadStoreOperands(const Address &Addr, |
| const MachineInstrBuilder &MIB, |
| MachineMemOperand *MMO) { |
| // Set the alignment operand (this is rewritten in SetP2AlignOperands). |
| // TODO: Disable SetP2AlignOperands for FastISel and just do it here. |
| MIB.addImm(0); |
| |
| if (const GlobalValue *GV = Addr.getGlobalValue()) |
| MIB.addGlobalAddress(GV, Addr.getOffset()); |
| else |
| MIB.addImm(Addr.getOffset()); |
| |
| if (Addr.isRegBase()) |
| MIB.addReg(Addr.getReg()); |
| else |
| MIB.addFrameIndex(Addr.getFI()); |
| |
| MIB.addMemOperand(MMO); |
| } |
| |
| unsigned WebAssemblyFastISel::maskI1Value(unsigned Reg, const Value *V) { |
| return zeroExtendToI32(Reg, V, MVT::i1); |
| } |
| |
| unsigned WebAssemblyFastISel::getRegForI1Value(const Value *V, |
| const BasicBlock *BB, |
| bool &Not) { |
| if (const auto *ICmp = dyn_cast<ICmpInst>(V)) |
| if (const ConstantInt *C = dyn_cast<ConstantInt>(ICmp->getOperand(1))) |
| if (ICmp->isEquality() && C->isZero() && C->getType()->isIntegerTy(32) && |
| ICmp->getParent() == BB) { |
| Not = ICmp->isTrueWhenEqual(); |
| return getRegForValue(ICmp->getOperand(0)); |
| } |
| |
| Not = false; |
| unsigned Reg = getRegForValue(V); |
| if (Reg == 0) |
| return 0; |
| return maskI1Value(Reg, V); |
| } |
| |
| unsigned WebAssemblyFastISel::zeroExtendToI32(unsigned Reg, const Value *V, |
| MVT::SimpleValueType From) { |
| if (Reg == 0) |
| return 0; |
| |
| switch (From) { |
| case MVT::i1: |
| // If the value is naturally an i1, we don't need to mask it. We only know |
| // if a value is naturally an i1 if it is definitely lowered by FastISel, |
| // not a DAG ISel fallback. |
| if (V != nullptr && isa<Argument>(V) && cast<Argument>(V)->hasZExtAttr()) |
| return copyValue(Reg); |
| break; |
| case MVT::i8: |
| case MVT::i16: |
| break; |
| case MVT::i32: |
| return copyValue(Reg); |
| default: |
| return 0; |
| } |
| |
| unsigned Imm = createResultReg(&WebAssembly::I32RegClass); |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, |
| TII.get(WebAssembly::CONST_I32), Imm) |
| .addImm(~(~uint64_t(0) << MVT(From).getSizeInBits())); |
| |
| unsigned Result = createResultReg(&WebAssembly::I32RegClass); |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, |
| TII.get(WebAssembly::AND_I32), Result) |
| .addReg(Reg) |
| .addReg(Imm); |
| |
| return Result; |
| } |
| |
| unsigned WebAssemblyFastISel::signExtendToI32(unsigned Reg, const Value *V, |
| MVT::SimpleValueType From) { |
| if (Reg == 0) |
| return 0; |
| |
| switch (From) { |
| case MVT::i1: |
| case MVT::i8: |
| case MVT::i16: |
| break; |
| case MVT::i32: |
| return copyValue(Reg); |
| default: |
| return 0; |
| } |
| |
| unsigned Imm = createResultReg(&WebAssembly::I32RegClass); |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, |
| TII.get(WebAssembly::CONST_I32), Imm) |
| .addImm(32 - MVT(From).getSizeInBits()); |
| |
| unsigned Left = createResultReg(&WebAssembly::I32RegClass); |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, |
| TII.get(WebAssembly::SHL_I32), Left) |
| .addReg(Reg) |
| .addReg(Imm); |
| |
| unsigned Right = createResultReg(&WebAssembly::I32RegClass); |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, |
| TII.get(WebAssembly::SHR_S_I32), Right) |
| .addReg(Left) |
| .addReg(Imm); |
| |
| return Right; |
| } |
| |
| unsigned WebAssemblyFastISel::zeroExtend(unsigned Reg, const Value *V, |
| MVT::SimpleValueType From, |
| MVT::SimpleValueType To) { |
| if (To == MVT::i64) { |
| if (From == MVT::i64) |
| return copyValue(Reg); |
| |
| Reg = zeroExtendToI32(Reg, V, From); |
| |
| unsigned Result = createResultReg(&WebAssembly::I64RegClass); |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, |
| TII.get(WebAssembly::I64_EXTEND_U_I32), Result) |
| .addReg(Reg); |
| return Result; |
| } |
| |
| if (To == MVT::i32) |
| return zeroExtendToI32(Reg, V, From); |
| |
| return 0; |
| } |
| |
| unsigned WebAssemblyFastISel::signExtend(unsigned Reg, const Value *V, |
| MVT::SimpleValueType From, |
| MVT::SimpleValueType To) { |
| if (To == MVT::i64) { |
| if (From == MVT::i64) |
| return copyValue(Reg); |
| |
| Reg = signExtendToI32(Reg, V, From); |
| |
| unsigned Result = createResultReg(&WebAssembly::I64RegClass); |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, |
| TII.get(WebAssembly::I64_EXTEND_S_I32), Result) |
| .addReg(Reg); |
| return Result; |
| } |
| |
| if (To == MVT::i32) |
| return signExtendToI32(Reg, V, From); |
| |
| return 0; |
| } |
| |
| unsigned WebAssemblyFastISel::getRegForUnsignedValue(const Value *V) { |
| MVT::SimpleValueType From = getSimpleType(V->getType()); |
| MVT::SimpleValueType To = getLegalType(From); |
| unsigned VReg = getRegForValue(V); |
| if (VReg == 0) |
| return 0; |
| return zeroExtend(VReg, V, From, To); |
| } |
| |
| unsigned WebAssemblyFastISel::getRegForSignedValue(const Value *V) { |
| MVT::SimpleValueType From = getSimpleType(V->getType()); |
| MVT::SimpleValueType To = getLegalType(From); |
| unsigned VReg = getRegForValue(V); |
| if (VReg == 0) |
| return 0; |
| return signExtend(VReg, V, From, To); |
| } |
| |
| unsigned WebAssemblyFastISel::getRegForPromotedValue(const Value *V, |
| bool IsSigned) { |
| return IsSigned ? getRegForSignedValue(V) : getRegForUnsignedValue(V); |
| } |
| |
| unsigned WebAssemblyFastISel::notValue(unsigned Reg) { |
| assert(MRI.getRegClass(Reg) == &WebAssembly::I32RegClass); |
| |
| unsigned NotReg = createResultReg(&WebAssembly::I32RegClass); |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, |
| TII.get(WebAssembly::EQZ_I32), NotReg) |
| .addReg(Reg); |
| return NotReg; |
| } |
| |
| unsigned WebAssemblyFastISel::copyValue(unsigned Reg) { |
| unsigned ResultReg = createResultReg(MRI.getRegClass(Reg)); |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(WebAssembly::COPY), |
| ResultReg) |
| .addReg(Reg); |
| return ResultReg; |
| } |
| |
| unsigned WebAssemblyFastISel::fastMaterializeAlloca(const AllocaInst *AI) { |
| DenseMap<const AllocaInst *, int>::iterator SI = |
| FuncInfo.StaticAllocaMap.find(AI); |
| |
| if (SI != FuncInfo.StaticAllocaMap.end()) { |
| unsigned ResultReg = |
| createResultReg(Subtarget->hasAddr64() ? &WebAssembly::I64RegClass |
| : &WebAssembly::I32RegClass); |
| unsigned Opc = |
| Subtarget->hasAddr64() ? WebAssembly::COPY_I64 : WebAssembly::COPY_I32; |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg) |
| .addFrameIndex(SI->second); |
| return ResultReg; |
| } |
| |
| return 0; |
| } |
| |
| unsigned WebAssemblyFastISel::fastMaterializeConstant(const Constant *C) { |
| if (const GlobalValue *GV = dyn_cast<GlobalValue>(C)) { |
| if (TLI.isPositionIndependent()) |
| return 0; |
| if (GV->isThreadLocal()) |
| return 0; |
| unsigned ResultReg = |
| createResultReg(Subtarget->hasAddr64() ? &WebAssembly::I64RegClass |
| : &WebAssembly::I32RegClass); |
| unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CONST_I64 |
| : WebAssembly::CONST_I32; |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg) |
| .addGlobalAddress(GV); |
| return ResultReg; |
| } |
| |
| // Let target-independent code handle it. |
| return 0; |
| } |
| |
| bool WebAssemblyFastISel::fastLowerArguments() { |
| if (!FuncInfo.CanLowerReturn) |
| return false; |
| |
| const Function *F = FuncInfo.Fn; |
| if (F->isVarArg()) |
| return false; |
| |
| if (FuncInfo.Fn->getCallingConv() == CallingConv::Swift) |
| return false; |
| |
| unsigned I = 0; |
| for (auto const &Arg : F->args()) { |
| const AttributeList &Attrs = F->getAttributes(); |
| if (Attrs.hasParamAttr(I, Attribute::ByVal) || |
| Attrs.hasParamAttr(I, Attribute::SwiftSelf) || |
| Attrs.hasParamAttr(I, Attribute::SwiftError) || |
| Attrs.hasParamAttr(I, Attribute::InAlloca) || |
| Attrs.hasParamAttr(I, Attribute::Nest)) |
| return false; |
| |
| Type *ArgTy = Arg.getType(); |
| if (ArgTy->isStructTy() || ArgTy->isArrayTy()) |
| return false; |
| if (!Subtarget->hasSIMD128() && ArgTy->isVectorTy()) |
| return false; |
| |
| unsigned Opc; |
| const TargetRegisterClass *RC; |
| switch (getSimpleType(ArgTy)) { |
| case MVT::i1: |
| case MVT::i8: |
| case MVT::i16: |
| case MVT::i32: |
| Opc = WebAssembly::ARGUMENT_i32; |
| RC = &WebAssembly::I32RegClass; |
| break; |
| case MVT::i64: |
| Opc = WebAssembly::ARGUMENT_i64; |
| RC = &WebAssembly::I64RegClass; |
| break; |
| case MVT::f32: |
| Opc = WebAssembly::ARGUMENT_f32; |
| RC = &WebAssembly::F32RegClass; |
| break; |
| case MVT::f64: |
| Opc = WebAssembly::ARGUMENT_f64; |
| RC = &WebAssembly::F64RegClass; |
| break; |
| case MVT::v16i8: |
| Opc = WebAssembly::ARGUMENT_v16i8; |
| RC = &WebAssembly::V128RegClass; |
| break; |
| case MVT::v8i16: |
| Opc = WebAssembly::ARGUMENT_v8i16; |
| RC = &WebAssembly::V128RegClass; |
| break; |
| case MVT::v4i32: |
| Opc = WebAssembly::ARGUMENT_v4i32; |
| RC = &WebAssembly::V128RegClass; |
| break; |
| case MVT::v2i64: |
| Opc = WebAssembly::ARGUMENT_v2i64; |
| RC = &WebAssembly::V128RegClass; |
| break; |
| case MVT::v4f32: |
| Opc = WebAssembly::ARGUMENT_v4f32; |
| RC = &WebAssembly::V128RegClass; |
| break; |
| case MVT::v2f64: |
| Opc = WebAssembly::ARGUMENT_v2f64; |
| RC = &WebAssembly::V128RegClass; |
| break; |
| case MVT::funcref: |
| Opc = WebAssembly::ARGUMENT_funcref; |
| RC = &WebAssembly::FUNCREFRegClass; |
| break; |
| case MVT::externref: |
| Opc = WebAssembly::ARGUMENT_externref; |
| RC = &WebAssembly::EXTERNREFRegClass; |
| break; |
| default: |
| return false; |
| } |
| unsigned ResultReg = createResultReg(RC); |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg) |
| .addImm(I); |
| updateValueMap(&Arg, ResultReg); |
| |
| ++I; |
| } |
| |
| MRI.addLiveIn(WebAssembly::ARGUMENTS); |
| |
| auto *MFI = MF->getInfo<WebAssemblyFunctionInfo>(); |
| for (auto const &Arg : F->args()) { |
| MVT::SimpleValueType ArgTy = getLegalType(getSimpleType(Arg.getType())); |
| if (ArgTy == MVT::INVALID_SIMPLE_VALUE_TYPE) { |
| MFI->clearParamsAndResults(); |
| return false; |
| } |
| MFI->addParam(ArgTy); |
| } |
| |
| if (!F->getReturnType()->isVoidTy()) { |
| MVT::SimpleValueType RetTy = |
| getLegalType(getSimpleType(F->getReturnType())); |
| if (RetTy == MVT::INVALID_SIMPLE_VALUE_TYPE) { |
| MFI->clearParamsAndResults(); |
| return false; |
| } |
| MFI->addResult(RetTy); |
| } |
| |
| return true; |
| } |
| |
| bool WebAssemblyFastISel::selectCall(const Instruction *I) { |
| const auto *Call = cast<CallInst>(I); |
| |
| // TODO: Support tail calls in FastISel |
| if (Call->isMustTailCall() || Call->isInlineAsm() || |
| Call->getFunctionType()->isVarArg()) |
| return false; |
| |
| Function *Func = Call->getCalledFunction(); |
| if (Func && Func->isIntrinsic()) |
| return false; |
| |
| if (Call->getCallingConv() == CallingConv::Swift) |
| return false; |
| |
| bool IsDirect = Func != nullptr; |
| if (!IsDirect && isa<ConstantExpr>(Call->getCalledOperand())) |
| return false; |
| |
| FunctionType *FuncTy = Call->getFunctionType(); |
| unsigned Opc = IsDirect ? WebAssembly::CALL : WebAssembly::CALL_INDIRECT; |
| bool IsVoid = FuncTy->getReturnType()->isVoidTy(); |
| unsigned ResultReg; |
| if (!IsVoid) { |
| if (!Subtarget->hasSIMD128() && Call->getType()->isVectorTy()) |
| return false; |
| |
| MVT::SimpleValueType RetTy = getSimpleType(Call->getType()); |
| switch (RetTy) { |
| case MVT::i1: |
| case MVT::i8: |
| case MVT::i16: |
| case MVT::i32: |
| ResultReg = createResultReg(&WebAssembly::I32RegClass); |
| break; |
| case MVT::i64: |
| ResultReg = createResultReg(&WebAssembly::I64RegClass); |
| break; |
| case MVT::f32: |
| ResultReg = createResultReg(&WebAssembly::F32RegClass); |
| break; |
| case MVT::f64: |
| ResultReg = createResultReg(&WebAssembly::F64RegClass); |
| break; |
| case MVT::v16i8: |
| ResultReg = createResultReg(&WebAssembly::V128RegClass); |
| break; |
| case MVT::v8i16: |
| ResultReg = createResultReg(&WebAssembly::V128RegClass); |
| break; |
| case MVT::v4i32: |
| ResultReg = createResultReg(&WebAssembly::V128RegClass); |
| break; |
| case MVT::v2i64: |
| ResultReg = createResultReg(&WebAssembly::V128RegClass); |
| break; |
| case MVT::v4f32: |
| ResultReg = createResultReg(&WebAssembly::V128RegClass); |
| break; |
| case MVT::v2f64: |
| ResultReg = createResultReg(&WebAssembly::V128RegClass); |
| break; |
| case MVT::funcref: |
| ResultReg = createResultReg(&WebAssembly::FUNCREFRegClass); |
| break; |
| case MVT::externref: |
| ResultReg = createResultReg(&WebAssembly::EXTERNREFRegClass); |
| break; |
| default: |
| return false; |
| } |
| } |
| |
| SmallVector<unsigned, 8> Args; |
| for (unsigned I = 0, E = Call->arg_size(); I < E; ++I) { |
| Value *V = Call->getArgOperand(I); |
| MVT::SimpleValueType ArgTy = getSimpleType(V->getType()); |
| if (ArgTy == MVT::INVALID_SIMPLE_VALUE_TYPE) |
| return false; |
| |
| const AttributeList &Attrs = Call->getAttributes(); |
| if (Attrs.hasParamAttr(I, Attribute::ByVal) || |
| Attrs.hasParamAttr(I, Attribute::SwiftSelf) || |
| Attrs.hasParamAttr(I, Attribute::SwiftError) || |
| Attrs.hasParamAttr(I, Attribute::InAlloca) || |
| Attrs.hasParamAttr(I, Attribute::Nest)) |
| return false; |
| |
| unsigned Reg; |
| |
| if (Attrs.hasParamAttr(I, Attribute::SExt)) |
| Reg = getRegForSignedValue(V); |
| else if (Attrs.hasParamAttr(I, Attribute::ZExt)) |
| Reg = getRegForUnsignedValue(V); |
| else |
| Reg = getRegForValue(V); |
| |
| if (Reg == 0) |
| return false; |
| |
| Args.push_back(Reg); |
| } |
| |
| unsigned CalleeReg = 0; |
| if (!IsDirect) { |
| CalleeReg = getRegForValue(Call->getCalledOperand()); |
| if (!CalleeReg) |
| return false; |
| } |
| |
| auto MIB = BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc)); |
| |
| if (!IsVoid) |
| MIB.addReg(ResultReg, RegState::Define); |
| |
| if (IsDirect) { |
| MIB.addGlobalAddress(Func); |
| } else { |
| // Placeholder for the type index. |
| MIB.addImm(0); |
| // The table into which this call_indirect indexes. |
| MCSymbolWasm *Table = WebAssembly::getOrCreateFunctionTableSymbol( |
| MF->getMMI().getContext(), Subtarget); |
| if (Subtarget->hasReferenceTypes()) { |
| MIB.addSym(Table); |
| } else { |
| // Otherwise for the MVP there is at most one table whose number is 0, but |
| // we can't write a table symbol or issue relocations. Instead we just |
| // ensure the table is live. |
| Table->setNoStrip(); |
| MIB.addImm(0); |
| } |
| // See if we must truncate the function pointer. |
| // CALL_INDIRECT takes an i32, but in wasm64 we represent function pointers |
| // as 64-bit for uniformity with other pointer types. |
| // See also: WebAssemblyISelLowering.cpp: LowerCallResults |
| if (Subtarget->hasAddr64()) { |
| auto Wrap = BuildMI(*FuncInfo.MBB, std::prev(FuncInfo.InsertPt), DbgLoc, |
| TII.get(WebAssembly::I32_WRAP_I64)); |
| unsigned Reg32 = createResultReg(&WebAssembly::I32RegClass); |
| Wrap.addReg(Reg32, RegState::Define); |
| Wrap.addReg(CalleeReg); |
| CalleeReg = Reg32; |
| } |
| } |
| |
| for (unsigned ArgReg : Args) |
| MIB.addReg(ArgReg); |
| |
| if (!IsDirect) |
| MIB.addReg(CalleeReg); |
| |
| if (!IsVoid) |
| updateValueMap(Call, ResultReg); |
| return true; |
| } |
| |
| bool WebAssemblyFastISel::selectSelect(const Instruction *I) { |
| const auto *Select = cast<SelectInst>(I); |
| |
| bool Not; |
| unsigned CondReg = |
| getRegForI1Value(Select->getCondition(), I->getParent(), Not); |
| if (CondReg == 0) |
| return false; |
| |
| unsigned TrueReg = getRegForValue(Select->getTrueValue()); |
| if (TrueReg == 0) |
| return false; |
| |
| unsigned FalseReg = getRegForValue(Select->getFalseValue()); |
| if (FalseReg == 0) |
| return false; |
| |
| if (Not) |
| std::swap(TrueReg, FalseReg); |
| |
| unsigned Opc; |
| const TargetRegisterClass *RC; |
| switch (getSimpleType(Select->getType())) { |
| case MVT::i1: |
| case MVT::i8: |
| case MVT::i16: |
| case MVT::i32: |
| Opc = WebAssembly::SELECT_I32; |
| RC = &WebAssembly::I32RegClass; |
| break; |
| case MVT::i64: |
| Opc = WebAssembly::SELECT_I64; |
| RC = &WebAssembly::I64RegClass; |
| break; |
| case MVT::f32: |
| Opc = WebAssembly::SELECT_F32; |
| RC = &WebAssembly::F32RegClass; |
| break; |
| case MVT::f64: |
| Opc = WebAssembly::SELECT_F64; |
| RC = &WebAssembly::F64RegClass; |
| break; |
| case MVT::funcref: |
| Opc = WebAssembly::SELECT_FUNCREF; |
| RC = &WebAssembly::FUNCREFRegClass; |
| break; |
| case MVT::externref: |
| Opc = WebAssembly::SELECT_EXTERNREF; |
| RC = &WebAssembly::EXTERNREFRegClass; |
| break; |
| default: |
| return false; |
| } |
| |
| unsigned ResultReg = createResultReg(RC); |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg) |
| .addReg(TrueReg) |
| .addReg(FalseReg) |
| .addReg(CondReg); |
| |
| updateValueMap(Select, ResultReg); |
| return true; |
| } |
| |
| bool WebAssemblyFastISel::selectTrunc(const Instruction *I) { |
| const auto *Trunc = cast<TruncInst>(I); |
| |
| unsigned Reg = getRegForValue(Trunc->getOperand(0)); |
| if (Reg == 0) |
| return false; |
| |
| if (Trunc->getOperand(0)->getType()->isIntegerTy(64)) { |
| unsigned Result = createResultReg(&WebAssembly::I32RegClass); |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, |
| TII.get(WebAssembly::I32_WRAP_I64), Result) |
| .addReg(Reg); |
| Reg = Result; |
| } |
| |
| updateValueMap(Trunc, Reg); |
| return true; |
| } |
| |
| bool WebAssemblyFastISel::selectZExt(const Instruction *I) { |
| const auto *ZExt = cast<ZExtInst>(I); |
| |
| const Value *Op = ZExt->getOperand(0); |
| MVT::SimpleValueType From = getSimpleType(Op->getType()); |
| MVT::SimpleValueType To = getLegalType(getSimpleType(ZExt->getType())); |
| unsigned In = getRegForValue(Op); |
| if (In == 0) |
| return false; |
| unsigned Reg = zeroExtend(In, Op, From, To); |
| if (Reg == 0) |
| return false; |
| |
| updateValueMap(ZExt, Reg); |
| return true; |
| } |
| |
| bool WebAssemblyFastISel::selectSExt(const Instruction *I) { |
| const auto *SExt = cast<SExtInst>(I); |
| |
| const Value *Op = SExt->getOperand(0); |
| MVT::SimpleValueType From = getSimpleType(Op->getType()); |
| MVT::SimpleValueType To = getLegalType(getSimpleType(SExt->getType())); |
| unsigned In = getRegForValue(Op); |
| if (In == 0) |
| return false; |
| unsigned Reg = signExtend(In, Op, From, To); |
| if (Reg == 0) |
| return false; |
| |
| updateValueMap(SExt, Reg); |
| return true; |
| } |
| |
| bool WebAssemblyFastISel::selectICmp(const Instruction *I) { |
| const auto *ICmp = cast<ICmpInst>(I); |
| |
| bool I32 = getSimpleType(ICmp->getOperand(0)->getType()) != MVT::i64; |
| unsigned Opc; |
| bool IsSigned = false; |
| switch (ICmp->getPredicate()) { |
| case ICmpInst::ICMP_EQ: |
| Opc = I32 ? WebAssembly::EQ_I32 : WebAssembly::EQ_I64; |
| break; |
| case ICmpInst::ICMP_NE: |
| Opc = I32 ? WebAssembly::NE_I32 : WebAssembly::NE_I64; |
| break; |
| case ICmpInst::ICMP_UGT: |
| Opc = I32 ? WebAssembly::GT_U_I32 : WebAssembly::GT_U_I64; |
| break; |
| case ICmpInst::ICMP_UGE: |
| Opc = I32 ? WebAssembly::GE_U_I32 : WebAssembly::GE_U_I64; |
| break; |
| case ICmpInst::ICMP_ULT: |
| Opc = I32 ? WebAssembly::LT_U_I32 : WebAssembly::LT_U_I64; |
| break; |
| case ICmpInst::ICMP_ULE: |
| Opc = I32 ? WebAssembly::LE_U_I32 : WebAssembly::LE_U_I64; |
| break; |
| case ICmpInst::ICMP_SGT: |
| Opc = I32 ? WebAssembly::GT_S_I32 : WebAssembly::GT_S_I64; |
| IsSigned = true; |
| break; |
| case ICmpInst::ICMP_SGE: |
| Opc = I32 ? WebAssembly::GE_S_I32 : WebAssembly::GE_S_I64; |
| IsSigned = true; |
| break; |
| case ICmpInst::ICMP_SLT: |
| Opc = I32 ? WebAssembly::LT_S_I32 : WebAssembly::LT_S_I64; |
| IsSigned = true; |
| break; |
| case ICmpInst::ICMP_SLE: |
| Opc = I32 ? WebAssembly::LE_S_I32 : WebAssembly::LE_S_I64; |
| IsSigned = true; |
| break; |
| default: |
| return false; |
| } |
| |
| unsigned LHS = getRegForPromotedValue(ICmp->getOperand(0), IsSigned); |
| if (LHS == 0) |
| return false; |
| |
| unsigned RHS = getRegForPromotedValue(ICmp->getOperand(1), IsSigned); |
| if (RHS == 0) |
| return false; |
| |
| unsigned ResultReg = createResultReg(&WebAssembly::I32RegClass); |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg) |
| .addReg(LHS) |
| .addReg(RHS); |
| updateValueMap(ICmp, ResultReg); |
| return true; |
| } |
| |
| bool WebAssemblyFastISel::selectFCmp(const Instruction *I) { |
| const auto *FCmp = cast<FCmpInst>(I); |
| |
| unsigned LHS = getRegForValue(FCmp->getOperand(0)); |
| if (LHS == 0) |
| return false; |
| |
| unsigned RHS = getRegForValue(FCmp->getOperand(1)); |
| if (RHS == 0) |
| return false; |
| |
| bool F32 = getSimpleType(FCmp->getOperand(0)->getType()) != MVT::f64; |
| unsigned Opc; |
| bool Not = false; |
| switch (FCmp->getPredicate()) { |
| case FCmpInst::FCMP_OEQ: |
| Opc = F32 ? WebAssembly::EQ_F32 : WebAssembly::EQ_F64; |
| break; |
| case FCmpInst::FCMP_UNE: |
| Opc = F32 ? WebAssembly::NE_F32 : WebAssembly::NE_F64; |
| break; |
| case FCmpInst::FCMP_OGT: |
| Opc = F32 ? WebAssembly::GT_F32 : WebAssembly::GT_F64; |
| break; |
| case FCmpInst::FCMP_OGE: |
| Opc = F32 ? WebAssembly::GE_F32 : WebAssembly::GE_F64; |
| break; |
| case FCmpInst::FCMP_OLT: |
| Opc = F32 ? WebAssembly::LT_F32 : WebAssembly::LT_F64; |
| break; |
| case FCmpInst::FCMP_OLE: |
| Opc = F32 ? WebAssembly::LE_F32 : WebAssembly::LE_F64; |
| break; |
| case FCmpInst::FCMP_UGT: |
| Opc = F32 ? WebAssembly::LE_F32 : WebAssembly::LE_F64; |
| Not = true; |
| break; |
| case FCmpInst::FCMP_UGE: |
| Opc = F32 ? WebAssembly::LT_F32 : WebAssembly::LT_F64; |
| Not = true; |
| break; |
| case FCmpInst::FCMP_ULT: |
| Opc = F32 ? WebAssembly::GE_F32 : WebAssembly::GE_F64; |
| Not = true; |
| break; |
| case FCmpInst::FCMP_ULE: |
| Opc = F32 ? WebAssembly::GT_F32 : WebAssembly::GT_F64; |
| Not = true; |
| break; |
| default: |
| return false; |
| } |
| |
| unsigned ResultReg = createResultReg(&WebAssembly::I32RegClass); |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg) |
| .addReg(LHS) |
| .addReg(RHS); |
| |
| if (Not) |
| ResultReg = notValue(ResultReg); |
| |
| updateValueMap(FCmp, ResultReg); |
| return true; |
| } |
| |
| bool WebAssemblyFastISel::selectBitCast(const Instruction *I) { |
| // Target-independent code can handle this, except it doesn't set the dead |
| // flag on the ARGUMENTS clobber, so we have to do that manually in order |
| // to satisfy code that expects this of isBitcast() instructions. |
| EVT VT = TLI.getValueType(DL, I->getOperand(0)->getType()); |
| EVT RetVT = TLI.getValueType(DL, I->getType()); |
| if (!VT.isSimple() || !RetVT.isSimple()) |
| return false; |
| |
| unsigned In = getRegForValue(I->getOperand(0)); |
| if (In == 0) |
| return false; |
| |
| if (VT == RetVT) { |
| // No-op bitcast. |
| updateValueMap(I, In); |
| return true; |
| } |
| |
| Register Reg = fastEmit_ISD_BITCAST_r(VT.getSimpleVT(), RetVT.getSimpleVT(), |
| In); |
| if (!Reg) |
| return false; |
| MachineBasicBlock::iterator Iter = FuncInfo.InsertPt; |
| --Iter; |
| assert(Iter->isBitcast()); |
| Iter->setPhysRegsDeadExcept(ArrayRef<Register>(), TRI); |
| updateValueMap(I, Reg); |
| return true; |
| } |
| |
| bool WebAssemblyFastISel::selectLoad(const Instruction *I) { |
| const auto *Load = cast<LoadInst>(I); |
| if (Load->isAtomic()) |
| return false; |
| if (!WebAssembly::isDefaultAddressSpace(Load->getPointerAddressSpace())) |
| return false; |
| if (!Subtarget->hasSIMD128() && Load->getType()->isVectorTy()) |
| return false; |
| |
| Address Addr; |
| if (!computeAddress(Load->getPointerOperand(), Addr)) |
| return false; |
| |
| // TODO: Fold a following sign-/zero-extend into the load instruction. |
| |
| unsigned Opc; |
| const TargetRegisterClass *RC; |
| bool A64 = Subtarget->hasAddr64(); |
| switch (getSimpleType(Load->getType())) { |
| case MVT::i1: |
| case MVT::i8: |
| Opc = A64 ? WebAssembly::LOAD8_U_I32_A64 : WebAssembly::LOAD8_U_I32_A32; |
| RC = &WebAssembly::I32RegClass; |
| break; |
| case MVT::i16: |
| Opc = A64 ? WebAssembly::LOAD16_U_I32_A64 : WebAssembly::LOAD16_U_I32_A32; |
| RC = &WebAssembly::I32RegClass; |
| break; |
| case MVT::i32: |
| Opc = A64 ? WebAssembly::LOAD_I32_A64 : WebAssembly::LOAD_I32_A32; |
| RC = &WebAssembly::I32RegClass; |
| break; |
| case MVT::i64: |
| Opc = A64 ? WebAssembly::LOAD_I64_A64 : WebAssembly::LOAD_I64_A32; |
| RC = &WebAssembly::I64RegClass; |
| break; |
| case MVT::f32: |
| Opc = A64 ? WebAssembly::LOAD_F32_A64 : WebAssembly::LOAD_F32_A32; |
| RC = &WebAssembly::F32RegClass; |
| break; |
| case MVT::f64: |
| Opc = A64 ? WebAssembly::LOAD_F64_A64 : WebAssembly::LOAD_F64_A32; |
| RC = &WebAssembly::F64RegClass; |
| break; |
| default: |
| return false; |
| } |
| |
| materializeLoadStoreOperands(Addr); |
| |
| unsigned ResultReg = createResultReg(RC); |
| auto MIB = BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), |
| ResultReg); |
| |
| addLoadStoreOperands(Addr, MIB, createMachineMemOperandFor(Load)); |
| |
| updateValueMap(Load, ResultReg); |
| return true; |
| } |
| |
| bool WebAssemblyFastISel::selectStore(const Instruction *I) { |
| const auto *Store = cast<StoreInst>(I); |
| if (Store->isAtomic()) |
| return false; |
| if (!WebAssembly::isDefaultAddressSpace(Store->getPointerAddressSpace())) |
| return false; |
| if (!Subtarget->hasSIMD128() && |
| Store->getValueOperand()->getType()->isVectorTy()) |
| return false; |
| |
| Address Addr; |
| if (!computeAddress(Store->getPointerOperand(), Addr)) |
| return false; |
| |
| unsigned Opc; |
| bool VTIsi1 = false; |
| bool A64 = Subtarget->hasAddr64(); |
| switch (getSimpleType(Store->getValueOperand()->getType())) { |
| case MVT::i1: |
| VTIsi1 = true; |
| LLVM_FALLTHROUGH; |
| case MVT::i8: |
| Opc = A64 ? WebAssembly::STORE8_I32_A64 : WebAssembly::STORE8_I32_A32; |
| break; |
| case MVT::i16: |
| Opc = A64 ? WebAssembly::STORE16_I32_A64 : WebAssembly::STORE16_I32_A32; |
| break; |
| case MVT::i32: |
| Opc = A64 ? WebAssembly::STORE_I32_A64 : WebAssembly::STORE_I32_A32; |
| break; |
| case MVT::i64: |
| Opc = A64 ? WebAssembly::STORE_I64_A64 : WebAssembly::STORE_I64_A32; |
| break; |
| case MVT::f32: |
| Opc = A64 ? WebAssembly::STORE_F32_A64 : WebAssembly::STORE_F32_A32; |
| break; |
| case MVT::f64: |
| Opc = A64 ? WebAssembly::STORE_F64_A64 : WebAssembly::STORE_F64_A32; |
| break; |
| default: |
| return false; |
| } |
| |
| materializeLoadStoreOperands(Addr); |
| |
| unsigned ValueReg = getRegForValue(Store->getValueOperand()); |
| if (ValueReg == 0) |
| return false; |
| if (VTIsi1) |
| ValueReg = maskI1Value(ValueReg, Store->getValueOperand()); |
| |
| auto MIB = BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc)); |
| |
| addLoadStoreOperands(Addr, MIB, createMachineMemOperandFor(Store)); |
| |
| MIB.addReg(ValueReg); |
| return true; |
| } |
| |
| bool WebAssemblyFastISel::selectBr(const Instruction *I) { |
| const auto *Br = cast<BranchInst>(I); |
| if (Br->isUnconditional()) { |
| MachineBasicBlock *MSucc = FuncInfo.MBBMap[Br->getSuccessor(0)]; |
| fastEmitBranch(MSucc, Br->getDebugLoc()); |
| return true; |
| } |
| |
| MachineBasicBlock *TBB = FuncInfo.MBBMap[Br->getSuccessor(0)]; |
| MachineBasicBlock *FBB = FuncInfo.MBBMap[Br->getSuccessor(1)]; |
| |
| bool Not; |
| unsigned CondReg = getRegForI1Value(Br->getCondition(), Br->getParent(), Not); |
| if (CondReg == 0) |
| return false; |
| |
| unsigned Opc = WebAssembly::BR_IF; |
| if (Not) |
| Opc = WebAssembly::BR_UNLESS; |
| |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc)) |
| .addMBB(TBB) |
| .addReg(CondReg); |
| |
| finishCondBranch(Br->getParent(), TBB, FBB); |
| return true; |
| } |
| |
| bool WebAssemblyFastISel::selectRet(const Instruction *I) { |
| if (!FuncInfo.CanLowerReturn) |
| return false; |
| |
| const auto *Ret = cast<ReturnInst>(I); |
| |
| if (Ret->getNumOperands() == 0) { |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, |
| TII.get(WebAssembly::RETURN)); |
| return true; |
| } |
| |
| // TODO: support multiple return in FastISel |
| if (Ret->getNumOperands() > 1) |
| return false; |
| |
| Value *RV = Ret->getOperand(0); |
| if (!Subtarget->hasSIMD128() && RV->getType()->isVectorTy()) |
| return false; |
| |
| switch (getSimpleType(RV->getType())) { |
| case MVT::i1: |
| case MVT::i8: |
| case MVT::i16: |
| case MVT::i32: |
| case MVT::i64: |
| case MVT::f32: |
| case MVT::f64: |
| case MVT::v16i8: |
| case MVT::v8i16: |
| case MVT::v4i32: |
| case MVT::v2i64: |
| case MVT::v4f32: |
| case MVT::v2f64: |
| case MVT::funcref: |
| case MVT::externref: |
| break; |
| default: |
| return false; |
| } |
| |
| unsigned Reg; |
| if (FuncInfo.Fn->getAttributes().hasRetAttr(Attribute::SExt)) |
| Reg = getRegForSignedValue(RV); |
| else if (FuncInfo.Fn->getAttributes().hasRetAttr(Attribute::ZExt)) |
| Reg = getRegForUnsignedValue(RV); |
| else |
| Reg = getRegForValue(RV); |
| |
| if (Reg == 0) |
| return false; |
| |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, |
| TII.get(WebAssembly::RETURN)) |
| .addReg(Reg); |
| return true; |
| } |
| |
| bool WebAssemblyFastISel::selectUnreachable(const Instruction *I) { |
| BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, |
| TII.get(WebAssembly::UNREACHABLE)); |
| return true; |
| } |
| |
| bool WebAssemblyFastISel::fastSelectInstruction(const Instruction *I) { |
| switch (I->getOpcode()) { |
| case Instruction::Call: |
| if (selectCall(I)) |
| return true; |
| break; |
| case Instruction::Select: |
| return selectSelect(I); |
| case Instruction::Trunc: |
| return selectTrunc(I); |
| case Instruction::ZExt: |
| return selectZExt(I); |
| case Instruction::SExt: |
| return selectSExt(I); |
| case Instruction::ICmp: |
| return selectICmp(I); |
| case Instruction::FCmp: |
| return selectFCmp(I); |
| case Instruction::BitCast: |
| return selectBitCast(I); |
| case Instruction::Load: |
| return selectLoad(I); |
| case Instruction::Store: |
| return selectStore(I); |
| case Instruction::Br: |
| return selectBr(I); |
| case Instruction::Ret: |
| return selectRet(I); |
| case Instruction::Unreachable: |
| return selectUnreachable(I); |
| default: |
| break; |
| } |
| |
| // Fall back to target-independent instruction selection. |
| return selectOperator(I, I->getOpcode()); |
| } |
| |
| FastISel *WebAssembly::createFastISel(FunctionLoweringInfo &FuncInfo, |
| const TargetLibraryInfo *LibInfo) { |
| return new WebAssemblyFastISel(FuncInfo, LibInfo); |
| } |