diff --git a/lib/Target/WebAssembly/WebAssemblyFastISel.cpp b/lib/Target/WebAssembly/WebAssemblyFastISel.cpp index ee87e1bb4f0..7bfa4074849 100644 --- a/lib/Target/WebAssembly/WebAssemblyFastISel.cpp +++ b/lib/Target/WebAssembly/WebAssemblyFastISel.cpp @@ -12,10 +12,13 @@ /// class. Some of the target-specific code is generated by tablegen in the file /// WebAssemblyGenFastISel.inc, which is #included here. /// +/// TODO: kill flags +/// //===----------------------------------------------------------------------===// #include "WebAssembly.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" +#include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblySubtarget.h" #include "WebAssemblyTargetMachine.h" #include "llvm/Analysis/BranchProbabilityInfo.h" @@ -104,10 +107,12 @@ private: case MVT::i1: case MVT::i8: case MVT::i16: - case MVT::i32: return MVT::i32; + case MVT::i32: case MVT::i64: - return MVT::i64; + case MVT::f32: + case MVT::f64: + return VT; default: break; } @@ -118,7 +123,7 @@ private: void addLoadStoreOperands(const Address &Addr, const MachineInstrBuilder &MIB, MachineMemOperand *MMO); unsigned maskI1Value(unsigned Reg, const Value *V); - unsigned getRegForI1Value(const Value *V); + unsigned getRegForI1Value(const Value *V, bool &Not); unsigned zeroExtendToI32(unsigned Reg, const Value *V, MVT::SimpleValueType From); unsigned signExtendToI32(unsigned Reg, const Value *V, @@ -133,12 +138,17 @@ private: 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); @@ -221,7 +231,7 @@ bool WebAssemblyFastISel::computeAddress(const Value *Obj, Address &Addr) { uint64_t TmpOffset = Addr.getOffset(); // 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); + 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 = dyn_cast(*GTI)) { @@ -236,6 +246,11 @@ bool WebAssemblyFastISel::computeAddress(const Value *Obj, Address &Addr) { 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. + Addr.setReg(getRegForValue(Op)); + break; + } if (canFoldAddIntoGEP(U, Op)) { // A compatible add with a constant operand. Fold the constant. ConstantInt *CI = @@ -347,7 +362,20 @@ unsigned WebAssemblyFastISel::maskI1Value(unsigned Reg, const Value *V) { return zeroExtendToI32(Reg, V, MVT::i1); } -unsigned WebAssemblyFastISel::getRegForI1Value(const Value *V) { +unsigned WebAssemblyFastISel::getRegForI1Value(const Value *V, bool &Not) { + if (const ICmpInst *ICmp = dyn_cast(V)) + if (const ConstantInt *C = dyn_cast(ICmp->getOperand(1))) + if (ICmp->isEquality() && C->isZero() && C->getType()->isIntegerTy(32)) { + Not = ICmp->isTrueWhenEqual(); + return getRegForValue(ICmp->getOperand(0)); + } + + if (BinaryOperator::isNot(V)) { + Not = true; + return getRegForValue(BinaryOperator::getNotArgument(V)); + } + + Not = false; return maskI1Value(getRegForValue(V), V); } @@ -357,11 +385,16 @@ unsigned WebAssemblyFastISel::zeroExtendToI32(unsigned Reg, const Value *V, case MVT::i1: // If the value is naturally an i1, we don't need to mask it. // TODO: Recursively examine selects, phis, and, or, xor, constants. - if (From == MVT::i1 && V != nullptr && isa(V)) - return Reg; + if (From == MVT::i1 && V != nullptr) { + if (isa(V) || + (isa(V) && cast(V)->hasZExtAttr())) + return copyValue(Reg); + } + case MVT::i8: + case MVT::i16: break; case MVT::i32: - return Reg; + return copyValue(Reg); default: return 0; } @@ -388,9 +421,9 @@ unsigned WebAssemblyFastISel::signExtendToI32(unsigned Reg, const Value *V, case MVT::i16: break; case MVT::i32: - return Reg; + return copyValue(Reg); default: - return false; + return 0; } unsigned Imm = createResultReg(&WebAssembly::I32RegClass); @@ -418,7 +451,7 @@ unsigned WebAssemblyFastISel::zeroExtend(unsigned Reg, const Value *V, MVT::SimpleValueType To) { if (To == MVT::i64) { if (From == MVT::i64) - return Reg; + return copyValue(Reg); Reg = zeroExtendToI32(Reg, V, From); @@ -429,21 +462,6 @@ unsigned WebAssemblyFastISel::zeroExtend(unsigned Reg, const Value *V, return Result; } - switch (From) { - case MVT::i1: - // If the value is naturally an i1, we don't need to mask it. - // TODO: Recursively examine selects, phis, and, or, xor, constants. - if (From == MVT::i1 && V != nullptr && isa(V)) - return Reg; - case MVT::i8: - case MVT::i16: - break; - case MVT::i32: - return Reg; - default: - return 0; - } - return zeroExtendToI32(Reg, V, From); } @@ -452,7 +470,7 @@ unsigned WebAssemblyFastISel::signExtend(unsigned Reg, const Value *V, MVT::SimpleValueType To) { if (To == MVT::i64) { if (From == MVT::i64) - return Reg; + return copyValue(Reg); Reg = signExtendToI32(Reg, V, From); @@ -463,17 +481,6 @@ unsigned WebAssemblyFastISel::signExtend(unsigned Reg, const Value *V, return Result; } - switch (From) { - case MVT::i1: - case MVT::i8: - case MVT::i16: - break; - case MVT::i32: - return Reg; - default: - return false; - } - return signExtendToI32(Reg, V, From); } @@ -496,6 +503,8 @@ unsigned WebAssemblyFastISel::getRegForPromotedValue(const Value *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) @@ -503,6 +512,14 @@ unsigned WebAssemblyFastISel::notValue(unsigned 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::iterator SI = FuncInfo.StaticAllocaMap.find(AI); @@ -524,25 +541,259 @@ unsigned WebAssemblyFastISel::fastMaterializeAlloca(const AllocaInst *AI) { unsigned WebAssemblyFastISel::fastMaterializeConstant(const Constant *C) { if (const GlobalValue *GV = dyn_cast(C)) { - unsigned Reg = createResultReg(Subtarget->hasAddr64() ? - &WebAssembly::I64RegClass : - &WebAssembly::I32RegClass); + 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), Reg) + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg) .addGlobalAddress(GV); - return Reg; + 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; + + unsigned i = 0; + for (auto const &Arg : F->args()) { + const AttributeSet &Attrs = F->getAttributes(); + if (Attrs.hasAttribute(i+1, Attribute::ByVal) || + Attrs.hasAttribute(i+1, Attribute::SwiftSelf) || + Attrs.hasAttribute(i+1, Attribute::SwiftError) || + Attrs.hasAttribute(i+1, Attribute::InAlloca) || + Attrs.hasAttribute(i+1, Attribute::Nest)) + return false; + + Type *ArgTy = Arg.getType(); + if (ArgTy->isStructTy() || ArgTy->isArrayTy() || 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; + 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(); + for (auto const &Arg : F->args()) + MFI->addParam(getLegalType(getSimpleType(Arg.getType()))); + + return true; +} + +bool WebAssemblyFastISel::selectCall(const Instruction *I) { + const CallInst *Call = cast(I); + + if (Call->isMustTailCall() || Call->isInlineAsm() || + Call->getFunctionType()->isVarArg()) + return false; + + Function *Func = Call->getCalledFunction(); + if (Func && Func->isIntrinsic()) + return false; + + FunctionType *FuncTy = Call->getFunctionType(); + unsigned Opc; + bool IsDirect = Func != nullptr; + bool IsVoid = FuncTy->getReturnType()->isVoidTy(); + unsigned ResultReg; + if (IsVoid) { + Opc = IsDirect ? WebAssembly::CALL_VOID : WebAssembly::CALL_INDIRECT_VOID; + } else { + MVT::SimpleValueType RetTy = getSimpleType(Call->getType()); + switch (RetTy) { + case MVT::i1: + case MVT::i8: + case MVT::i16: + case MVT::i32: + Opc = IsDirect ? WebAssembly::CALL_I32 : WebAssembly::CALL_INDIRECT_I32; + ResultReg = createResultReg(&WebAssembly::I32RegClass); + break; + case MVT::i64: + Opc = IsDirect ? WebAssembly::CALL_I64 : WebAssembly::CALL_INDIRECT_I64; + ResultReg = createResultReg(&WebAssembly::I64RegClass); + break; + case MVT::f32: + Opc = IsDirect ? WebAssembly::CALL_F32 : WebAssembly::CALL_INDIRECT_F32; + ResultReg = createResultReg(&WebAssembly::F32RegClass); + break; + case MVT::f64: + Opc = IsDirect ? WebAssembly::CALL_F64 : WebAssembly::CALL_INDIRECT_F64; + ResultReg = createResultReg(&WebAssembly::F64RegClass); + break; + default: + return false; + } + } + + SmallVector Args; + for (unsigned i = 0, e = Call->getNumArgOperands(); i < e; ++i) { + Value *V = Call->getArgOperand(i); + MVT::SimpleValueType ArgTy = getSimpleType(V->getType()); + if (ArgTy == MVT::INVALID_SIMPLE_VALUE_TYPE) + return false; + + const AttributeSet &Attrs = Call->getAttributes(); + if (Attrs.hasAttribute(i+1, Attribute::ByVal) || + Attrs.hasAttribute(i+1, Attribute::SwiftSelf) || + Attrs.hasAttribute(i+1, Attribute::SwiftError) || + Attrs.hasAttribute(i+1, Attribute::InAlloca) || + Attrs.hasAttribute(i+1, Attribute::Nest)) + return false; + + unsigned Reg; + + if (Attrs.hasAttribute(i+1, Attribute::SExt)) + Reg = getRegForSignedValue(V); + else if (Attrs.hasAttribute(i+1, Attribute::ZExt)) + Reg = getRegForUnsignedValue(V); + else + Reg = getRegForValue(V); + + if (Reg == 0) + return false; + + Args.push_back(Reg); + } + + auto MIB = BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc)); + + if (!IsVoid) + MIB.addReg(ResultReg, RegState::Define); + + if (IsDirect) + MIB.addGlobalAddress(Func); + else + MIB.addReg(getRegForValue(Call->getCalledValue())); + + for (unsigned ArgReg : Args) + MIB.addReg(ArgReg); + + if (!IsVoid) + updateValueMap(Call, ResultReg); + return true; +} + +bool WebAssemblyFastISel::selectSelect(const Instruction *I) { + const SelectInst *Select = cast(I); + + bool Not; + unsigned CondReg = getRegForI1Value(Select->getCondition(), 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; + 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 TruncInst *Trunc = cast(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 ZExtInst *ZExt = cast(I); - unsigned Reg = getRegForUnsignedValue(ZExt->getOperand(0)); + const Value *Op = ZExt->getOperand(0); + MVT::SimpleValueType From = getSimpleType(Op->getType()); + MVT::SimpleValueType To = getLegalType(getSimpleType(ZExt->getType())); + unsigned Reg = zeroExtend(getRegForValue(Op), Op, From, To); if (Reg == 0) return false; @@ -553,7 +804,10 @@ bool WebAssemblyFastISel::selectZExt(const Instruction *I) { bool WebAssemblyFastISel::selectSExt(const Instruction *I) { const SExtInst *SExt = cast(I); - unsigned Reg = getRegForSignedValue(SExt->getOperand(0)); + const Value *Op = SExt->getOperand(0); + MVT::SimpleValueType From = getSimpleType(Op->getType()); + MVT::SimpleValueType To = getLegalType(getSimpleType(SExt->getType())); + unsigned Reg = signExtend(getRegForValue(Op), Op, From, To); if (Reg == 0) return false; @@ -694,6 +948,13 @@ bool WebAssemblyFastISel::selectBitCast(const Instruction *I) { EVT RetVT = TLI.getValueType(DL, I->getType()); if (!VT.isSimple() || !RetVT.isSimple()) return false; + + if (VT == RetVT) { + // No-op bitcast. + updateValueMap(I, getRegForValue(I->getOperand(0))); + return true; + } + unsigned Reg = fastEmit_ISD_BITCAST_r(VT.getSimpleVT(), RetVT.getSimpleVT(), getRegForValue(I->getOperand(0)), I->getOperand(0)->hasOneUse()); @@ -831,14 +1092,13 @@ bool WebAssemblyFastISel::selectBr(const Instruction *I) { MachineBasicBlock *TBB = FuncInfo.MBBMap[Br->getSuccessor(0)]; MachineBasicBlock *FBB = FuncInfo.MBBMap[Br->getSuccessor(1)]; - Value *Cond = Br->getCondition(); - unsigned Opc = WebAssembly::BR_IF; - if (BinaryOperator::isNot(Cond)) { - Cond = BinaryOperator::getNotArgument(Cond); - Opc = WebAssembly::BR_UNLESS; - } + bool Not; + unsigned CondReg = getRegForI1Value(Br->getCondition(), Not); + + unsigned Opc = WebAssembly::BR_IF; + if (Not) + Opc = WebAssembly::BR_UNLESS; - unsigned CondReg = getRegForI1Value(Cond); BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc)) .addMBB(TBB) .addReg(CondReg); @@ -874,7 +1134,14 @@ bool WebAssemblyFastISel::selectRet(const Instruction *I) { default: return false; } - unsigned Reg = getRegForValue(RV); + unsigned Reg; + if (FuncInfo.Fn->getAttributes().hasAttribute(0, Attribute::SExt)) + Reg = getRegForSignedValue(RV); + else if (FuncInfo.Fn->getAttributes().hasAttribute(0, Attribute::ZExt)) + Reg = getRegForUnsignedValue(RV); + else + Reg = getRegForValue(RV); + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc)).addReg(Reg); return true; } @@ -887,6 +1154,12 @@ bool WebAssemblyFastISel::selectUnreachable(const Instruction *I) { 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); diff --git a/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp b/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp index 8a00d544f48..1e6d5f079ac 100644 --- a/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp +++ b/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp @@ -297,13 +297,14 @@ static MachineInstr *MoveAndTeeForMultiUse( unsigned NewReg = MRI.createVirtualRegister(RegClass); unsigned TeeReg = MRI.createVirtualRegister(RegClass); unsigned DefReg = MRI.createVirtualRegister(RegClass); + MachineOperand &DefMO = Def->getOperand(0); MRI.replaceRegWith(Reg, NewReg); MachineInstr *Tee = BuildMI(MBB, Insert, Insert->getDebugLoc(), TII->get(GetTeeLocalOpcode(RegClass)), TeeReg) .addReg(NewReg, RegState::Define) - .addReg(DefReg); + .addReg(DefReg, getUndefRegState(DefMO.isDead())); Op.setReg(TeeReg); - Def->getOperand(0).setReg(DefReg); + DefMO.setReg(DefReg); LIS.InsertMachineInstrInMaps(*Tee); LIS.removeInterval(Reg); LIS.createAndComputeVirtRegInterval(NewReg); diff --git a/lib/Target/WebAssembly/WebAssemblyStoreResults.cpp b/lib/Target/WebAssembly/WebAssemblyStoreResults.cpp index b3da7861f29..1e9a773ae62 100644 --- a/lib/Target/WebAssembly/WebAssemblyStoreResults.cpp +++ b/lib/Target/WebAssembly/WebAssemblyStoreResults.cpp @@ -115,9 +115,11 @@ static bool ReplaceDominatedUses(MachineBasicBlock &MBB, MachineInstr &MI, O.setReg(ToReg); // If the store's def was previously dead, it is no longer. - MI.getOperand(0).setIsDead(false); + if (!O.isUndef()) { + MI.getOperand(0).setIsDead(false); - Indices.push_back(WhereIdx.getRegSlot()); + Indices.push_back(WhereIdx.getRegSlot()); + } } if (Changed) { diff --git a/test/CodeGen/WebAssembly/call.ll b/test/CodeGen/WebAssembly/call.ll index 9f0be794798..9e05da03588 100644 --- a/test/CodeGen/WebAssembly/call.ll +++ b/test/CodeGen/WebAssembly/call.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -fast-isel -fast-isel-abort=1 | FileCheck %s ; Test that basic call operations assemble as expected. diff --git a/test/CodeGen/WebAssembly/select.ll b/test/CodeGen/WebAssembly/select.ll index e198a0bdf21..6e26da59496 100644 --- a/test/CodeGen/WebAssembly/select.ll +++ b/test/CodeGen/WebAssembly/select.ll @@ -1,5 +1,5 @@ ; RUN: llc < %s -asm-verbose=false | FileCheck %s -; RUN: llc < %s -asm-verbose=false -fast-isel | FileCheck %s +; RUN: llc < %s -asm-verbose=false -fast-isel -fast-isel-abort=1 | FileCheck %s ; Test that wasm select instruction is selected from LLVM select instruction.