X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=lib%2FTarget%2FWebAssembly%2FWebAssemblyISelLowering.cpp;h=4883d83647db5e0b255d060270be82f1bc8af986;hb=ca9fa31c8cddf8adbe019dbc12871a7b5703d0a4;hp=5a3e02133f5a4f7ed523f29eb0a83a0f70729d73;hpb=fe3415b2af46d76f92ef21c345b07defa763dba8;p=oota-llvm.git diff --git a/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index 5a3e02133f5..4883d83647d 100644 --- a/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -118,6 +118,13 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom); setOperationAction(ISD::JumpTable, MVTPtr, Custom); + // Take the default expansion for va_arg, va_copy, and va_end. There is no + // default action for va_start, so we do that custom. + setOperationAction(ISD::VASTART, MVT::Other, Custom); + setOperationAction(ISD::VAARG, MVT::Other, Expand); + setOperationAction(ISD::VACOPY, MVT::Other, Expand); + setOperationAction(ISD::VAEND, MVT::Other, Expand); + for (auto T : {MVT::f32, MVT::f64}) { // Don't expand the floating-point types to constant pools. setOperationAction(ISD::ConstantFP, T, Legal); @@ -126,12 +133,13 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( ISD::SETULT, ISD::SETULE, ISD::SETUGT, ISD::SETUGE}) setCondCodeAction(CC, T, Expand); // Expand floating-point library function operators. - for (auto Op : {ISD::FSIN, ISD::FCOS, ISD::FSINCOS, ISD::FPOWI, ISD::FPOW}) + for (auto Op : {ISD::FSIN, ISD::FCOS, ISD::FSINCOS, ISD::FPOWI, ISD::FPOW, + ISD::FREM}) setOperationAction(Op, T, Expand); // Note supported floating-point library function operators that otherwise // default to expand. - for (auto Op : {ISD::FCEIL, ISD::FFLOOR, ISD::FTRUNC, ISD::FNEARBYINT, - ISD::FRINT}) + for (auto Op : + {ISD::FCEIL, ISD::FFLOOR, ISD::FTRUNC, ISD::FNEARBYINT, ISD::FRINT}) setOperationAction(Op, T, Legal); // Support minnan and maxnan, which otherwise default to expand. setOperationAction(ISD::FMINNAN, T, Legal); @@ -140,11 +148,11 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( for (auto T : {MVT::i32, MVT::i64}) { // Expand unavailable integer operations. - for (auto Op : {ISD::BSWAP, ISD::ROTL, ISD::ROTR, - ISD::SMUL_LOHI, ISD::UMUL_LOHI, - ISD::MULHS, ISD::MULHU, ISD::SDIVREM, ISD::UDIVREM, - ISD::SHL_PARTS, ISD::SRA_PARTS, ISD::SRL_PARTS, - ISD::ADDC, ISD::ADDE, ISD::SUBC, ISD::SUBE}) { + for (auto Op : + {ISD::BSWAP, ISD::ROTL, ISD::ROTR, ISD::SMUL_LOHI, ISD::UMUL_LOHI, + ISD::MULHS, ISD::MULHU, ISD::SDIVREM, ISD::UDIVREM, ISD::SHL_PARTS, + ISD::SRA_PARTS, ISD::SRL_PARTS, ISD::ADDC, ISD::ADDE, ISD::SUBC, + ISD::SUBE}) { setOperationAction(Op, T, Expand); } } @@ -187,13 +195,12 @@ FastISel *WebAssemblyTargetLowering::createFastISel( } bool WebAssemblyTargetLowering::isOffsetFoldingLegal( - const GlobalAddressSDNode *GA) const { - // The WebAssembly target doesn't support folding offsets into global - // addresses. - return false; + const GlobalAddressSDNode * /*GA*/) const { + // All offsets can be folded. + return true; } -MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout &DL, +MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout & /*DL*/, EVT VT) const { return VT.getSimpleVT(); } @@ -220,7 +227,14 @@ WebAssemblyTargetLowering::getRegForInlineAsmConstraint( if (Constraint.size() == 1) { switch (Constraint[0]) { case 'r': - return std::make_pair(0U, &WebAssembly::I32RegClass); + assert(VT != MVT::iPTR && "Pointer MVT not expected here"); + if (VT.isInteger() && !VT.isVector()) { + if (VT.getSizeInBits() <= 32) + return std::make_pair(0U, &WebAssembly::I32RegClass); + if (VT.getSizeInBits() <= 64) + return std::make_pair(0U, &WebAssembly::I64RegClass); + } + break; default: break; } @@ -253,6 +267,19 @@ static void fail(SDLoc DL, SelectionDAG &DAG, const char *msg) { DiagnosticInfoUnsupported(DL, *MF.getFunction(), msg, SDValue())); } +// Test whether the given calling convention is supported. +static bool CallingConvSupported(CallingConv::ID CallConv) { + // We currently support the language-independent target-independent + // conventions. We don't yet have a way to annotate calls with properties like + // "cold", and we don't have any call-clobbered registers, so these are mostly + // all handled the same. + return CallConv == CallingConv::C || CallConv == CallingConv::Fast || + CallConv == CallingConv::Cold || + CallConv == CallingConv::PreserveMost || + CallConv == CallingConv::PreserveAll || + CallConv == CallingConv::CXX_FAST_TLS; +} + SDValue WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, SmallVectorImpl &InVals) const { @@ -263,9 +290,7 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, MachineFunction &MF = DAG.getMachineFunction(); CallingConv::ID CallConv = CLI.CallConv; - if (CallConv != CallingConv::C && - CallConv != CallingConv::Fast && - CallConv != CallingConv::Cold) + if (!CallingConvSupported(CallConv)) fail(DL, DAG, "WebAssembly doesn't support language-specific or target-specific " "calling conventions yet"); @@ -286,28 +311,97 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, if (Ins.size() > 1) fail(DL, DAG, "WebAssembly doesn't support more than 1 returned value yet"); + SmallVectorImpl &Outs = CLI.Outs; + for (const ISD::OutputArg &Out : Outs) { + assert(!Out.Flags.isByVal() && "byval is not valid for return values"); + assert(!Out.Flags.isNest() && "nest is not valid for return values"); + if (Out.Flags.isInAlloca()) + fail(DL, DAG, "WebAssembly hasn't implemented inalloca results"); + if (Out.Flags.isInConsecutiveRegs()) + fail(DL, DAG, "WebAssembly hasn't implemented cons regs results"); + if (Out.Flags.isInConsecutiveRegsLast()) + fail(DL, DAG, "WebAssembly hasn't implemented cons regs last results"); + } + bool IsVarArg = CLI.IsVarArg; - if (IsVarArg) - fail(DL, DAG, "WebAssembly doesn't support varargs yet"); + unsigned NumFixedArgs = CLI.NumFixedArgs; + auto PtrVT = getPointerTy(MF.getDataLayout()); // Analyze operands of the call, assigning locations to each operand. SmallVector ArgLocs; CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); - unsigned NumBytes = CCInfo.getNextStackOffset(); - auto PtrVT = getPointerTy(MF.getDataLayout()); - auto Zero = DAG.getConstant(0, DL, PtrVT, true); + if (IsVarArg) { + // Outgoing non-fixed arguments are placed at the top of the stack. First + // compute their offsets and the total amount of argument stack space + // needed. + for (SDValue Arg : + make_range(OutVals.begin() + NumFixedArgs, OutVals.end())) { + EVT VT = Arg.getValueType(); + assert(VT != MVT::iPTR && "Legalized args should be concrete"); + Type *Ty = VT.getTypeForEVT(*DAG.getContext()); + unsigned Offset = + CCInfo.AllocateStack(MF.getDataLayout().getTypeAllocSize(Ty), + MF.getDataLayout().getABITypeAlignment(Ty)); + CCInfo.addLoc(CCValAssign::getMem(ArgLocs.size(), VT.getSimpleVT(), + Offset, VT.getSimpleVT(), + CCValAssign::Full)); + } + } + + unsigned NumBytes = CCInfo.getAlignedCallFrameSize(); + auto NB = DAG.getConstant(NumBytes, DL, PtrVT, true); Chain = DAG.getCALLSEQ_START(Chain, NB, DL); + if (IsVarArg) { + // For non-fixed arguments, next emit stores to store the argument values + // to the stack at the offsets computed above. + SDValue SP = DAG.getCopyFromReg( + Chain, DL, getStackPointerRegisterToSaveRestore(), PtrVT); + unsigned ValNo = 0; + SmallVector Chains; + for (SDValue Arg : + make_range(OutVals.begin() + NumFixedArgs, OutVals.end())) { + assert(ArgLocs[ValNo].getValNo() == ValNo && + "ArgLocs should remain in order and only hold varargs args"); + unsigned Offset = ArgLocs[ValNo++].getLocMemOffset(); + SDValue Add = DAG.getNode(ISD::ADD, DL, PtrVT, SP, + DAG.getConstant(Offset, DL, PtrVT)); + Chains.push_back(DAG.getStore(Chain, DL, Arg, Add, + MachinePointerInfo::getStack(MF, Offset), + false, false, 0)); + } + if (!Chains.empty()) + Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Chains); + } + + // Compute the operands for the CALLn node. SmallVector Ops; Ops.push_back(Chain); Ops.push_back(Callee); - Ops.append(OutVals.begin(), OutVals.end()); + + // Add all fixed arguments. Note that for non-varargs calls, NumFixedArgs + // isn't reliable. + Ops.append(OutVals.begin(), + IsVarArg ? OutVals.begin() + NumFixedArgs : OutVals.end()); SmallVector Tys; - for (const auto &In : Ins) + for (const auto &In : Ins) { + if (In.Flags.isByVal()) + fail(DL, DAG, "WebAssembly hasn't implemented byval arguments"); + if (In.Flags.isInAlloca()) + fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments"); + if (In.Flags.isNest()) + fail(DL, DAG, "WebAssembly hasn't implemented nest arguments"); + if (In.Flags.isInConsecutiveRegs()) + fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments"); + if (In.Flags.isInConsecutiveRegsLast()) + fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments"); + // Ignore In.getOrigAlign() because all our arguments are passed in + // registers. Tys.push_back(In.VT); + } Tys.push_back(MVT::Other); SDVTList TyList = DAG.getVTList(Tys); SDValue Res = @@ -320,28 +414,28 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, Chain = Res.getValue(1); } - Chain = DAG.getCALLSEQ_END(Chain, NB, Zero, SDValue(), DL); + SDValue Unused = DAG.getUNDEF(PtrVT); + Chain = DAG.getCALLSEQ_END(Chain, NB, Unused, SDValue(), DL); return Chain; } bool WebAssemblyTargetLowering::CanLowerReturn( - CallingConv::ID CallConv, MachineFunction &MF, bool IsVarArg, - const SmallVectorImpl &Outs, LLVMContext &Context) const { + CallingConv::ID /*CallConv*/, MachineFunction & /*MF*/, bool /*IsVarArg*/, + const SmallVectorImpl &Outs, + LLVMContext & /*Context*/) const { // WebAssembly can't currently handle returning tuples. return Outs.size() <= 1; } SDValue WebAssemblyTargetLowering::LowerReturn( - SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, + SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/, const SmallVectorImpl &Outs, const SmallVectorImpl &OutVals, SDLoc DL, SelectionDAG &DAG) const { assert(Outs.size() <= 1 && "WebAssembly can only return up to one value"); - if (CallConv != CallingConv::C) + if (!CallingConvSupported(CallConv)) fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions"); - if (IsVarArg) - fail(DL, DAG, "WebAssembly doesn't support varargs yet"); SmallVector RetOps(1, Chain); RetOps.append(OutVals.begin(), OutVals.end()); @@ -349,33 +443,28 @@ SDValue WebAssemblyTargetLowering::LowerReturn( // Record the number and types of the return values. for (const ISD::OutputArg &Out : Outs) { - if (Out.Flags.isByVal()) - fail(DL, DAG, "WebAssembly hasn't implemented byval results"); + assert(!Out.Flags.isByVal() && "byval is not valid for return values"); + assert(!Out.Flags.isNest() && "nest is not valid for return values"); + assert(Out.IsFixed && "non-fixed return value is not valid"); if (Out.Flags.isInAlloca()) fail(DL, DAG, "WebAssembly hasn't implemented inalloca results"); - if (Out.Flags.isNest()) - fail(DL, DAG, "WebAssembly hasn't implemented nest results"); if (Out.Flags.isInConsecutiveRegs()) fail(DL, DAG, "WebAssembly hasn't implemented cons regs results"); if (Out.Flags.isInConsecutiveRegsLast()) fail(DL, DAG, "WebAssembly hasn't implemented cons regs last results"); - if (!Out.IsFixed) - fail(DL, DAG, "WebAssembly doesn't support non-fixed results yet"); } return Chain; } SDValue WebAssemblyTargetLowering::LowerFormalArguments( - SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, + SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/, const SmallVectorImpl &Ins, SDLoc DL, SelectionDAG &DAG, SmallVectorImpl &InVals) const { MachineFunction &MF = DAG.getMachineFunction(); - if (CallConv != CallingConv::C) + if (!CallingConvSupported(CallConv)) fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions"); - if (IsVarArg) - fail(DL, DAG, "WebAssembly doesn't support varargs yet"); // Set up the incoming ARGUMENTS value, which serves to represent the liveness // of the incoming values before they're represented by virtual registers. @@ -398,12 +487,15 @@ SDValue WebAssemblyTargetLowering::LowerFormalArguments( In.Used ? DAG.getNode(WebAssemblyISD::ARGUMENT, DL, In.VT, DAG.getTargetConstant(InVals.size(), DL, MVT::i32)) - : DAG.getNode(ISD::UNDEF, DL, In.VT)); + : DAG.getUNDEF(In.VT)); // Record the number and types of arguments. MF.getInfo()->addParam(In.VT); } + // Incoming varargs arguments are on the stack and will be accessed through + // va_arg, so we don't need to do anything for them here. + return Chain; } @@ -425,6 +517,8 @@ SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op, return LowerJumpTable(Op, DAG); case ISD::BR_JT: return LowerBR_JT(Op, DAG); + case ISD::VASTART: + return LowerVASTART(Op, DAG); } } @@ -433,17 +527,17 @@ SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op, SDLoc DL(Op); const auto *GA = cast(Op); EVT VT = Op.getValueType(); - assert(GA->getOffset() == 0 && - "offsets on global addresses are forbidden by isOffsetFoldingLegal"); assert(GA->getTargetFlags() == 0 && "WebAssembly doesn't set target flags"); if (GA->getAddressSpace() != 0) fail(DL, DAG, "WebAssembly only expects the 0 address space"); return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, - DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT)); + DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, + GA->getOffset())); } -SDValue WebAssemblyTargetLowering::LowerExternalSymbol(SDValue Op, - SelectionDAG &DAG) const { +SDValue +WebAssemblyTargetLowering::LowerExternalSymbol(SDValue Op, + SelectionDAG &DAG) const { SDLoc DL(Op); const auto *ES = cast(Op); EVT VT = Op.getValueType(); @@ -489,13 +583,30 @@ SDValue WebAssemblyTargetLowering::LowerBR_JT(SDValue Op, return DAG.getNode(WebAssemblyISD::TABLESWITCH, DL, MVT::Other, Ops); } +SDValue WebAssemblyTargetLowering::LowerVASTART(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + EVT PtrVT = getPointerTy(DAG.getMachineFunction().getDataLayout()); + + // The incoming non-fixed arguments are placed on the top of the stack, with + // natural alignment, at the point of the call, so the base pointer is just + // the current frame pointer. + DAG.getMachineFunction().getFrameInfo()->setFrameAddressIsTaken(true); + unsigned FP = + Subtarget->getRegisterInfo()->getFrameRegister(DAG.getMachineFunction()); + SDValue FrameAddr = DAG.getCopyFromReg(DAG.getEntryNode(), DL, FP, PtrVT); + const Value *SV = cast(Op.getOperand(2))->getValue(); + return DAG.getStore(Op.getOperand(0), DL, FrameAddr, Op.getOperand(1), + MachinePointerInfo(SV), false, false, 0); +} + //===----------------------------------------------------------------------===// // WebAssembly Optimization Hooks //===----------------------------------------------------------------------===// MCSection *WebAssemblyTargetObjectFile::SelectSectionForGlobal( - const GlobalValue *GV, SectionKind Kind, Mangler &Mang, - const TargetMachine &TM) const { + const GlobalValue *GV, SectionKind /*Kind*/, Mangler & /*Mang*/, + const TargetMachine & /*TM*/) const { // TODO: Be more sophisticated than this. return isa(GV) ? getTextSection() : getDataSection(); }