110e730a2SDan Gohman //=- WebAssemblyISelLowering.cpp - WebAssembly DAG Lowering Implementation -==//
210e730a2SDan Gohman //
310e730a2SDan Gohman //                     The LLVM Compiler Infrastructure
410e730a2SDan Gohman //
510e730a2SDan Gohman // This file is distributed under the University of Illinois Open Source
610e730a2SDan Gohman // License. See LICENSE.TXT for details.
710e730a2SDan Gohman //
810e730a2SDan Gohman //===----------------------------------------------------------------------===//
910e730a2SDan Gohman ///
1010e730a2SDan Gohman /// \file
1110e730a2SDan Gohman /// \brief This file implements the WebAssemblyTargetLowering class.
1210e730a2SDan Gohman ///
1310e730a2SDan Gohman //===----------------------------------------------------------------------===//
1410e730a2SDan Gohman 
1510e730a2SDan Gohman #include "WebAssemblyISelLowering.h"
1610e730a2SDan Gohman #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
1710e730a2SDan Gohman #include "WebAssemblyMachineFunctionInfo.h"
1810e730a2SDan Gohman #include "WebAssemblySubtarget.h"
1910e730a2SDan Gohman #include "WebAssemblyTargetMachine.h"
2010e730a2SDan Gohman #include "llvm/CodeGen/Analysis.h"
21af111db8SJF Bastien #include "llvm/CodeGen/CallingConvLower.h"
22950a13cfSDan Gohman #include "llvm/CodeGen/MachineJumpTableInfo.h"
2310e730a2SDan Gohman #include "llvm/CodeGen/MachineRegisterInfo.h"
2410e730a2SDan Gohman #include "llvm/CodeGen/SelectionDAG.h"
25b9073fb2SJF Bastien #include "llvm/IR/DiagnosticInfo.h"
26b9073fb2SJF Bastien #include "llvm/IR/DiagnosticPrinter.h"
2710e730a2SDan Gohman #include "llvm/IR/Function.h"
2810e730a2SDan Gohman #include "llvm/IR/Intrinsics.h"
2910e730a2SDan Gohman #include "llvm/Support/CommandLine.h"
3010e730a2SDan Gohman #include "llvm/Support/Debug.h"
3110e730a2SDan Gohman #include "llvm/Support/ErrorHandling.h"
3210e730a2SDan Gohman #include "llvm/Support/raw_ostream.h"
3310e730a2SDan Gohman #include "llvm/Target/TargetOptions.h"
3410e730a2SDan Gohman using namespace llvm;
3510e730a2SDan Gohman 
3610e730a2SDan Gohman #define DEBUG_TYPE "wasm-lower"
3710e730a2SDan Gohman 
3810e730a2SDan Gohman WebAssemblyTargetLowering::WebAssemblyTargetLowering(
3910e730a2SDan Gohman     const TargetMachine &TM, const WebAssemblySubtarget &STI)
40bfaf7e15SDan Gohman     : TargetLowering(TM), Subtarget(&STI) {
41af111db8SJF Bastien   auto MVTPtr = Subtarget->hasAddr64() ? MVT::i64 : MVT::i32;
42af111db8SJF Bastien 
4371d29aceSJF Bastien   // Booleans always contain 0 or 1.
4471d29aceSJF Bastien   setBooleanContents(ZeroOrOneBooleanContent);
45bfaf7e15SDan Gohman   // WebAssembly does not produce floating-point exceptions on normal floating
46bfaf7e15SDan Gohman   // point operations.
47bfaf7e15SDan Gohman   setHasFloatingPointExceptions(false);
48489abd70SDan Gohman   // We don't know the microarchitecture here, so just reduce register pressure.
49489abd70SDan Gohman   setSchedulingPreference(Sched::RegPressure);
50b9073fb2SJF Bastien   // Tell ISel that we have a stack pointer.
51b9073fb2SJF Bastien   setStackPointerRegisterToSaveRestore(
52b9073fb2SJF Bastien       Subtarget->hasAddr64() ? WebAssembly::SP64 : WebAssembly::SP32);
53b9073fb2SJF Bastien   // Set up the register classes.
54d0bf9812SDan Gohman   addRegisterClass(MVT::i32, &WebAssembly::I32RegClass);
55d0bf9812SDan Gohman   addRegisterClass(MVT::i64, &WebAssembly::I64RegClass);
56d0bf9812SDan Gohman   addRegisterClass(MVT::f32, &WebAssembly::F32RegClass);
57d0bf9812SDan Gohman   addRegisterClass(MVT::f64, &WebAssembly::F64RegClass);
58b9073fb2SJF Bastien   // Compute derived properties from the register classes.
59b9073fb2SJF Bastien   computeRegisterProperties(Subtarget->getRegisterInfo());
60b9073fb2SJF Bastien 
61af111db8SJF Bastien   setOperationAction(ISD::GlobalAddress, MVTPtr, Custom);
622c8fe6a4SDan Gohman   setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom);
63950a13cfSDan Gohman   setOperationAction(ISD::JumpTable, MVTPtr, Custom);
64af111db8SJF Bastien 
6535bfb24cSDan Gohman   // Take the default expansion for va_arg, va_copy, and va_end. There is no
6635bfb24cSDan Gohman   // default action for va_start, so we do that custom.
6735bfb24cSDan Gohman   setOperationAction(ISD::VASTART, MVT::Other, Custom);
6835bfb24cSDan Gohman   setOperationAction(ISD::VAARG, MVT::Other, Expand);
6935bfb24cSDan Gohman   setOperationAction(ISD::VACOPY, MVT::Other, Expand);
7035bfb24cSDan Gohman   setOperationAction(ISD::VAEND, MVT::Other, Expand);
7135bfb24cSDan Gohman 
72da06bce8SJF Bastien   for (auto T : {MVT::f32, MVT::f64}) {
73da06bce8SJF Bastien     // Don't expand the floating-point types to constant pools.
74da06bce8SJF Bastien     setOperationAction(ISD::ConstantFP, T, Legal);
75da06bce8SJF Bastien     // Expand floating-point comparisons.
76da06bce8SJF Bastien     for (auto CC : {ISD::SETO, ISD::SETUO, ISD::SETUEQ, ISD::SETONE,
77da06bce8SJF Bastien                     ISD::SETULT, ISD::SETULE, ISD::SETUGT, ISD::SETUGE})
78da06bce8SJF Bastien       setCondCodeAction(CC, T, Expand);
7932907a6bSDan Gohman     // Expand floating-point library function operators.
80ebb23545SDan Gohman     for (auto Op : {ISD::FSIN, ISD::FCOS, ISD::FSINCOS, ISD::FPOWI, ISD::FPOW,
819341c1d4SDan Gohman                     ISD::FREM, ISD::FMA})
8232907a6bSDan Gohman       setOperationAction(Op, T, Expand);
83896e53faSDan Gohman     // Note supported floating-point library function operators that otherwise
84896e53faSDan Gohman     // default to expand.
857a6b9825SDan Gohman     for (auto Op :
867a6b9825SDan Gohman          {ISD::FCEIL, ISD::FFLOOR, ISD::FTRUNC, ISD::FNEARBYINT, ISD::FRINT})
87896e53faSDan Gohman       setOperationAction(Op, T, Legal);
88b84ae9bbSDan Gohman     // Support minnan and maxnan, which otherwise default to expand.
89b84ae9bbSDan Gohman     setOperationAction(ISD::FMINNAN, T, Legal);
90b84ae9bbSDan Gohman     setOperationAction(ISD::FMAXNAN, T, Legal);
91da06bce8SJF Bastien   }
9232907a6bSDan Gohman 
9332907a6bSDan Gohman   for (auto T : {MVT::i32, MVT::i64}) {
9432907a6bSDan Gohman     // Expand unavailable integer operations.
957a6b9825SDan Gohman     for (auto Op :
967a6b9825SDan Gohman          {ISD::BSWAP, ISD::ROTL, ISD::ROTR, ISD::SMUL_LOHI, ISD::UMUL_LOHI,
977a6b9825SDan Gohman           ISD::MULHS, ISD::MULHU, ISD::SDIVREM, ISD::UDIVREM, ISD::SHL_PARTS,
987a6b9825SDan Gohman           ISD::SRA_PARTS, ISD::SRL_PARTS, ISD::ADDC, ISD::ADDE, ISD::SUBC,
997a6b9825SDan Gohman           ISD::SUBE}) {
10032907a6bSDan Gohman       setOperationAction(Op, T, Expand);
10132907a6bSDan Gohman     }
10232907a6bSDan Gohman   }
10332907a6bSDan Gohman 
10432907a6bSDan Gohman   // As a special case, these operators use the type to mean the type to
10532907a6bSDan Gohman   // sign-extend from.
106a5603b83SDan Gohman   for (auto T : {MVT::i1, MVT::i8, MVT::i16, MVT::i32})
10732907a6bSDan Gohman     setOperationAction(ISD::SIGN_EXTEND_INREG, T, Expand);
10832907a6bSDan Gohman 
10932907a6bSDan Gohman   // Dynamic stack allocation: use the default expansion.
11032907a6bSDan Gohman   setOperationAction(ISD::STACKSAVE, MVT::Other, Expand);
11132907a6bSDan Gohman   setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand);
1122683a553SDan Gohman   setOperationAction(ISD::DYNAMIC_STACKALLOC, MVTPtr, Expand);
11373ff6afaSJF Bastien 
1149769debfSDerek Schuff   setOperationAction(ISD::FrameIndex, MVT::i32, Custom);
1159769debfSDerek Schuff 
116950a13cfSDan Gohman   // Expand these forms; we pattern-match the forms that we can handle in isel.
117950a13cfSDan Gohman   for (auto T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64})
118950a13cfSDan Gohman     for (auto Op : {ISD::BR_CC, ISD::SELECT_CC})
119950a13cfSDan Gohman       setOperationAction(Op, T, Expand);
120950a13cfSDan Gohman 
121950a13cfSDan Gohman   // We have custom switch handling.
122950a13cfSDan Gohman   setOperationAction(ISD::BR_JT, MVT::Other, Custom);
123950a13cfSDan Gohman 
12473ff6afaSJF Bastien   // WebAssembly doesn't have:
12573ff6afaSJF Bastien   //  - Floating-point extending loads.
12673ff6afaSJF Bastien   //  - Floating-point truncating stores.
12773ff6afaSJF Bastien   //  - i1 extending loads.
12860bddf17SDan Gohman   setLoadExtAction(ISD::EXTLOAD, MVT::f64, MVT::f32, Expand);
12973ff6afaSJF Bastien   setTruncStoreAction(MVT::f64, MVT::f32, Expand);
13073ff6afaSJF Bastien   for (auto T : MVT::integer_valuetypes())
13173ff6afaSJF Bastien     for (auto Ext : {ISD::EXTLOAD, ISD::ZEXTLOAD, ISD::SEXTLOAD})
13273ff6afaSJF Bastien       setLoadExtAction(Ext, T, MVT::i1, Promote);
133ffa143ceSDerek Schuff 
134ffa143ceSDerek Schuff   // Trap lowers to wasm unreachable
135ffa143ceSDerek Schuff   setOperationAction(ISD::TRAP, MVT::Other, Legal);
136bfaf7e15SDan Gohman }
13710e730a2SDan Gohman 
1387b63484bSDan Gohman FastISel *WebAssemblyTargetLowering::createFastISel(
1397b63484bSDan Gohman     FunctionLoweringInfo &FuncInfo, const TargetLibraryInfo *LibInfo) const {
1407b63484bSDan Gohman   return WebAssembly::createFastISel(FuncInfo, LibInfo);
1417b63484bSDan Gohman }
1427b63484bSDan Gohman 
143af111db8SJF Bastien bool WebAssemblyTargetLowering::isOffsetFoldingLegal(
1447a6b9825SDan Gohman     const GlobalAddressSDNode * /*GA*/) const {
145a4b710a7SDan Gohman   // All offsets can be folded.
146a4b710a7SDan Gohman   return true;
147af111db8SJF Bastien }
148af111db8SJF Bastien 
1497a6b9825SDan Gohman MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout & /*DL*/,
150fda53373SJF Bastien                                                       EVT VT) const {
151a8483755SDan Gohman   unsigned BitWidth = NextPowerOf2(VT.getSizeInBits() - 1);
152a8483755SDan Gohman   if (BitWidth > 1 && BitWidth < 8)
153a8483755SDan Gohman     BitWidth = 8;
15441729538SDan Gohman 
15541729538SDan Gohman   if (BitWidth > 64) {
15641729538SDan Gohman     BitWidth = 64;
15741729538SDan Gohman     assert(BitWidth >= Log2_32_Ceil(VT.getSizeInBits()) &&
15841729538SDan Gohman            "64-bit shift counts ought to be enough for anyone");
15941729538SDan Gohman   }
16041729538SDan Gohman 
161a8483755SDan Gohman   MVT Result = MVT::getIntegerVT(BitWidth);
162a8483755SDan Gohman   assert(Result != MVT::INVALID_SIMPLE_VALUE_TYPE &&
163a8483755SDan Gohman          "Unable to represent scalar shift amount type");
164a8483755SDan Gohman   return Result;
165fda53373SJF Bastien }
166fda53373SJF Bastien 
167480c8408SJF Bastien const char *
168480c8408SJF Bastien WebAssemblyTargetLowering::getTargetNodeName(unsigned Opcode) const {
169480c8408SJF Bastien   switch (static_cast<WebAssemblyISD::NodeType>(Opcode)) {
170af111db8SJF Bastien   case WebAssemblyISD::FIRST_NUMBER:
171af111db8SJF Bastien     break;
172af111db8SJF Bastien #define HANDLE_NODETYPE(NODE)                                                  \
173af111db8SJF Bastien   case WebAssemblyISD::NODE:                                                   \
174af111db8SJF Bastien     return "WebAssemblyISD::" #NODE;
175af111db8SJF Bastien #include "WebAssemblyISD.def"
176af111db8SJF Bastien #undef HANDLE_NODETYPE
177480c8408SJF Bastien   }
178480c8408SJF Bastien   return nullptr;
179480c8408SJF Bastien }
180480c8408SJF Bastien 
181f19ed562SDan Gohman std::pair<unsigned, const TargetRegisterClass *>
182f19ed562SDan Gohman WebAssemblyTargetLowering::getRegForInlineAsmConstraint(
183f19ed562SDan Gohman     const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const {
184f19ed562SDan Gohman   // First, see if this is a constraint that directly corresponds to a
185f19ed562SDan Gohman   // WebAssembly register class.
186f19ed562SDan Gohman   if (Constraint.size() == 1) {
187f19ed562SDan Gohman     switch (Constraint[0]) {
188f19ed562SDan Gohman     case 'r':
189284384b6SDan Gohman       assert(VT != MVT::iPTR && "Pointer MVT not expected here");
190284384b6SDan Gohman       if (VT.isInteger() && !VT.isVector()) {
191284384b6SDan Gohman         if (VT.getSizeInBits() <= 32)
192f19ed562SDan Gohman           return std::make_pair(0U, &WebAssembly::I32RegClass);
193284384b6SDan Gohman         if (VT.getSizeInBits() <= 64)
194a774d719SDan Gohman           return std::make_pair(0U, &WebAssembly::I64RegClass);
195284384b6SDan Gohman       }
196a774d719SDan Gohman       break;
197f19ed562SDan Gohman     default:
198f19ed562SDan Gohman       break;
199f19ed562SDan Gohman     }
200f19ed562SDan Gohman   }
201f19ed562SDan Gohman 
202f19ed562SDan Gohman   return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT);
203f19ed562SDan Gohman }
204f19ed562SDan Gohman 
2053192ddfeSDan Gohman bool WebAssemblyTargetLowering::isCheapToSpeculateCttz() const {
2063192ddfeSDan Gohman   // Assume ctz is a relatively cheap operation.
2073192ddfeSDan Gohman   return true;
2083192ddfeSDan Gohman }
2093192ddfeSDan Gohman 
2103192ddfeSDan Gohman bool WebAssemblyTargetLowering::isCheapToSpeculateCtlz() const {
2113192ddfeSDan Gohman   // Assume clz is a relatively cheap operation.
2123192ddfeSDan Gohman   return true;
2133192ddfeSDan Gohman }
2143192ddfeSDan Gohman 
2154b9d7916SDan Gohman bool WebAssemblyTargetLowering::isLegalAddressingMode(const DataLayout &DL,
2164b9d7916SDan Gohman                                                       const AddrMode &AM,
2174b9d7916SDan Gohman                                                       Type *Ty,
2184b9d7916SDan Gohman                                                       unsigned AS) const {
2194b9d7916SDan Gohman   // WebAssembly offsets are added as unsigned without wrapping. The
2204b9d7916SDan Gohman   // isLegalAddressingMode gives us no way to determine if wrapping could be
2214b9d7916SDan Gohman   // happening, so we approximate this by accepting only non-negative offsets.
2224b9d7916SDan Gohman   if (AM.BaseOffs < 0)
2234b9d7916SDan Gohman     return false;
2244b9d7916SDan Gohman 
2254b9d7916SDan Gohman   // WebAssembly has no scale register operands.
2264b9d7916SDan Gohman   if (AM.Scale != 0)
2274b9d7916SDan Gohman     return false;
2284b9d7916SDan Gohman 
2294b9d7916SDan Gohman   // Everything else is legal.
2304b9d7916SDan Gohman   return true;
2314b9d7916SDan Gohman }
2324b9d7916SDan Gohman 
233bb372243SDan Gohman bool WebAssemblyTargetLowering::allowsMisalignedMemoryAccesses(
234bb372243SDan Gohman     EVT /*VT*/, unsigned /*AddrSpace*/, unsigned /*Align*/,
235bb372243SDan Gohman     bool *Fast) const {
236bb372243SDan Gohman   // WebAssembly supports unaligned accesses, though it should be declared
237bb372243SDan Gohman   // with the p2align attribute on loads and stores which do so, and there
238bb372243SDan Gohman   // may be a performance impact. We tell LLVM they're "fast" because
239fb619e96SDan Gohman   // for the kinds of things that LLVM uses this for (merging adjacent stores
240bb372243SDan Gohman   // of constants, etc.), WebAssembly implementations will either want the
241bb372243SDan Gohman   // unaligned access or they'll split anyway.
242bb372243SDan Gohman   if (Fast)
243bb372243SDan Gohman     *Fast = true;
244bb372243SDan Gohman   return true;
245bb372243SDan Gohman }
246bb372243SDan Gohman 
24710e730a2SDan Gohman //===----------------------------------------------------------------------===//
24810e730a2SDan Gohman // WebAssembly Lowering private implementation.
24910e730a2SDan Gohman //===----------------------------------------------------------------------===//
25010e730a2SDan Gohman 
25110e730a2SDan Gohman //===----------------------------------------------------------------------===//
25210e730a2SDan Gohman // Lowering Code
25310e730a2SDan Gohman //===----------------------------------------------------------------------===//
25410e730a2SDan Gohman 
255b9073fb2SJF Bastien static void fail(SDLoc DL, SelectionDAG &DAG, const char *msg) {
256b9073fb2SJF Bastien   MachineFunction &MF = DAG.getMachineFunction();
257b9073fb2SJF Bastien   DAG.getContext()->diagnose(
258c8be5be9SBenjamin Kramer       DiagnosticInfoUnsupported(*MF.getFunction(), msg, DL));
259b9073fb2SJF Bastien }
260b9073fb2SJF Bastien 
26185dbdda1SDan Gohman // Test whether the given calling convention is supported.
262a3f5ce5fSDan Gohman static bool CallingConvSupported(CallingConv::ID CallConv) {
26385dbdda1SDan Gohman   // We currently support the language-independent target-independent
2641ce2b1afSDan Gohman   // conventions. We don't yet have a way to annotate calls with properties like
2651ce2b1afSDan Gohman   // "cold", and we don't have any call-clobbered registers, so these are mostly
2661ce2b1afSDan Gohman   // all handled the same.
267a3f5ce5fSDan Gohman   return CallConv == CallingConv::C || CallConv == CallingConv::Fast ||
2681ce2b1afSDan Gohman          CallConv == CallingConv::Cold ||
2691ce2b1afSDan Gohman          CallConv == CallingConv::PreserveMost ||
2701ce2b1afSDan Gohman          CallConv == CallingConv::PreserveAll ||
2711ce2b1afSDan Gohman          CallConv == CallingConv::CXX_FAST_TLS;
27285dbdda1SDan Gohman }
27385dbdda1SDan Gohman 
274d8a9d66dSJF Bastien SDValue
275d8a9d66dSJF Bastien WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI,
276d8a9d66dSJF Bastien                                      SmallVectorImpl<SDValue> &InVals) const {
277d8a9d66dSJF Bastien   SelectionDAG &DAG = CLI.DAG;
278d8a9d66dSJF Bastien   SDLoc DL = CLI.DL;
279d8a9d66dSJF Bastien   SDValue Chain = CLI.Chain;
280d8a9d66dSJF Bastien   SDValue Callee = CLI.Callee;
281d8a9d66dSJF Bastien   MachineFunction &MF = DAG.getMachineFunction();
282d8a9d66dSJF Bastien 
283d8a9d66dSJF Bastien   CallingConv::ID CallConv = CLI.CallConv;
28485dbdda1SDan Gohman   if (!CallingConvSupported(CallConv))
2859cc692b0SDan Gohman     fail(DL, DAG,
2869cc692b0SDan Gohman          "WebAssembly doesn't support language-specific or target-specific "
2879cc692b0SDan Gohman          "calling conventions yet");
288d8a9d66dSJF Bastien   if (CLI.IsPatchPoint)
289d8a9d66dSJF Bastien     fail(DL, DAG, "WebAssembly doesn't support patch point yet");
290d8a9d66dSJF Bastien 
2919cc692b0SDan Gohman   // WebAssembly doesn't currently support explicit tail calls. If they are
2929cc692b0SDan Gohman   // required, fail. Otherwise, just disable them.
2939cc692b0SDan Gohman   if ((CallConv == CallingConv::Fast && CLI.IsTailCall &&
2949cc692b0SDan Gohman        MF.getTarget().Options.GuaranteedTailCallOpt) ||
2959cc692b0SDan Gohman       (CLI.CS && CLI.CS->isMustTailCall()))
2969cc692b0SDan Gohman     fail(DL, DAG, "WebAssembly doesn't support tail call yet");
2979cc692b0SDan Gohman   CLI.IsTailCall = false;
2989cc692b0SDan Gohman 
299d8a9d66dSJF Bastien   SmallVectorImpl<ISD::InputArg> &Ins = CLI.Ins;
300e590b33bSDan Gohman   if (Ins.size() > 1)
301e590b33bSDan Gohman     fail(DL, DAG, "WebAssembly doesn't support more than 1 returned value yet");
302e590b33bSDan Gohman 
3032d822e73SDan Gohman   SmallVectorImpl<ISD::OutputArg> &Outs = CLI.Outs;
304*4dd67786SDerek Schuff   SmallVectorImpl<SDValue> &OutVals = CLI.OutVals;
305*4dd67786SDerek Schuff   for (unsigned i = 0; i < Outs.size(); ++i) {
306*4dd67786SDerek Schuff     const ISD::OutputArg &Out = Outs[i];
307*4dd67786SDerek Schuff     SDValue &OutVal = OutVals[i];
3087935fa3dSDan Gohman     if (Out.Flags.isNest())
3097935fa3dSDan Gohman       fail(DL, DAG, "WebAssembly hasn't implemented nest arguments");
3102d822e73SDan Gohman     if (Out.Flags.isInAlloca())
3117935fa3dSDan Gohman       fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments");
3122d822e73SDan Gohman     if (Out.Flags.isInConsecutiveRegs())
3137935fa3dSDan Gohman       fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments");
3142d822e73SDan Gohman     if (Out.Flags.isInConsecutiveRegsLast())
3157935fa3dSDan Gohman       fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments");
316*4dd67786SDerek Schuff     if (Out.Flags.isByVal()) {
317*4dd67786SDerek Schuff       auto *MFI = MF.getFrameInfo();
318*4dd67786SDerek Schuff       assert(Out.Flags.getByValSize() && "Zero-size byval?");
319*4dd67786SDerek Schuff       int FI = MFI->CreateStackObject(Out.Flags.getByValSize(),
320*4dd67786SDerek Schuff                                       Out.Flags.getByValAlign(),
321*4dd67786SDerek Schuff                                       /*isSS=*/false);
322*4dd67786SDerek Schuff       SDValue SizeNode =
323*4dd67786SDerek Schuff           DAG.getConstant(Out.Flags.getByValSize(), DL, MVT::i32);
324*4dd67786SDerek Schuff       SDValue FINode = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout()));
325*4dd67786SDerek Schuff       Chain = DAG.getMemcpy(
326*4dd67786SDerek Schuff           Chain, DL, FINode, OutVal, SizeNode, Out.Flags.getByValAlign(),
327*4dd67786SDerek Schuff           /*isVolatile*/ false, /*AlwaysInline=*/true,
328*4dd67786SDerek Schuff           /*isTailCall*/ false, MachinePointerInfo(), MachinePointerInfo());
329*4dd67786SDerek Schuff       OutVal = FINode;
330*4dd67786SDerek Schuff     }
3312d822e73SDan Gohman   }
3322d822e73SDan Gohman 
333d8a9d66dSJF Bastien   bool IsVarArg = CLI.IsVarArg;
33435bfb24cSDan Gohman   unsigned NumFixedArgs = CLI.NumFixedArgs;
33535bfb24cSDan Gohman   auto PtrVT = getPointerTy(MF.getDataLayout());
336e590b33bSDan Gohman 
337d8a9d66dSJF Bastien   // Analyze operands of the call, assigning locations to each operand.
338d8a9d66dSJF Bastien   SmallVector<CCValAssign, 16> ArgLocs;
339d8a9d66dSJF Bastien   CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext());
340d8a9d66dSJF Bastien 
34135bfb24cSDan Gohman   if (IsVarArg) {
34235bfb24cSDan Gohman     // Outgoing non-fixed arguments are placed at the top of the stack. First
34335bfb24cSDan Gohman     // compute their offsets and the total amount of argument stack space
34435bfb24cSDan Gohman     // needed.
34535bfb24cSDan Gohman     for (SDValue Arg :
34635bfb24cSDan Gohman          make_range(OutVals.begin() + NumFixedArgs, OutVals.end())) {
34735bfb24cSDan Gohman       EVT VT = Arg.getValueType();
34835bfb24cSDan Gohman       assert(VT != MVT::iPTR && "Legalized args should be concrete");
34935bfb24cSDan Gohman       Type *Ty = VT.getTypeForEVT(*DAG.getContext());
35035bfb24cSDan Gohman       unsigned Offset =
35135bfb24cSDan Gohman           CCInfo.AllocateStack(MF.getDataLayout().getTypeAllocSize(Ty),
35235bfb24cSDan Gohman                                MF.getDataLayout().getABITypeAlignment(Ty));
35335bfb24cSDan Gohman       CCInfo.addLoc(CCValAssign::getMem(ArgLocs.size(), VT.getSimpleVT(),
35435bfb24cSDan Gohman                                         Offset, VT.getSimpleVT(),
35535bfb24cSDan Gohman                                         CCValAssign::Full));
35635bfb24cSDan Gohman     }
35735bfb24cSDan Gohman   }
35835bfb24cSDan Gohman 
35935bfb24cSDan Gohman   unsigned NumBytes = CCInfo.getAlignedCallFrameSize();
36035bfb24cSDan Gohman 
3615a143063SDerek Schuff   SDValue NB;
3625a143063SDerek Schuff   if (NumBytes) {
3635a143063SDerek Schuff     NB = DAG.getConstant(NumBytes, DL, PtrVT, true);
364af111db8SJF Bastien     Chain = DAG.getCALLSEQ_START(Chain, NB, DL);
3655a143063SDerek Schuff   }
366d8a9d66dSJF Bastien 
36735bfb24cSDan Gohman   if (IsVarArg) {
36835bfb24cSDan Gohman     // For non-fixed arguments, next emit stores to store the argument values
36935bfb24cSDan Gohman     // to the stack at the offsets computed above.
37035bfb24cSDan Gohman     SDValue SP = DAG.getCopyFromReg(
37135bfb24cSDan Gohman         Chain, DL, getStackPointerRegisterToSaveRestore(), PtrVT);
37235bfb24cSDan Gohman     unsigned ValNo = 0;
37335bfb24cSDan Gohman     SmallVector<SDValue, 8> Chains;
37435bfb24cSDan Gohman     for (SDValue Arg :
37535bfb24cSDan Gohman          make_range(OutVals.begin() + NumFixedArgs, OutVals.end())) {
37635bfb24cSDan Gohman       assert(ArgLocs[ValNo].getValNo() == ValNo &&
37735bfb24cSDan Gohman              "ArgLocs should remain in order and only hold varargs args");
37835bfb24cSDan Gohman       unsigned Offset = ArgLocs[ValNo++].getLocMemOffset();
37935bfb24cSDan Gohman       SDValue Add = DAG.getNode(ISD::ADD, DL, PtrVT, SP,
38035bfb24cSDan Gohman                                 DAG.getConstant(Offset, DL, PtrVT));
38135bfb24cSDan Gohman       Chains.push_back(DAG.getStore(Chain, DL, Arg, Add,
38235bfb24cSDan Gohman                                     MachinePointerInfo::getStack(MF, Offset),
38335bfb24cSDan Gohman                                     false, false, 0));
38435bfb24cSDan Gohman     }
38535bfb24cSDan Gohman     if (!Chains.empty())
38635bfb24cSDan Gohman       Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Chains);
38735bfb24cSDan Gohman   }
38835bfb24cSDan Gohman 
38935bfb24cSDan Gohman   // Compute the operands for the CALLn node.
390d8a9d66dSJF Bastien   SmallVector<SDValue, 16> Ops;
391d8a9d66dSJF Bastien   Ops.push_back(Chain);
392af111db8SJF Bastien   Ops.push_back(Callee);
39335bfb24cSDan Gohman 
39435bfb24cSDan Gohman   // Add all fixed arguments. Note that for non-varargs calls, NumFixedArgs
39535bfb24cSDan Gohman   // isn't reliable.
39635bfb24cSDan Gohman   Ops.append(OutVals.begin(),
39735bfb24cSDan Gohman              IsVarArg ? OutVals.begin() + NumFixedArgs : OutVals.end());
398d8a9d66dSJF Bastien 
399d8a9d66dSJF Bastien   SmallVector<EVT, 8> Tys;
4002d822e73SDan Gohman   for (const auto &In : Ins) {
4017935fa3dSDan Gohman     assert(!In.Flags.isByVal() && "byval is not valid for return values");
4027935fa3dSDan Gohman     assert(!In.Flags.isNest() && "nest is not valid for return values");
4032d822e73SDan Gohman     if (In.Flags.isInAlloca())
4047935fa3dSDan Gohman       fail(DL, DAG, "WebAssembly hasn't implemented inalloca return values");
4052d822e73SDan Gohman     if (In.Flags.isInConsecutiveRegs())
4067935fa3dSDan Gohman       fail(DL, DAG, "WebAssembly hasn't implemented cons regs return values");
4072d822e73SDan Gohman     if (In.Flags.isInConsecutiveRegsLast())
4084b9d7916SDan Gohman       fail(DL, DAG,
4094b9d7916SDan Gohman            "WebAssembly hasn't implemented cons regs last return values");
4102d822e73SDan Gohman     // Ignore In.getOrigAlign() because all our arguments are passed in
4112d822e73SDan Gohman     // registers.
412d8a9d66dSJF Bastien     Tys.push_back(In.VT);
4132d822e73SDan Gohman   }
414d8a9d66dSJF Bastien   Tys.push_back(MVT::Other);
415af111db8SJF Bastien   SDVTList TyList = DAG.getVTList(Tys);
416f71abef7SDan Gohman   SDValue Res =
417f71abef7SDan Gohman       DAG.getNode(Ins.empty() ? WebAssemblyISD::CALL0 : WebAssemblyISD::CALL1,
418f71abef7SDan Gohman                   DL, TyList, Ops);
419af111db8SJF Bastien   if (Ins.empty()) {
420af111db8SJF Bastien     Chain = Res;
421af111db8SJF Bastien   } else {
422d8a9d66dSJF Bastien     InVals.push_back(Res);
423d8a9d66dSJF Bastien     Chain = Res.getValue(1);
424af111db8SJF Bastien   }
425d8a9d66dSJF Bastien 
4265a143063SDerek Schuff   if (NumBytes) {
4278bb5f292SDerek Schuff     SDValue Unused = DAG.getTargetConstant(0, DL, PtrVT);
42835bfb24cSDan Gohman     Chain = DAG.getCALLSEQ_END(Chain, NB, Unused, SDValue(), DL);
4295a143063SDerek Schuff   }
430d8a9d66dSJF Bastien 
431d8a9d66dSJF Bastien   return Chain;
432d8a9d66dSJF Bastien }
433d8a9d66dSJF Bastien 
434b9073fb2SJF Bastien bool WebAssemblyTargetLowering::CanLowerReturn(
4357a6b9825SDan Gohman     CallingConv::ID /*CallConv*/, MachineFunction & /*MF*/, bool /*IsVarArg*/,
4367a6b9825SDan Gohman     const SmallVectorImpl<ISD::OutputArg> &Outs,
4377a6b9825SDan Gohman     LLVMContext & /*Context*/) const {
438b9073fb2SJF Bastien   // WebAssembly can't currently handle returning tuples.
439b9073fb2SJF Bastien   return Outs.size() <= 1;
440b9073fb2SJF Bastien }
441b9073fb2SJF Bastien 
442b9073fb2SJF Bastien SDValue WebAssemblyTargetLowering::LowerReturn(
44335bfb24cSDan Gohman     SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/,
444b9073fb2SJF Bastien     const SmallVectorImpl<ISD::OutputArg> &Outs,
445b9073fb2SJF Bastien     const SmallVectorImpl<SDValue> &OutVals, SDLoc DL,
446b9073fb2SJF Bastien     SelectionDAG &DAG) const {
447b9073fb2SJF Bastien   assert(Outs.size() <= 1 && "WebAssembly can only return up to one value");
44885dbdda1SDan Gohman   if (!CallingConvSupported(CallConv))
449b9073fb2SJF Bastien     fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions");
450b9073fb2SJF Bastien 
451600aee98SJF Bastien   SmallVector<SDValue, 4> RetOps(1, Chain);
452600aee98SJF Bastien   RetOps.append(OutVals.begin(), OutVals.end());
4534a2d5604SJF Bastien   Chain = DAG.getNode(WebAssemblyISD::RETURN, DL, MVT::Other, RetOps);
454b9073fb2SJF Bastien 
455754cd11dSDan Gohman   // Record the number and types of the return values.
456754cd11dSDan Gohman   for (const ISD::OutputArg &Out : Outs) {
457ac132e93SDan Gohman     assert(!Out.Flags.isByVal() && "byval is not valid for return values");
458ac132e93SDan Gohman     assert(!Out.Flags.isNest() && "nest is not valid for return values");
45935bfb24cSDan Gohman     assert(Out.IsFixed && "non-fixed return value is not valid");
460754cd11dSDan Gohman     if (Out.Flags.isInAlloca())
461754cd11dSDan Gohman       fail(DL, DAG, "WebAssembly hasn't implemented inalloca results");
462754cd11dSDan Gohman     if (Out.Flags.isInConsecutiveRegs())
463754cd11dSDan Gohman       fail(DL, DAG, "WebAssembly hasn't implemented cons regs results");
464754cd11dSDan Gohman     if (Out.Flags.isInConsecutiveRegsLast())
465754cd11dSDan Gohman       fail(DL, DAG, "WebAssembly hasn't implemented cons regs last results");
466754cd11dSDan Gohman   }
467754cd11dSDan Gohman 
468b9073fb2SJF Bastien   return Chain;
469b9073fb2SJF Bastien }
470b9073fb2SJF Bastien 
471b9073fb2SJF Bastien SDValue WebAssemblyTargetLowering::LowerFormalArguments(
47235bfb24cSDan Gohman     SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/,
473b9073fb2SJF Bastien     const SmallVectorImpl<ISD::InputArg> &Ins, SDLoc DL, SelectionDAG &DAG,
474b9073fb2SJF Bastien     SmallVectorImpl<SDValue> &InVals) const {
475b9073fb2SJF Bastien   MachineFunction &MF = DAG.getMachineFunction();
476b9073fb2SJF Bastien 
47785dbdda1SDan Gohman   if (!CallingConvSupported(CallConv))
478b9073fb2SJF Bastien     fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions");
479b9073fb2SJF Bastien 
480fb3e0594SDan Gohman   // Set up the incoming ARGUMENTS value, which serves to represent the liveness
481fb3e0594SDan Gohman   // of the incoming values before they're represented by virtual registers.
482fb3e0594SDan Gohman   MF.getRegInfo().addLiveIn(WebAssembly::ARGUMENTS);
483fb3e0594SDan Gohman 
484600aee98SJF Bastien   for (const ISD::InputArg &In : Ins) {
485600aee98SJF Bastien     if (In.Flags.isInAlloca())
486600aee98SJF Bastien       fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments");
487600aee98SJF Bastien     if (In.Flags.isNest())
488600aee98SJF Bastien       fail(DL, DAG, "WebAssembly hasn't implemented nest arguments");
489600aee98SJF Bastien     if (In.Flags.isInConsecutiveRegs())
490600aee98SJF Bastien       fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments");
491600aee98SJF Bastien     if (In.Flags.isInConsecutiveRegsLast())
492600aee98SJF Bastien       fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments");
4939c54d3b4SDan Gohman     // Ignore In.getOrigAlign() because all our arguments are passed in
4949c54d3b4SDan Gohman     // registers.
495d7fcc6f9SJF Bastien     InVals.push_back(
496d7fcc6f9SJF Bastien         In.Used
497d7fcc6f9SJF Bastien             ? DAG.getNode(WebAssemblyISD::ARGUMENT, DL, In.VT,
4985219ecf0SDan Gohman                           DAG.getTargetConstant(InVals.size(), DL, MVT::i32))
499cb7940f9SDan Gohman             : DAG.getUNDEF(In.VT));
500754cd11dSDan Gohman 
501754cd11dSDan Gohman     // Record the number and types of arguments.
502754cd11dSDan Gohman     MF.getInfo<WebAssemblyFunctionInfo>()->addParam(In.VT);
503600aee98SJF Bastien   }
504b9073fb2SJF Bastien 
50535bfb24cSDan Gohman   // Incoming varargs arguments are on the stack and will be accessed through
50635bfb24cSDan Gohman   // va_arg, so we don't need to do anything for them here.
50735bfb24cSDan Gohman 
508b9073fb2SJF Bastien   return Chain;
509b9073fb2SJF Bastien }
510b9073fb2SJF Bastien 
51110e730a2SDan Gohman //===----------------------------------------------------------------------===//
512af111db8SJF Bastien //  Custom lowering hooks.
51310e730a2SDan Gohman //===----------------------------------------------------------------------===//
51410e730a2SDan Gohman 
515af111db8SJF Bastien SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op,
516af111db8SJF Bastien                                                   SelectionDAG &DAG) const {
517af111db8SJF Bastien   switch (Op.getOpcode()) {
518af111db8SJF Bastien   default:
519af111db8SJF Bastien     llvm_unreachable("unimplemented operation lowering");
520af111db8SJF Bastien     return SDValue();
5219769debfSDerek Schuff   case ISD::FrameIndex:
5229769debfSDerek Schuff     return LowerFrameIndex(Op, DAG);
523af111db8SJF Bastien   case ISD::GlobalAddress:
524af111db8SJF Bastien     return LowerGlobalAddress(Op, DAG);
5252c8fe6a4SDan Gohman   case ISD::ExternalSymbol:
5262c8fe6a4SDan Gohman     return LowerExternalSymbol(Op, DAG);
527950a13cfSDan Gohman   case ISD::JumpTable:
528950a13cfSDan Gohman     return LowerJumpTable(Op, DAG);
529950a13cfSDan Gohman   case ISD::BR_JT:
530950a13cfSDan Gohman     return LowerBR_JT(Op, DAG);
53135bfb24cSDan Gohman   case ISD::VASTART:
53235bfb24cSDan Gohman     return LowerVASTART(Op, DAG);
533af111db8SJF Bastien   }
534af111db8SJF Bastien }
535af111db8SJF Bastien 
5369769debfSDerek Schuff SDValue WebAssemblyTargetLowering::LowerFrameIndex(SDValue Op,
5379769debfSDerek Schuff                                                    SelectionDAG &DAG) const {
5389769debfSDerek Schuff   int FI = cast<FrameIndexSDNode>(Op)->getIndex();
5399769debfSDerek Schuff   return DAG.getTargetFrameIndex(FI, Op.getValueType());
5409769debfSDerek Schuff }
5419769debfSDerek Schuff 
542af111db8SJF Bastien SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op,
543af111db8SJF Bastien                                                       SelectionDAG &DAG) const {
544af111db8SJF Bastien   SDLoc DL(Op);
545af111db8SJF Bastien   const auto *GA = cast<GlobalAddressSDNode>(Op);
546af111db8SJF Bastien   EVT VT = Op.getValueType();
54726c6765bSDan Gohman   assert(GA->getTargetFlags() == 0 &&
54826c6765bSDan Gohman          "Unexpected target flags on generic GlobalAddressSDNode");
549af111db8SJF Bastien   if (GA->getAddressSpace() != 0)
550af111db8SJF Bastien     fail(DL, DAG, "WebAssembly only expects the 0 address space");
5514b9d7916SDan Gohman   return DAG.getNode(
5524b9d7916SDan Gohman       WebAssemblyISD::Wrapper, DL, VT,
5534b9d7916SDan Gohman       DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset()));
554af111db8SJF Bastien }
555af111db8SJF Bastien 
5567a6b9825SDan Gohman SDValue
5577a6b9825SDan Gohman WebAssemblyTargetLowering::LowerExternalSymbol(SDValue Op,
5582c8fe6a4SDan Gohman                                                SelectionDAG &DAG) const {
5592c8fe6a4SDan Gohman   SDLoc DL(Op);
5602c8fe6a4SDan Gohman   const auto *ES = cast<ExternalSymbolSDNode>(Op);
5612c8fe6a4SDan Gohman   EVT VT = Op.getValueType();
56226c6765bSDan Gohman   assert(ES->getTargetFlags() == 0 &&
56326c6765bSDan Gohman          "Unexpected target flags on generic ExternalSymbolSDNode");
56426c6765bSDan Gohman   // Set the TargetFlags to 0x1 which indicates that this is a "function"
56526c6765bSDan Gohman   // symbol rather than a data symbol. We do this unconditionally even though
56626c6765bSDan Gohman   // we don't know anything about the symbol other than its name, because all
56726c6765bSDan Gohman   // external symbols used in target-independent SelectionDAG code are for
56826c6765bSDan Gohman   // functions.
5692c8fe6a4SDan Gohman   return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT,
57026c6765bSDan Gohman                      DAG.getTargetExternalSymbol(ES->getSymbol(), VT,
57126c6765bSDan Gohman                                                  /*TargetFlags=*/0x1));
5722c8fe6a4SDan Gohman }
5732c8fe6a4SDan Gohman 
574950a13cfSDan Gohman SDValue WebAssemblyTargetLowering::LowerJumpTable(SDValue Op,
575950a13cfSDan Gohman                                                   SelectionDAG &DAG) const {
576950a13cfSDan Gohman   // There's no need for a Wrapper node because we always incorporate a jump
577bb7ce8e4SDan Gohman   // table operand into a TABLESWITCH instruction, rather than ever
578bb7ce8e4SDan Gohman   // materializing it in a register.
579950a13cfSDan Gohman   const JumpTableSDNode *JT = cast<JumpTableSDNode>(Op);
580950a13cfSDan Gohman   return DAG.getTargetJumpTable(JT->getIndex(), Op.getValueType(),
581950a13cfSDan Gohman                                 JT->getTargetFlags());
582950a13cfSDan Gohman }
583950a13cfSDan Gohman 
584950a13cfSDan Gohman SDValue WebAssemblyTargetLowering::LowerBR_JT(SDValue Op,
585950a13cfSDan Gohman                                               SelectionDAG &DAG) const {
586950a13cfSDan Gohman   SDLoc DL(Op);
587950a13cfSDan Gohman   SDValue Chain = Op.getOperand(0);
588950a13cfSDan Gohman   const auto *JT = cast<JumpTableSDNode>(Op.getOperand(1));
589950a13cfSDan Gohman   SDValue Index = Op.getOperand(2);
590950a13cfSDan Gohman   assert(JT->getTargetFlags() == 0 && "WebAssembly doesn't set target flags");
591950a13cfSDan Gohman 
592950a13cfSDan Gohman   SmallVector<SDValue, 8> Ops;
593950a13cfSDan Gohman   Ops.push_back(Chain);
594950a13cfSDan Gohman   Ops.push_back(Index);
595950a13cfSDan Gohman 
596950a13cfSDan Gohman   MachineJumpTableInfo *MJTI = DAG.getMachineFunction().getJumpTableInfo();
597950a13cfSDan Gohman   const auto &MBBs = MJTI->getJumpTables()[JT->getIndex()].MBBs;
598950a13cfSDan Gohman 
599950a13cfSDan Gohman   // TODO: For now, we just pick something arbitrary for a default case for now.
600950a13cfSDan Gohman   // We really want to sniff out the guard and put in the real default case (and
601950a13cfSDan Gohman   // delete the guard).
602950a13cfSDan Gohman   Ops.push_back(DAG.getBasicBlock(MBBs[0]));
603950a13cfSDan Gohman 
604950a13cfSDan Gohman   // Add an operand for each case.
605950a13cfSDan Gohman   for (auto MBB : MBBs)
606950a13cfSDan Gohman     Ops.push_back(DAG.getBasicBlock(MBB));
607950a13cfSDan Gohman 
608bb7ce8e4SDan Gohman   return DAG.getNode(WebAssemblyISD::TABLESWITCH, DL, MVT::Other, Ops);
609950a13cfSDan Gohman }
610950a13cfSDan Gohman 
61135bfb24cSDan Gohman SDValue WebAssemblyTargetLowering::LowerVASTART(SDValue Op,
61235bfb24cSDan Gohman                                                 SelectionDAG &DAG) const {
61335bfb24cSDan Gohman   SDLoc DL(Op);
61435bfb24cSDan Gohman   EVT PtrVT = getPointerTy(DAG.getMachineFunction().getDataLayout());
61535bfb24cSDan Gohman 
61635bfb24cSDan Gohman   // The incoming non-fixed arguments are placed on the top of the stack, with
61735bfb24cSDan Gohman   // natural alignment, at the point of the call, so the base pointer is just
61835bfb24cSDan Gohman   // the current frame pointer.
61935bfb24cSDan Gohman   DAG.getMachineFunction().getFrameInfo()->setFrameAddressIsTaken(true);
62035bfb24cSDan Gohman   unsigned FP =
621fd98ea89SDan Gohman       Subtarget->getRegisterInfo()->getFrameRegister(DAG.getMachineFunction());
62235bfb24cSDan Gohman   SDValue FrameAddr = DAG.getCopyFromReg(DAG.getEntryNode(), DL, FP, PtrVT);
62335bfb24cSDan Gohman   const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue();
62435bfb24cSDan Gohman   return DAG.getStore(Op.getOperand(0), DL, FrameAddr, Op.getOperand(1),
62535bfb24cSDan Gohman                       MachinePointerInfo(SV), false, false, 0);
62635bfb24cSDan Gohman }
62735bfb24cSDan Gohman 
62810e730a2SDan Gohman //===----------------------------------------------------------------------===//
62910e730a2SDan Gohman //                          WebAssembly Optimization Hooks
63010e730a2SDan Gohman //===----------------------------------------------------------------------===//
631