[WebAssembly] Fast-isel support for calls, arguments, and selects.

llvm-svn: 269273
This commit is contained in:
Dan Gohman 2016-05-12 04:19:09 +00:00
parent 1fb10e846a
commit 33e694a807
5 changed files with 335 additions and 58 deletions

View File

@ -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<StructType>(*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<ICmpInst>(V))
if (const ConstantInt *C = dyn_cast<ConstantInt>(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<CmpInst>(V))
return Reg;
if (From == MVT::i1 && V != nullptr) {
if (isa<CmpInst>(V) ||
(isa<Argument>(V) && cast<Argument>(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<CmpInst>(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<const AllocaInst *, int>::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<GlobalValue>(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<WebAssemblyFunctionInfo>();
for (auto const &Arg : F->args())
MFI->addParam(getLegalType(getSimpleType(Arg.getType())));
return true;
}
bool WebAssemblyFastISel::selectCall(const Instruction *I) {
const CallInst *Call = cast<CallInst>(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<unsigned, 8> 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<SelectInst>(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<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 ZExtInst *ZExt = cast<ZExtInst>(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<SExtInst>(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);

View File

@ -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);

View File

@ -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) {

View File

@ -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.

View File

@ -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.