d95571e586f7fca8637b0dd60c75a9a84bb8f30d
[WebKit-https.git] / Source / JavaScriptCore / wasm / WasmAirIRGenerator.cpp
1 /*
2  * Copyright (C) 2019 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "WasmAirIRGenerator.h"
28
29 #if ENABLE(WEBASSEMBLY)
30
31 #include "AirCode.h"
32 #include "AirGenerate.h"
33 #include "AirOpcodeUtils.h"
34 #include "AirValidate.h"
35 #include "AllowMacroScratchRegisterUsageIf.h"
36 #include "B3CCallValue.h"
37 #include "B3CheckSpecial.h"
38 #include "B3CheckValue.h"
39 #include "B3PatchpointSpecial.h"
40 #include "B3Procedure.h"
41 #include "B3ProcedureInlines.h"
42 #include "BinarySwitch.h"
43 #include "ScratchRegisterAllocator.h"
44 #include "VirtualRegister.h"
45 #include "WasmCallingConvention.h"
46 #include "WasmContextInlines.h"
47 #include "WasmExceptionType.h"
48 #include "WasmFunctionParser.h"
49 #include "WasmInstance.h"
50 #include "WasmMemory.h"
51 #include "WasmOMGPlan.h"
52 #include "WasmOpcodeOrigin.h"
53 #include "WasmSignatureInlines.h"
54 #include "WasmThunks.h"
55 #include <limits>
56 #include <wtf/Box.h>
57 #include <wtf/Optional.h>
58 #include <wtf/StdLibExtras.h>
59
60 namespace JSC { namespace Wasm {
61
62 using namespace B3::Air;
63
64 struct ConstrainedTmp {
65     ConstrainedTmp(Tmp tmp)
66         : ConstrainedTmp(tmp, tmp.isReg() ? B3::ValueRep::reg(tmp.reg()) : B3::ValueRep::SomeRegister)
67     { }
68
69     ConstrainedTmp(Tmp tmp, B3::ValueRep rep)
70         : tmp(tmp)
71         , rep(rep)
72     {
73     }
74
75     Tmp tmp;
76     B3::ValueRep rep;
77 };
78
79 class TypedTmp {
80 public:
81     constexpr TypedTmp()
82         : m_tmp()
83         , m_type(Type::Void)
84     { }
85
86     TypedTmp(Tmp tmp, Type type)
87         : m_tmp(tmp)
88         , m_type(type)
89     { }
90
91     TypedTmp(const TypedTmp&) = default;
92     TypedTmp(TypedTmp&&) = default;
93     TypedTmp& operator=(TypedTmp&&) = default;
94     TypedTmp& operator=(const TypedTmp&) = default;
95
96     bool operator==(const TypedTmp& other) const
97     {
98         return m_tmp == other.m_tmp && m_type == other.m_type;
99     }
100     bool operator!=(const TypedTmp& other) const
101     {
102         return !(*this == other);
103     }
104
105     explicit operator bool() const { return !!tmp(); }
106
107     operator Tmp() const { return tmp(); }
108     operator Arg() const { return Arg(tmp()); }
109     Tmp tmp() const { return m_tmp; }
110     Type type() const { return m_type; }
111
112 private:
113
114     Tmp m_tmp;
115     Type m_type;
116 };
117
118 class AirIRGenerator {
119 public:
120     struct ControlData {
121         ControlData(B3::Origin origin, Type returnType, TypedTmp resultTmp, BlockType type, BasicBlock* continuation, BasicBlock* special = nullptr)
122             : blockType(type)
123             , continuation(continuation)
124             , special(special)
125             , returnType(returnType)
126         {
127             UNUSED_PARAM(origin); // FIXME: Use origin.
128             if (resultTmp) {
129                 ASSERT(returnType != Type::Void);
130                 result.append(resultTmp);
131             } else
132                 ASSERT(returnType == Type::Void);
133         }
134
135         ControlData()
136         {
137         }
138
139         void dump(PrintStream& out) const
140         {
141             switch (type()) {
142             case BlockType::If:
143                 out.print("If:       ");
144                 break;
145             case BlockType::Block:
146                 out.print("Block:    ");
147                 break;
148             case BlockType::Loop:
149                 out.print("Loop:     ");
150                 break;
151             case BlockType::TopLevel:
152                 out.print("TopLevel: ");
153                 break;
154             }
155             out.print("Continuation: ", *continuation, ", Special: ");
156             if (special)
157                 out.print(*special);
158             else
159                 out.print("None");
160         }
161
162         BlockType type() const { return blockType; }
163
164         Type signature() const { return returnType; }
165
166         bool hasNonVoidSignature() const { return result.size(); }
167
168         BasicBlock* targetBlockForBranch()
169         {
170             if (type() == BlockType::Loop)
171                 return special;
172             return continuation;
173         }
174
175         void convertIfToBlock()
176         {
177             ASSERT(type() == BlockType::If);
178             blockType = BlockType::Block;
179             special = nullptr;
180         }
181
182         using ResultList = Vector<TypedTmp, 1>;
183
184         ResultList resultForBranch() const
185         {
186             if (type() == BlockType::Loop)
187                 return ResultList();
188             return result;
189         }
190
191     private:
192         friend class AirIRGenerator;
193         BlockType blockType;
194         BasicBlock* continuation;
195         BasicBlock* special;
196         ResultList result;
197         Type returnType;
198     };
199
200     using ExpressionType = TypedTmp;
201     using ControlType = ControlData;
202     using ExpressionList = Vector<ExpressionType, 1>;
203     using ResultList = ControlData::ResultList;
204     using ControlEntry = FunctionParser<AirIRGenerator>::ControlEntry;
205
206     static ExpressionType emptyExpression() { return { }; };
207
208     using ErrorType = String;
209     using UnexpectedResult = Unexpected<ErrorType>;
210     using Result = Expected<std::unique_ptr<InternalFunction>, ErrorType>;
211     using PartialResult = Expected<void, ErrorType>;
212
213     template <typename ...Args>
214     NEVER_INLINE UnexpectedResult WARN_UNUSED_RETURN fail(Args... args) const
215     {
216         using namespace FailureHelper; // See ADL comment in WasmParser.h.
217         return UnexpectedResult(makeString("WebAssembly.Module failed compiling: "_s, makeString(args)...));
218     }
219
220 #define WASM_COMPILE_FAIL_IF(condition, ...) do { \
221         if (UNLIKELY(condition))                  \
222             return fail(__VA_ARGS__);             \
223     } while (0)
224
225     AirIRGenerator(const ModuleInformation&, B3::Procedure&, InternalFunction*, Vector<UnlinkedWasmToWasmCall>&, MemoryMode, unsigned functionIndex, TierUpCount*, ThrowWasmException, const Signature&);
226
227     PartialResult WARN_UNUSED_RETURN addArguments(const Signature&);
228     PartialResult WARN_UNUSED_RETURN addLocal(Type, uint32_t);
229     ExpressionType addConstant(Type, uint64_t);
230     ExpressionType addConstant(BasicBlock*, Type, uint64_t);
231
232     // Locals
233     PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
234     PartialResult WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
235
236     // Globals
237     PartialResult WARN_UNUSED_RETURN getGlobal(uint32_t index, ExpressionType& result);
238     PartialResult WARN_UNUSED_RETURN setGlobal(uint32_t index, ExpressionType value);
239
240     // Memory
241     PartialResult WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset);
242     PartialResult WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset);
243     PartialResult WARN_UNUSED_RETURN addGrowMemory(ExpressionType delta, ExpressionType& result);
244     PartialResult WARN_UNUSED_RETURN addCurrentMemory(ExpressionType& result);
245
246     // Basic operators
247     template<OpType>
248     PartialResult WARN_UNUSED_RETURN addOp(ExpressionType arg, ExpressionType& result);
249     template<OpType>
250     PartialResult WARN_UNUSED_RETURN addOp(ExpressionType left, ExpressionType right, ExpressionType& result);
251     PartialResult WARN_UNUSED_RETURN addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result);
252
253     // Control flow
254     ControlData WARN_UNUSED_RETURN addTopLevel(Type signature);
255     ControlData WARN_UNUSED_RETURN addBlock(Type signature);
256     ControlData WARN_UNUSED_RETURN addLoop(Type signature);
257     PartialResult WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData& result);
258     PartialResult WARN_UNUSED_RETURN addElse(ControlData&, const ExpressionList&);
259     PartialResult WARN_UNUSED_RETURN addElseToUnreachable(ControlData&);
260
261     PartialResult WARN_UNUSED_RETURN addReturn(const ControlData&, const ExpressionList& returnValues);
262     PartialResult WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& returnValues);
263     PartialResult WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTargets, const ExpressionList& expressionStack);
264     PartialResult WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack);
265     PartialResult WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&);
266
267     // Calls
268     PartialResult WARN_UNUSED_RETURN addCall(uint32_t calleeIndex, const Signature&, Vector<ExpressionType>& args, ExpressionType& result);
269     PartialResult WARN_UNUSED_RETURN addCallIndirect(const Signature&, Vector<ExpressionType>& args, ExpressionType& result);
270     PartialResult WARN_UNUSED_RETURN addUnreachable();
271
272     PartialResult addShift(Type, B3::Air::Opcode, ExpressionType value, ExpressionType shift, ExpressionType& result);
273     PartialResult addIntegerSub(B3::Air::Opcode, ExpressionType lhs, ExpressionType rhs, ExpressionType& result);
274     PartialResult addFloatingPointAbs(B3::Air::Opcode, ExpressionType value, ExpressionType& result);
275     PartialResult addFloatingPointBinOp(Type, B3::Air::Opcode, ExpressionType lhs, ExpressionType rhs, ExpressionType& result);
276
277     void dump(const Vector<ControlEntry>& controlStack, const ExpressionList* expressionStack);
278     void setParser(FunctionParser<AirIRGenerator>* parser) { m_parser = parser; };
279
280     static Vector<Tmp> toTmpVector(const Vector<TypedTmp>& vector)
281     {
282         Vector<Tmp> result;
283         for (const auto& item : vector)
284             result.append(item.tmp());
285         return result;
286     }
287
288     ALWAYS_INLINE void didKill(const ExpressionType& typedTmp)
289     {
290         Tmp tmp = typedTmp.tmp();
291         if (!tmp)
292             return;
293         if (tmp.isGP())
294             m_freeGPs.append(tmp);
295         else
296             m_freeFPs.append(tmp);
297     }
298
299 private:
300     ALWAYS_INLINE void validateInst(Inst& inst)
301     {
302         if (!ASSERT_DISABLED) {
303             if (!inst.isValidForm()) {
304                 dataLogLn(inst);
305                 CRASH();
306             }
307         }
308     }
309
310     static Arg extractArg(const TypedTmp& tmp) { return tmp.tmp(); }
311     static Arg extractArg(const Tmp& tmp) { return Arg(tmp); }
312     static Arg extractArg(const Arg& arg) { return arg; }
313
314     template<typename... Arguments>
315     void append(BasicBlock* block, Kind kind, Arguments&&... arguments)
316     {
317         // FIXME: Find a way to use origin here.
318         auto& inst = block->append(kind, nullptr, extractArg(arguments)...);
319         validateInst(inst);
320     }
321
322     template<typename... Arguments>
323     void append(Kind kind, Arguments&&... arguments)
324     {
325         append(m_currentBlock, kind, std::forward<Arguments>(arguments)...);
326     }
327
328     template<typename... Arguments>
329     void appendEffectful(B3::Air::Opcode op, Arguments&&... arguments)
330     {
331         Kind kind = op;
332         kind.effects = true;
333         append(m_currentBlock, kind, std::forward<Arguments>(arguments)...);
334     }
335
336     Tmp newTmp(B3::Bank bank)
337     {
338         switch (bank) {
339         case B3::GP:
340             if (m_freeGPs.size())
341                 return m_freeGPs.takeLast();
342             break;
343         case B3::FP:
344             if (m_freeFPs.size())
345                 return m_freeFPs.takeLast();
346             break;
347         }
348         return m_code.newTmp(bank);
349     }
350
351     TypedTmp g32() { return { newTmp(B3::GP), Type::I32 }; }
352     TypedTmp g64() { return { newTmp(B3::GP), Type::I64 }; }
353     TypedTmp f32() { return { newTmp(B3::FP), Type::F32 }; }
354     TypedTmp f64() { return { newTmp(B3::FP), Type::F64 }; }
355
356     TypedTmp tmpForType(Type type)
357     {
358         switch (type) {
359         case Type::I32:
360             return g32();
361         case Type::I64:
362             return g64();
363         case Type::F32:
364             return f32();
365         case Type::F64:
366             return f64();
367         case Type::Void:
368             return { };
369         default:
370             RELEASE_ASSERT_NOT_REACHED();
371         }
372     }
373
374     B3::PatchpointValue* addPatchpoint(B3::Type type)
375     {
376         return m_proc.add<B3::PatchpointValue>(type, B3::Origin());
377     }
378
379     template <typename ...Args>
380     void emitPatchpoint(B3::PatchpointValue* patch, Tmp result, Args... theArgs)
381     {
382         emitPatchpoint(m_currentBlock, patch, result, std::forward<Args>(theArgs)...);
383     }
384
385     template <typename ...Args>
386     void emitPatchpoint(BasicBlock* basicBlock, B3::PatchpointValue* patch, Tmp result, Args... theArgs)
387     {
388         emitPatchpoint(basicBlock, patch, result, Vector<ConstrainedTmp, sizeof...(Args)>::from(theArgs...));
389     }
390
391     void emitPatchpoint(BasicBlock* basicBlock, B3::PatchpointValue* patch, Tmp result)
392     {
393         emitPatchpoint(basicBlock, patch, result, Vector<ConstrainedTmp>());
394     }
395
396     template <size_t inlineSize>
397     void emitPatchpoint(BasicBlock* basicBlock, B3::PatchpointValue* patch, Tmp result, Vector<ConstrainedTmp, inlineSize>&& args)
398     {
399         if (!m_patchpointSpecial)
400             m_patchpointSpecial = static_cast<B3::PatchpointSpecial*>(m_code.addSpecial(std::make_unique<B3::PatchpointSpecial>()));
401
402         Inst inst(Patch, patch, Arg::special(m_patchpointSpecial));
403         Inst resultMov;
404         if (result) {
405             ASSERT(patch->type() != B3::Void);
406             switch (patch->resultConstraint.kind()) {
407             case B3::ValueRep::Register:
408                 inst.args.append(Tmp(patch->resultConstraint.reg()));
409                 resultMov = Inst(result.isGP() ? Move : MoveDouble, nullptr, Tmp(patch->resultConstraint.reg()), result);
410                 break;
411             case B3::ValueRep::SomeRegister:
412                 inst.args.append(result);
413                 break;
414             default:
415                 RELEASE_ASSERT_NOT_REACHED();
416             }
417         } else
418             ASSERT(patch->type() == B3::Void);
419         
420         for (ConstrainedTmp& tmp : args) {
421             // FIXME: This is less than ideal to create dummy values just to satisfy Air's
422             // validation. We should abstrcat Patch enough so ValueRep's don't need to be
423             // backed by Values.
424             // https://bugs.webkit.org/show_bug.cgi?id=194040
425             B3::Value* dummyValue = m_proc.addConstant(B3::Origin(), tmp.tmp.isGP() ? B3::Int64 : B3::Double, 0);
426             patch->append(dummyValue, tmp.rep);
427             switch (tmp.rep.kind()) {
428             case B3::ValueRep::SomeRegister:
429                 inst.args.append(tmp.tmp);
430                 break;
431             case B3::ValueRep::Register:
432                 patch->earlyClobbered().clear(tmp.rep.reg());
433                 append(basicBlock, tmp.tmp.isGP() ? Move : MoveDouble, tmp.tmp, tmp.rep.reg());
434                 inst.args.append(Tmp(tmp.rep.reg()));
435                 break;
436             case B3::ValueRep::StackArgument: {
437                 auto arg = Arg::callArg(tmp.rep.offsetFromSP());
438                 append(basicBlock, tmp.tmp.isGP() ? Move : MoveDouble, tmp.tmp, arg);
439                 inst.args.append(arg);
440                 break;
441             }
442             default:
443                 RELEASE_ASSERT_NOT_REACHED();
444             }
445         }
446
447         if (patch->resultConstraint.isReg())
448             patch->lateClobbered().clear(patch->resultConstraint.reg());
449         for (unsigned i = patch->numGPScratchRegisters; i--;)
450             inst.args.append(g64().tmp());
451         for (unsigned i = patch->numFPScratchRegisters; i--;)
452             inst.args.append(f64().tmp());
453
454         validateInst(inst);
455         basicBlock->append(WTFMove(inst));
456         if (resultMov) {
457             validateInst(resultMov);
458             basicBlock->append(WTFMove(resultMov));
459         }
460     }
461
462     template <typename Branch, typename Generator>
463     void emitCheck(const Branch& makeBranch, const Generator& generator)
464     {
465         // We fail along the truthy edge of 'branch'.
466         Inst branch = makeBranch();
467
468         // FIXME: Make a hashmap of these.
469         B3::CheckSpecial::Key key(branch);
470         B3::CheckSpecial* special = static_cast<B3::CheckSpecial*>(m_code.addSpecial(std::make_unique<B3::CheckSpecial>(key)));
471
472         // FIXME: Remove the need for dummy values
473         // https://bugs.webkit.org/show_bug.cgi?id=194040
474         B3::Value* dummyPredicate = m_proc.addConstant(B3::Origin(), B3::Int32, 42);
475         B3::CheckValue* checkValue = m_proc.add<B3::CheckValue>(B3::Check, B3::Origin(), dummyPredicate);
476         checkValue->setGenerator(generator);
477
478         Inst inst(Patch, checkValue, Arg::special(special));
479         inst.args.appendVector(branch.args);
480         m_currentBlock->append(WTFMove(inst));
481     }
482
483     template <typename Func, typename ...Args>
484     void emitCCall(Func func, TypedTmp result, Args... args)
485     {
486         emitCCall(m_currentBlock, func, result, std::forward<Args>(args)...);
487     }
488     template <typename Func, typename ...Args>
489     void emitCCall(BasicBlock* block, Func func, TypedTmp result, Args... theArgs)
490     {
491         B3::Type resultType = B3::Void;
492         if (result) {
493             switch (result.type()) {
494             case Type::I32:
495                 resultType = B3::Int32;
496                 break;
497             case Type::I64:
498                 resultType = B3::Int64;
499                 break;
500             case Type::F32:
501                 resultType = B3::Float;
502                 break;
503             case Type::F64:
504                 resultType = B3::Double;
505                 break;
506             default:
507                 RELEASE_ASSERT_NOT_REACHED();
508             }
509         }
510
511         auto makeDummyValue = [&] (Tmp tmp) {
512             // FIXME: This is less than ideal to create dummy values just to satisfy Air's
513             // validation. We should abstrcat CCall enough so we're not reliant on arguments
514             // to the B3::CCallValue.
515             // https://bugs.webkit.org/show_bug.cgi?id=194040
516             if (tmp.isGP())
517                 return m_proc.addConstant(B3::Origin(), B3::Int64, 0);
518             return m_proc.addConstant(B3::Origin(), B3::Double, 0);
519         };
520
521         B3::Value* dummyFunc = m_proc.addConstant(B3::Origin(), B3::Int64, bitwise_cast<uintptr_t>(func));
522         B3::Value* origin = m_proc.add<B3::CCallValue>(resultType, B3::Origin(), B3::Effects::none(), dummyFunc, makeDummyValue(theArgs)...);
523
524         Inst inst(CCall, origin);
525
526         Tmp callee = g64();
527         append(Move, Arg::immPtr(tagCFunctionPtr<void*>(func, B3CCallPtrTag)), callee);
528         inst.args.append(callee);
529
530         if (result)
531             inst.args.append(result.tmp());
532
533         for (Tmp tmp : Vector<Tmp, sizeof...(Args)>::from(theArgs.tmp()...))
534             inst.args.append(tmp);
535
536         block->append(WTFMove(inst));
537     }
538
539     static B3::Air::Opcode moveOpForValueType(Type type)
540     {
541         switch (type) {
542         case Type::I32:
543             return Move32;
544         case Type::I64:
545             return Move;
546         case Type::F32:
547             return MoveFloat;
548         case Type::F64:
549             return MoveDouble;
550         default:
551             RELEASE_ASSERT_NOT_REACHED();
552         }
553     }
554
555     void emitThrowException(CCallHelpers&, ExceptionType);
556
557     void emitTierUpCheck(uint32_t decrementCount, B3::Origin);
558
559     ExpressionType emitCheckAndPreparePointer(ExpressionType pointer, uint32_t offset, uint32_t sizeOfOp);
560     ExpressionType emitLoadOp(LoadOpType, ExpressionType pointer, uint32_t offset);
561     void emitStoreOp(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset);
562
563     void unify(const ExpressionType& dst, const ExpressionType& source);
564     void unifyValuesWithBlock(const ExpressionList& resultStack, const ResultList& stack);
565
566     template <typename IntType>
567     void emitChecksForModOrDiv(bool isSignedDiv, ExpressionType left, ExpressionType right);
568
569     template <typename IntType>
570     void emitModOrDiv(bool isDiv, ExpressionType lhs, ExpressionType rhs, ExpressionType& result);
571
572     enum class MinOrMax { Min, Max };
573
574     PartialResult addFloatingPointMinOrMax(Type, MinOrMax, ExpressionType lhs, ExpressionType rhs, ExpressionType& result);
575
576     int32_t WARN_UNUSED_RETURN fixupPointerPlusOffset(ExpressionType&, uint32_t);
577
578     void restoreWasmContextInstance(BasicBlock*, TypedTmp);
579     enum class RestoreCachedStackLimit { No, Yes };
580     void restoreWebAssemblyGlobalState(RestoreCachedStackLimit, const MemoryInformation&, TypedTmp instance, BasicBlock*);
581
582     B3::Origin origin();
583
584     FunctionParser<AirIRGenerator>* m_parser { nullptr };
585     const ModuleInformation& m_info;
586     const MemoryMode m_mode { MemoryMode::BoundsChecking };
587     const unsigned m_functionIndex { UINT_MAX };
588     const TierUpCount* m_tierUp { nullptr };
589
590     B3::Procedure& m_proc;
591     Code& m_code;
592     BasicBlock* m_currentBlock { nullptr };
593     BasicBlock* m_rootBlock { nullptr };
594     Vector<TypedTmp> m_locals;
595     Vector<UnlinkedWasmToWasmCall>& m_unlinkedWasmToWasmCalls; // List each call site and the function index whose address it should be patched with.
596     GPRReg m_memoryBaseGPR { InvalidGPRReg };
597     GPRReg m_memorySizeGPR { InvalidGPRReg };
598     GPRReg m_wasmContextInstanceGPR { InvalidGPRReg };
599     bool m_makesCalls { false };
600
601     Vector<Tmp, 8> m_freeGPs;
602     Vector<Tmp, 8> m_freeFPs;
603
604     TypedTmp m_instanceValue; // Always use the accessor below to ensure the instance value is materialized when used.
605     bool m_usesInstanceValue { false };
606     TypedTmp instanceValue()
607     {
608         m_usesInstanceValue = true;
609         return m_instanceValue;
610     }
611
612     uint32_t m_maxNumJSCallArguments { 0 };
613
614     B3::PatchpointSpecial* m_patchpointSpecial { nullptr };
615 };
616
617 // Memory accesses in WebAssembly have unsigned 32-bit offsets, whereas they have signed 32-bit offsets in B3.
618 int32_t AirIRGenerator::fixupPointerPlusOffset(ExpressionType& ptr, uint32_t offset)
619 {
620     if (static_cast<uint64_t>(offset) > static_cast<uint64_t>(std::numeric_limits<int32_t>::max())) {
621         auto previousPtr = ptr;
622         ptr = g64();
623         auto constant = g64();
624         append(Move, Arg::bigImm(offset), constant);
625         append(Add64, constant, previousPtr, ptr);
626         return 0;
627     }
628     return offset;
629 }
630
631 void AirIRGenerator::restoreWasmContextInstance(BasicBlock* block, TypedTmp instance)
632 {
633     if (Context::useFastTLS()) {
634         auto* patchpoint = addPatchpoint(B3::Void);
635         if (CCallHelpers::storeWasmContextInstanceNeedsMacroScratchRegister())
636             patchpoint->clobber(RegisterSet::macroScratchRegisters());
637         patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
638             AllowMacroScratchRegisterUsageIf allowScratch(jit, CCallHelpers::storeWasmContextInstanceNeedsMacroScratchRegister());
639             jit.storeWasmContextInstance(params[0].gpr());
640         });
641         emitPatchpoint(block, patchpoint, Tmp(), instance);
642         return;
643     }
644
645     // FIXME: Because WasmToWasm call clobbers wasmContextInstance register and does not restore it, we need to restore it in the caller side.
646     // This prevents us from using ArgumentReg to this (logically) immutable pinned register.
647     auto* patchpoint = addPatchpoint(B3::Void);
648     B3::Effects effects = B3::Effects::none();
649     effects.writesPinned = true;
650     effects.reads = B3::HeapRange::top();
651     patchpoint->effects = effects;
652     patchpoint->clobberLate(RegisterSet(m_wasmContextInstanceGPR));
653     GPRReg wasmContextInstanceGPR = m_wasmContextInstanceGPR;
654     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& param) {
655         jit.move(param[0].gpr(), wasmContextInstanceGPR);
656     });
657     emitPatchpoint(block, patchpoint, Tmp(), instance);
658 }
659
660 AirIRGenerator::AirIRGenerator(const ModuleInformation& info, B3::Procedure& procedure, InternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, MemoryMode mode, unsigned functionIndex, TierUpCount* tierUp, ThrowWasmException throwWasmException, const Signature& signature)
661     : m_info(info)
662     , m_mode(mode)
663     , m_functionIndex(functionIndex)
664     , m_tierUp(tierUp)
665     , m_proc(procedure)
666     , m_code(m_proc.code())
667     , m_unlinkedWasmToWasmCalls(unlinkedWasmToWasmCalls)
668 {
669     m_currentBlock = m_code.addBlock();
670     m_rootBlock = m_currentBlock;
671
672     // FIXME we don't really need to pin registers here if there's no memory. It makes wasm -> wasm thunks simpler for now. https://bugs.webkit.org/show_bug.cgi?id=166623
673     const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get();
674
675     m_memoryBaseGPR = pinnedRegs.baseMemoryPointer;
676     m_code.pinRegister(m_memoryBaseGPR);
677
678     m_wasmContextInstanceGPR = pinnedRegs.wasmContextInstancePointer;
679     if (!Context::useFastTLS())
680         m_code.pinRegister(m_wasmContextInstanceGPR);
681
682     if (mode != MemoryMode::Signaling) {
683         ASSERT(!pinnedRegs.sizeRegisters[0].sizeOffset);
684         m_memorySizeGPR = pinnedRegs.sizeRegisters[0].sizeRegister;
685         for (const PinnedSizeRegisterInfo& regInfo : pinnedRegs.sizeRegisters)
686             m_code.pinRegister(regInfo.sizeRegister);
687     }
688
689     if (throwWasmException)
690         Thunks::singleton().setThrowWasmException(throwWasmException);
691
692     if (info.memory) {
693         switch (m_mode) {
694         case MemoryMode::BoundsChecking:
695             break;
696         case MemoryMode::Signaling:
697             // Most memory accesses in signaling mode don't do an explicit
698             // exception check because they can rely on fault handling to detect
699             // out-of-bounds accesses. FaultSignalHandler nonetheless needs the
700             // thunk to exist so that it can jump to that thunk.
701             if (UNLIKELY(!Thunks::singleton().stub(throwExceptionFromWasmThunkGenerator)))
702                 CRASH();
703             break;
704         }
705     }
706
707     m_code.setNumEntrypoints(1);
708
709     GPRReg contextInstance = Context::useFastTLS() ? wasmCallingConventionAir().prologueScratch(1) : m_wasmContextInstanceGPR;
710
711     Ref<B3::Air::PrologueGenerator> prologueGenerator = createSharedTask<B3::Air::PrologueGeneratorFunction>([=] (CCallHelpers& jit, B3::Air::Code& code) {
712         AllowMacroScratchRegisterUsage allowScratch(jit);
713         code.emitDefaultPrologue(jit);
714
715         {
716             GPRReg calleeGPR = wasmCallingConventionAir().prologueScratch(0);
717             auto moveLocation = jit.moveWithPatch(MacroAssembler::TrustedImmPtr(nullptr), calleeGPR);
718             jit.addLinkTask([compilation, moveLocation] (LinkBuffer& linkBuffer) {
719                 compilation->calleeMoveLocation = linkBuffer.locationOf<WasmEntryPtrTag>(moveLocation);
720             });
721             jit.emitPutToCallFrameHeader(calleeGPR, CallFrameSlot::callee);
722             jit.emitPutToCallFrameHeader(nullptr, CallFrameSlot::codeBlock);
723         }
724
725         {
726             const Checked<int32_t> wasmFrameSize = m_code.frameSize();
727             const unsigned minimumParentCheckSize = WTF::roundUpToMultipleOf(stackAlignmentBytes(), 1024);
728             const unsigned extraFrameSize = WTF::roundUpToMultipleOf(stackAlignmentBytes(), std::max<uint32_t>(
729                 // This allows us to elide stack checks for functions that are terminal nodes in the call
730                 // tree, (e.g they don't make any calls) and have a small enough frame size. This works by
731                 // having any such terminal node have its parent caller include some extra size in its
732                 // own check for it. The goal here is twofold:
733                 // 1. Emit less code.
734                 // 2. Try to speed things up by skipping stack checks.
735                 minimumParentCheckSize,
736                 // This allows us to elide stack checks in the Wasm -> Embedder call IC stub. Since these will
737                 // spill all arguments to the stack, we ensure that a stack check here covers the
738                 // stack that such a stub would use.
739                 (Checked<uint32_t>(m_maxNumJSCallArguments) * sizeof(Register) + jscCallingConvention().headerSizeInBytes()).unsafeGet()
740             ));
741             const int32_t checkSize = m_makesCalls ? (wasmFrameSize + extraFrameSize).unsafeGet() : wasmFrameSize.unsafeGet();
742             bool needUnderflowCheck = static_cast<unsigned>(checkSize) > Options::reservedZoneSize();
743             bool needsOverflowCheck = m_makesCalls || wasmFrameSize >= minimumParentCheckSize || needUnderflowCheck;
744
745             // This allows leaf functions to not do stack checks if their frame size is within
746             // certain limits since their caller would have already done the check.
747             if (needsOverflowCheck) {
748                 GPRReg scratch = wasmCallingConventionAir().prologueScratch(0);
749
750                 if (Context::useFastTLS())
751                     jit.loadWasmContextInstance(contextInstance);
752
753                 jit.addPtr(CCallHelpers::TrustedImm32(-checkSize), GPRInfo::callFrameRegister, scratch);
754                 MacroAssembler::JumpList overflow;
755                 if (UNLIKELY(needUnderflowCheck))
756                     overflow.append(jit.branchPtr(CCallHelpers::Above, scratch, GPRInfo::callFrameRegister));
757                 overflow.append(jit.branchPtr(CCallHelpers::Below, scratch, CCallHelpers::Address(contextInstance, Instance::offsetOfCachedStackLimit())));
758                 jit.addLinkTask([overflow] (LinkBuffer& linkBuffer) {
759                     linkBuffer.link(overflow, CodeLocationLabel<JITThunkPtrTag>(Thunks::singleton().stub(throwStackOverflowFromWasmThunkGenerator).code()));
760                 });
761             } else if (m_usesInstanceValue && Context::useFastTLS()) {
762                 // No overflow check is needed, but the instance values still needs to be correct.
763                 jit.loadWasmContextInstance(contextInstance);
764             }
765         }
766     });
767
768     m_code.setPrologueForEntrypoint(0, WTFMove(prologueGenerator));
769
770     if (Context::useFastTLS()) {
771         m_instanceValue = g64();
772         // FIXME: Would be nice to only do this if we use instance value.
773         append(Move, Tmp(contextInstance), m_instanceValue);
774     } else
775         m_instanceValue = { Tmp(contextInstance), Type::I64 };
776
777     ASSERT(!m_locals.size());
778     m_locals.grow(signature.argumentCount());
779     for (unsigned i = 0; i < signature.argumentCount(); ++i) {
780         Type type = signature.argument(i);
781         m_locals[i] = tmpForType(type);
782     }
783
784     wasmCallingConventionAir().loadArguments(signature, [&] (const Arg& arg, unsigned i) {
785         switch (signature.argument(i)) {
786         case Type::I32:
787             append(Move32, arg, m_locals[i]);
788             break;
789         case Type::I64:
790             append(Move, arg, m_locals[i]);
791             break;
792         case Type::F32:
793             append(MoveFloat, arg, m_locals[i]);
794             break;
795         case Type::F64:
796             append(MoveDouble, arg, m_locals[i]);
797             break;
798         default:
799             RELEASE_ASSERT_NOT_REACHED();
800         }
801     });
802
803     emitTierUpCheck(TierUpCount::functionEntryDecrement(), B3::Origin());
804 }
805
806 void AirIRGenerator::restoreWebAssemblyGlobalState(RestoreCachedStackLimit restoreCachedStackLimit, const MemoryInformation& memory, TypedTmp instance, BasicBlock* block)
807 {
808     restoreWasmContextInstance(block, instance);
809
810     if (restoreCachedStackLimit == RestoreCachedStackLimit::Yes) {
811         // The Instance caches the stack limit, but also knows where its canonical location is.
812         static_assert(sizeof(decltype(static_cast<Instance*>(nullptr)->cachedStackLimit())) == sizeof(uint64_t), "");
813
814         RELEASE_ASSERT(Arg::isValidAddrForm(Instance::offsetOfPointerToActualStackLimit(), B3::Width64));
815         RELEASE_ASSERT(Arg::isValidAddrForm(Instance::offsetOfCachedStackLimit(), B3::Width64));
816         auto temp = g64();
817         append(block, Move, Arg::addr(instanceValue(), Instance::offsetOfPointerToActualStackLimit()), temp);
818         append(block, Move, Arg::addr(temp), temp);
819         append(block, Move, temp, Arg::addr(instanceValue(), Instance::offsetOfCachedStackLimit()));
820     }
821
822     if (!!memory) {
823         const PinnedRegisterInfo* pinnedRegs = &PinnedRegisterInfo::get();
824         RegisterSet clobbers;
825         clobbers.set(pinnedRegs->baseMemoryPointer);
826         for (auto info : pinnedRegs->sizeRegisters)
827             clobbers.set(info.sizeRegister);
828
829         auto* patchpoint = addPatchpoint(B3::Void);
830         B3::Effects effects = B3::Effects::none();
831         effects.writesPinned = true;
832         effects.reads = B3::HeapRange::top();
833         patchpoint->effects = effects;
834         patchpoint->clobber(clobbers);
835
836         patchpoint->setGenerator([pinnedRegs] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
837             GPRReg baseMemory = pinnedRegs->baseMemoryPointer;
838             const auto& sizeRegs = pinnedRegs->sizeRegisters;
839             ASSERT(sizeRegs.size() >= 1);
840             ASSERT(!sizeRegs[0].sizeOffset); // The following code assumes we start at 0, and calculates subsequent size registers relative to 0.
841             jit.loadPtr(CCallHelpers::Address(params[0].gpr(), Instance::offsetOfCachedMemorySize()), sizeRegs[0].sizeRegister);
842             jit.loadPtr(CCallHelpers::Address(params[0].gpr(), Instance::offsetOfCachedMemory()), baseMemory);
843             for (unsigned i = 1; i < sizeRegs.size(); ++i)
844                 jit.add64(CCallHelpers::TrustedImm32(-sizeRegs[i].sizeOffset), sizeRegs[0].sizeRegister, sizeRegs[i].sizeRegister);
845         });
846
847         emitPatchpoint(block, patchpoint, Tmp(), instance);
848     }
849 }
850
851 void AirIRGenerator::emitThrowException(CCallHelpers& jit, ExceptionType type)
852 {
853     jit.move(CCallHelpers::TrustedImm32(static_cast<uint32_t>(type)), GPRInfo::argumentGPR1);
854     auto jumpToExceptionStub = jit.jump();
855
856     jit.addLinkTask([jumpToExceptionStub] (LinkBuffer& linkBuffer) {
857         linkBuffer.link(jumpToExceptionStub, CodeLocationLabel<JITThunkPtrTag>(Thunks::singleton().stub(throwExceptionFromWasmThunkGenerator).code()));
858     });
859 }
860
861 auto AirIRGenerator::addLocal(Type type, uint32_t count) -> PartialResult
862 {
863     Checked<uint32_t, RecordOverflow> totalBytesChecked = count;
864     totalBytesChecked += m_locals.size();
865     uint32_t totalBytes;
866     WASM_COMPILE_FAIL_IF((totalBytesChecked.safeGet(totalBytes) == CheckedState::DidOverflow) || !m_locals.tryReserveCapacity(totalBytes), "can't allocate memory for ", totalBytes, " locals");
867
868     for (uint32_t i = 0; i < count; ++i) {
869         auto local = tmpForType(type);
870         m_locals.uncheckedAppend(local);
871         switch (type) {
872         case Type::I32:
873         case Type::I64: {
874             append(Xor64, local, local);
875             break;
876         }
877         case Type::F32:
878         case Type::F64: {
879             auto temp = g64();
880             // IEEE 754 "0" is just int32/64 zero.
881             append(Xor64, temp, temp);
882             append(type == Type::F32 ? Move32ToFloat : Move64ToDouble, temp, local);
883             break;
884         }
885         default:
886             RELEASE_ASSERT_NOT_REACHED();
887         }
888     }
889     return { };
890 }
891
892 auto AirIRGenerator::addConstant(Type type, uint64_t value) -> ExpressionType
893 {
894     return addConstant(m_currentBlock, type, value);
895 }
896
897 auto AirIRGenerator::addConstant(BasicBlock* block, Type type, uint64_t value) -> ExpressionType
898 {
899     auto result = tmpForType(type);
900     switch (type) {
901     case Type::I32:
902     case Type::I64:
903         append(block, Move, Arg::bigImm(value), result);
904         break;
905     case Type::F32:
906     case Type::F64: {
907         auto tmp = g64();
908         append(block, Move, Arg::bigImm(value), tmp);
909         append(block, type == Type::F32 ? Move32ToFloat : Move64ToDouble, tmp, result);
910         break;
911     }
912
913     default:
914         RELEASE_ASSERT_NOT_REACHED();
915     }
916
917     return result;
918 }
919
920 auto AirIRGenerator::addArguments(const Signature& signature) -> PartialResult
921 {
922     RELEASE_ASSERT(m_locals.size() == signature.argumentCount()); // We handle arguments in the prologue
923     return { };
924 }
925
926 auto AirIRGenerator::getLocal(uint32_t index, ExpressionType& result) -> PartialResult
927 {
928     ASSERT(m_locals[index].tmp());
929     result = tmpForType(m_locals[index].type());
930     append(moveOpForValueType(m_locals[index].type()), m_locals[index].tmp(), result);
931     return { };
932 }
933
934 auto AirIRGenerator::addUnreachable() -> PartialResult
935 {
936     B3::PatchpointValue* unreachable = addPatchpoint(B3::Void);
937     unreachable->setGenerator([this] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
938         this->emitThrowException(jit, ExceptionType::Unreachable);
939     });
940     unreachable->effects.terminal = true;
941     emitPatchpoint(unreachable, Tmp());
942     return { };
943 }
944
945 auto AirIRGenerator::addGrowMemory(ExpressionType delta, ExpressionType& result) -> PartialResult
946 {
947     int32_t (*growMemory)(void*, Instance*, int32_t) = [] (void* callFrame, Instance* instance, int32_t delta) -> int32_t {
948         instance->storeTopCallFrame(callFrame);
949
950         if (delta < 0)
951             return -1;
952
953         auto grown = instance->memory()->grow(PageCount(delta));
954         if (!grown) {
955             switch (grown.error()) {
956             case Memory::GrowFailReason::InvalidDelta:
957             case Memory::GrowFailReason::InvalidGrowSize:
958             case Memory::GrowFailReason::WouldExceedMaximum:
959             case Memory::GrowFailReason::OutOfMemory:
960                 return -1;
961             }
962             RELEASE_ASSERT_NOT_REACHED();
963         }
964
965         return grown.value().pageCount();
966     };
967
968     result = g32();
969     emitCCall(growMemory, result, TypedTmp { Tmp(GPRInfo::callFrameRegister), Type::I64 }, instanceValue(), delta);
970     restoreWebAssemblyGlobalState(RestoreCachedStackLimit::No, m_info.memory, instanceValue(), m_currentBlock);
971
972     return { };
973 }
974
975 auto AirIRGenerator::addCurrentMemory(ExpressionType& result) -> PartialResult
976 {
977     static_assert(sizeof(decltype(static_cast<Memory*>(nullptr)->size())) == sizeof(uint64_t), "codegen relies on this size");
978
979     auto temp1 = g64();
980     auto temp2 = g64();
981
982     RELEASE_ASSERT(Arg::isValidAddrForm(Instance::offsetOfCachedMemorySize(), B3::Width64));
983     append(Move, Arg::addr(instanceValue(), Instance::offsetOfCachedMemorySize()), temp1);
984     constexpr uint32_t shiftValue = 16;
985     static_assert(PageCount::pageSize == 1ull << shiftValue, "This must hold for the code below to be correct.");
986     append(Move, Arg::imm(16), temp2);
987     addShift(Type::I32, Urshift64, temp1, temp2, result);
988     append(Move32, result, result);
989
990     return { };
991 }
992
993 auto AirIRGenerator::setLocal(uint32_t index, ExpressionType value) -> PartialResult
994 {
995     ASSERT(m_locals[index].tmp());
996     append(moveOpForValueType(m_locals[index].type()), value, m_locals[index].tmp());
997     return { };
998 }
999
1000 auto AirIRGenerator::getGlobal(uint32_t index, ExpressionType& result) -> PartialResult
1001 {
1002     Type type = m_info.globals[index].type;
1003
1004     result = tmpForType(type);
1005
1006     auto temp = g64();
1007
1008     RELEASE_ASSERT(Arg::isValidAddrForm(Instance::offsetOfGlobals(), B3::Width64));
1009     append(Move, Arg::addr(instanceValue(), Instance::offsetOfGlobals()), temp);
1010
1011     int32_t offset = safeCast<int32_t>(index * sizeof(Register));
1012     if (Arg::isValidAddrForm(offset, B3::widthForType(toB3Type(type))))
1013         append(moveOpForValueType(type), Arg::addr(temp, offset), result);
1014     else {
1015         auto temp2 = g64();
1016         append(Move, Arg::bigImm(offset), temp2);
1017         append(Add64, temp2, temp, temp);
1018         append(moveOpForValueType(type), Arg::addr(temp), result);
1019     }
1020     return { };
1021 }
1022
1023 auto AirIRGenerator::setGlobal(uint32_t index, ExpressionType value) -> PartialResult
1024 {
1025     auto temp = g64();
1026
1027     RELEASE_ASSERT(Arg::isValidAddrForm(Instance::offsetOfGlobals(), B3::Width64));
1028     append(Move, Arg::addr(instanceValue(), Instance::offsetOfGlobals()), temp);
1029
1030     Type type = m_info.globals[index].type;
1031
1032     int32_t offset = safeCast<int32_t>(index * sizeof(Register));
1033     if (Arg::isValidAddrForm(offset, B3::widthForType(toB3Type(type))))
1034         append(moveOpForValueType(type), value, Arg::addr(temp, offset));
1035     else {
1036         auto temp2 = g64();
1037         append(Move, Arg::bigImm(offset), temp2);
1038         append(Add64, temp2, temp, temp);
1039         append(moveOpForValueType(type), value, Arg::addr(temp));
1040     }
1041
1042     return { };
1043 }
1044
1045 inline AirIRGenerator::ExpressionType AirIRGenerator::emitCheckAndPreparePointer(ExpressionType pointer, uint32_t offset, uint32_t sizeOfOperation)
1046 {
1047     ASSERT(m_memoryBaseGPR);
1048
1049     auto result = g64();
1050     append(Move32, pointer, result);
1051
1052     switch (m_mode) {
1053     case MemoryMode::BoundsChecking: {
1054         // We're not using signal handling at all, we must therefore check that no memory access exceeds the current memory size.
1055         ASSERT(m_memorySizeGPR);
1056         ASSERT(sizeOfOperation + offset > offset);
1057         auto temp = g64();
1058         append(Move, Arg::bigImm(static_cast<uint64_t>(sizeOfOperation) + offset - 1), temp);
1059         append(Add64, result, temp);
1060
1061         emitCheck([&] {
1062             return Inst(Branch64, nullptr, Arg::relCond(MacroAssembler::AboveOrEqual), temp, Tmp(m_memorySizeGPR));
1063         }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1064             this->emitThrowException(jit, ExceptionType::OutOfBoundsMemoryAccess);
1065         });
1066         break;
1067     }
1068
1069     case MemoryMode::Signaling: {
1070         // We've virtually mapped 4GiB+redzone for this memory. Only the user-allocated pages are addressable, contiguously in range [0, current],
1071         // and everything above is mapped PROT_NONE. We don't need to perform any explicit bounds check in the 4GiB range because WebAssembly register
1072         // memory accesses are 32-bit. However WebAssembly register + offset accesses perform the addition in 64-bit which can push an access above
1073         // the 32-bit limit (the offset is unsigned 32-bit). The redzone will catch most small offsets, and we'll explicitly bounds check any
1074         // register + large offset access. We don't think this will be generated frequently.
1075         //
1076         // We could check that register + large offset doesn't exceed 4GiB+redzone since that's technically the limit we need to avoid overflowing the
1077         // PROT_NONE region, but it's better if we use a smaller immediate because it can codegens better. We know that anything equal to or greater
1078         // than the declared 'maximum' will trap, so we can compare against that number. If there was no declared 'maximum' then we still know that
1079         // any access equal to or greater than 4GiB will trap, no need to add the redzone.
1080         if (offset >= Memory::fastMappedRedzoneBytes()) {
1081             uint64_t maximum = m_info.memory.maximum() ? m_info.memory.maximum().bytes() : std::numeric_limits<uint32_t>::max();
1082             auto temp = g64();
1083             append(Move, Arg::bigImm(static_cast<uint64_t>(sizeOfOperation) + offset - 1), temp);
1084             append(Add64, result, temp);
1085             auto sizeMax = addConstant(Type::I64, maximum);
1086
1087             emitCheck([&] {
1088                 return Inst(Branch64, nullptr, Arg::relCond(MacroAssembler::AboveOrEqual), temp, sizeMax);
1089             }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1090                 this->emitThrowException(jit, ExceptionType::OutOfBoundsMemoryAccess);
1091             });
1092         }
1093         break;
1094     }
1095     }
1096
1097     append(Add64, Tmp(m_memoryBaseGPR), result);
1098     return result;
1099 }
1100
1101 inline uint32_t sizeOfLoadOp(LoadOpType op)
1102 {
1103     switch (op) {
1104     case LoadOpType::I32Load8S:
1105     case LoadOpType::I32Load8U:
1106     case LoadOpType::I64Load8S:
1107     case LoadOpType::I64Load8U:
1108         return 1;
1109     case LoadOpType::I32Load16S:
1110     case LoadOpType::I64Load16S:
1111     case LoadOpType::I32Load16U:
1112     case LoadOpType::I64Load16U:
1113         return 2;
1114     case LoadOpType::I32Load:
1115     case LoadOpType::I64Load32S:
1116     case LoadOpType::I64Load32U:
1117     case LoadOpType::F32Load:
1118         return 4;
1119     case LoadOpType::I64Load:
1120     case LoadOpType::F64Load:
1121         return 8;
1122     }
1123     RELEASE_ASSERT_NOT_REACHED();
1124 }
1125
1126 inline TypedTmp AirIRGenerator::emitLoadOp(LoadOpType op, ExpressionType pointer, uint32_t uoffset)
1127 {
1128     uint32_t offset = fixupPointerPlusOffset(pointer, uoffset);
1129
1130     TypedTmp immTmp;
1131     TypedTmp newPtr;
1132     TypedTmp result;
1133
1134     Arg addrArg;
1135     if (Arg::isValidAddrForm(offset, B3::widthForBytes(sizeOfLoadOp(op))))
1136         addrArg = Arg::addr(pointer, offset);
1137     else {
1138         immTmp = g64();
1139         newPtr = g64();
1140         append(Move, Arg::bigImm(offset), immTmp);
1141         append(Add64, immTmp, pointer, newPtr);
1142         addrArg = Arg::addr(newPtr);
1143     }
1144
1145     switch (op) {
1146     case LoadOpType::I32Load8S: {
1147         result = g32();
1148         appendEffectful(Load8SignedExtendTo32, addrArg, result);
1149         break;
1150     }
1151
1152     case LoadOpType::I64Load8S: {
1153         result = g64();
1154         appendEffectful(Load8SignedExtendTo32, addrArg, result);
1155         append(SignExtend32ToPtr, result, result);
1156         break;
1157     }
1158
1159     case LoadOpType::I32Load8U: {
1160         result = g32();
1161         appendEffectful(Load8, addrArg, result);
1162         break;
1163     }
1164
1165     case LoadOpType::I64Load8U: {
1166         result = g64();
1167         appendEffectful(Load8, addrArg, result);
1168         break;
1169     }
1170
1171     case LoadOpType::I32Load16S: {
1172         result = g32();
1173         appendEffectful(Load16SignedExtendTo32, addrArg, result);
1174         break;
1175     }
1176
1177     case LoadOpType::I64Load16S: {
1178         result = g64();
1179         appendEffectful(Load16SignedExtendTo32, addrArg, result);
1180         append(SignExtend32ToPtr, result, result);
1181         break;
1182     }
1183
1184     case LoadOpType::I32Load16U: {
1185         result = g32();
1186         appendEffectful(Load16, addrArg, result);
1187         break;
1188     }
1189
1190     case LoadOpType::I64Load16U: {
1191         result = g64();
1192         appendEffectful(Load16, addrArg, result);
1193         break;
1194     }
1195
1196     case LoadOpType::I32Load:
1197         result = g32();
1198         appendEffectful(Move32, addrArg, result);
1199         break;
1200
1201     case LoadOpType::I64Load32U: {
1202         result = g64();
1203         appendEffectful(Move32, addrArg, result);
1204         break;
1205     }
1206
1207     case LoadOpType::I64Load32S: {
1208         result = g64();
1209         appendEffectful(Move32, addrArg, result);
1210         append(SignExtend32ToPtr, result, result);
1211         break;
1212     }
1213
1214     case LoadOpType::I64Load: {
1215         result = g64();
1216         appendEffectful(Move, addrArg, result);
1217         break;
1218     }
1219
1220     case LoadOpType::F32Load: {
1221         result = f32();
1222         appendEffectful(MoveFloat, addrArg, result);
1223         break;
1224     }
1225
1226     case LoadOpType::F64Load: {
1227         result = f64();
1228         appendEffectful(MoveDouble, addrArg, result);
1229         break;
1230     }
1231     }
1232
1233     return result;
1234 }
1235
1236 auto AirIRGenerator::load(LoadOpType op, ExpressionType pointer, ExpressionType& result, uint32_t offset) -> PartialResult
1237 {
1238     ASSERT(pointer.tmp().isGP());
1239
1240     if (UNLIKELY(sumOverflows<uint32_t>(offset, sizeOfLoadOp(op)))) {
1241         // FIXME: Even though this is provably out of bounds, it's not a validation error, so we have to handle it
1242         // as a runtime exception. However, this may change: https://bugs.webkit.org/show_bug.cgi?id=166435
1243         auto* patch = addPatchpoint(B3::Void);
1244         patch->setGenerator([this] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1245             this->emitThrowException(jit, ExceptionType::OutOfBoundsMemoryAccess);
1246         });
1247         emitPatchpoint(patch, Tmp());
1248
1249         // We won't reach here, so we just pick a random reg.
1250         switch (op) {
1251         case LoadOpType::I32Load8S:
1252         case LoadOpType::I32Load16S:
1253         case LoadOpType::I32Load:
1254         case LoadOpType::I32Load16U:
1255         case LoadOpType::I32Load8U:
1256             result = g32();
1257             break;
1258         case LoadOpType::I64Load8S:
1259         case LoadOpType::I64Load8U:
1260         case LoadOpType::I64Load16S:
1261         case LoadOpType::I64Load32U:
1262         case LoadOpType::I64Load32S:
1263         case LoadOpType::I64Load:
1264         case LoadOpType::I64Load16U:
1265             result = g64();
1266             break;
1267         case LoadOpType::F32Load:
1268             result = f32();
1269             break;
1270         case LoadOpType::F64Load:
1271             result = f64();
1272             break;
1273         }
1274     } else
1275         result = emitLoadOp(op, emitCheckAndPreparePointer(pointer, offset, sizeOfLoadOp(op)), offset);
1276
1277     return { };
1278 }
1279
1280 inline uint32_t sizeOfStoreOp(StoreOpType op)
1281 {
1282     switch (op) {
1283     case StoreOpType::I32Store8:
1284     case StoreOpType::I64Store8:
1285         return 1;
1286     case StoreOpType::I32Store16:
1287     case StoreOpType::I64Store16:
1288         return 2;
1289     case StoreOpType::I32Store:
1290     case StoreOpType::I64Store32:
1291     case StoreOpType::F32Store:
1292         return 4;
1293     case StoreOpType::I64Store:
1294     case StoreOpType::F64Store:
1295         return 8;
1296     }
1297     RELEASE_ASSERT_NOT_REACHED();
1298 }
1299
1300
1301 inline void AirIRGenerator::emitStoreOp(StoreOpType op, ExpressionType pointer, ExpressionType value, uint32_t uoffset)
1302 {
1303     uint32_t offset = fixupPointerPlusOffset(pointer, uoffset);
1304
1305     TypedTmp immTmp;
1306     TypedTmp newPtr;
1307
1308     Arg addrArg;
1309     if (Arg::isValidAddrForm(offset, B3::widthForBytes(sizeOfStoreOp(op))))
1310         addrArg = Arg::addr(pointer, offset);
1311     else {
1312         immTmp = g64();
1313         newPtr = g64();
1314         append(Move, Arg::bigImm(offset), immTmp);
1315         append(Add64, immTmp, pointer, newPtr);
1316         addrArg = Arg::addr(newPtr);
1317     }
1318
1319     switch (op) {
1320     case StoreOpType::I64Store8:
1321     case StoreOpType::I32Store8:
1322         append(Store8, value, addrArg);
1323         return;
1324
1325     case StoreOpType::I64Store16:
1326     case StoreOpType::I32Store16:
1327         append(Store16, value, addrArg);
1328         return;
1329
1330     case StoreOpType::I64Store32:
1331     case StoreOpType::I32Store:
1332         append(Move32, value, addrArg);
1333         return;
1334
1335     case StoreOpType::I64Store:
1336         append(Move, value, addrArg);
1337         return;
1338
1339     case StoreOpType::F32Store:
1340         append(MoveFloat, value, addrArg);
1341         return;
1342
1343     case StoreOpType::F64Store:
1344         append(MoveDouble, value, addrArg);
1345         return;
1346     }
1347
1348     RELEASE_ASSERT_NOT_REACHED();
1349 }
1350
1351 auto AirIRGenerator::store(StoreOpType op, ExpressionType pointer, ExpressionType value, uint32_t offset) -> PartialResult
1352 {
1353     ASSERT(pointer.tmp().isGP());
1354
1355     if (UNLIKELY(sumOverflows<uint32_t>(offset, sizeOfStoreOp(op)))) {
1356         // FIXME: Even though this is provably out of bounds, it's not a validation error, so we have to handle it
1357         // as a runtime exception. However, this may change: https://bugs.webkit.org/show_bug.cgi?id=166435
1358         auto* throwException = addPatchpoint(B3::Void);
1359         throwException->setGenerator([this] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1360             this->emitThrowException(jit, ExceptionType::OutOfBoundsMemoryAccess);
1361         });
1362         emitPatchpoint(throwException, Tmp());
1363     } else
1364         emitStoreOp(op, emitCheckAndPreparePointer(pointer, offset, sizeOfStoreOp(op)), value, offset);
1365
1366     return { };
1367 }
1368
1369 auto AirIRGenerator::addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result) -> PartialResult
1370 {
1371     ASSERT(nonZero.type() == zero.type());
1372     result = tmpForType(nonZero.type());
1373     append(moveOpForValueType(nonZero.type()), nonZero, result);
1374
1375     BasicBlock* isZero = m_code.addBlock();
1376     BasicBlock* continuation = m_code.addBlock();
1377
1378     append(BranchTest32, Arg::resCond(MacroAssembler::Zero), condition, condition);
1379     m_currentBlock->setSuccessors(isZero, continuation);
1380
1381     append(isZero, moveOpForValueType(zero.type()), zero, result);
1382     append(isZero, Jump);
1383     isZero->setSuccessors(continuation);
1384
1385     m_currentBlock = continuation;
1386
1387     return { };
1388 }
1389
1390 void AirIRGenerator::emitTierUpCheck(uint32_t decrementCount, B3::Origin origin)
1391 {
1392     UNUSED_PARAM(origin);
1393
1394     if (!m_tierUp)
1395         return;
1396
1397     auto countdownPtr = g64();
1398     auto oldCountdown = g64();
1399     auto newCountdown = g64();
1400
1401     append(Move, Arg::bigImm(reinterpret_cast<uint64_t>(m_tierUp)), countdownPtr);
1402     append(Move32, Arg::addr(countdownPtr), oldCountdown);
1403
1404     RELEASE_ASSERT(Arg::isValidImmForm(decrementCount));
1405     append(Move32, oldCountdown, newCountdown);
1406     append(Sub32, Arg::imm(decrementCount), newCountdown);
1407     append(Move32, newCountdown, Arg::addr(countdownPtr));
1408
1409     auto* patch = addPatchpoint(B3::Void);
1410     B3::Effects effects = B3::Effects::none();
1411     effects.reads = B3::HeapRange::top();
1412     effects.writes = B3::HeapRange::top();
1413     patch->effects = effects;
1414
1415     patch->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
1416         MacroAssembler::Jump tierUp = jit.branch32(MacroAssembler::Above, params[0].gpr(), params[1].gpr());
1417         MacroAssembler::Label tierUpResume = jit.label();
1418
1419         params.addLatePath([=] (CCallHelpers& jit) {
1420             tierUp.link(&jit);
1421
1422             const unsigned extraPaddingBytes = 0;
1423             RegisterSet registersToSpill = { };
1424             registersToSpill.add(GPRInfo::argumentGPR1);
1425             unsigned numberOfStackBytesUsedForRegisterPreservation = ScratchRegisterAllocator::preserveRegistersToStackForCall(jit, registersToSpill, extraPaddingBytes);
1426
1427             jit.move(MacroAssembler::TrustedImm32(m_functionIndex), GPRInfo::argumentGPR1);
1428             MacroAssembler::Call call = jit.nearCall();
1429
1430             ScratchRegisterAllocator::restoreRegistersFromStackForCall(jit, registersToSpill, RegisterSet(), numberOfStackBytesUsedForRegisterPreservation, extraPaddingBytes);
1431             jit.jump(tierUpResume);
1432
1433             jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
1434                 MacroAssembler::repatchNearCall(linkBuffer.locationOfNearCall<NoPtrTag>(call), CodeLocationLabel<JITThunkPtrTag>(Thunks::singleton().stub(triggerOMGTierUpThunkGenerator).code()));
1435
1436             });
1437         });
1438     });
1439
1440     emitPatchpoint(patch, Tmp(), newCountdown, oldCountdown);
1441 }
1442
1443 AirIRGenerator::ControlData AirIRGenerator::addLoop(Type signature)
1444 {
1445     BasicBlock* body = m_code.addBlock();
1446     BasicBlock* continuation = m_code.addBlock();
1447
1448     append(Jump);
1449     m_currentBlock->setSuccessors(body);
1450
1451     m_currentBlock = body;
1452     emitTierUpCheck(TierUpCount::loopDecrement(), origin());
1453
1454     return ControlData(origin(), signature, tmpForType(signature), BlockType::Loop, continuation, body);
1455 }
1456
1457 AirIRGenerator::ControlData AirIRGenerator::addTopLevel(Type signature)
1458 {
1459     return ControlData(B3::Origin(), signature, tmpForType(signature), BlockType::TopLevel, m_code.addBlock());
1460 }
1461
1462 AirIRGenerator::ControlData AirIRGenerator::addBlock(Type signature)
1463 {
1464     return ControlData(origin(), signature, tmpForType(signature), BlockType::Block, m_code.addBlock());
1465 }
1466
1467 auto AirIRGenerator::addIf(ExpressionType condition, Type signature, ControlType& result) -> PartialResult
1468 {
1469     BasicBlock* taken = m_code.addBlock();
1470     BasicBlock* notTaken = m_code.addBlock();
1471     BasicBlock* continuation = m_code.addBlock();
1472     
1473     // Wasm bools are i32.
1474     append(BranchTest32, Arg::resCond(MacroAssembler::NonZero), condition, condition);
1475     m_currentBlock->setSuccessors(taken, notTaken);
1476
1477     m_currentBlock = taken;
1478     result = ControlData(origin(), signature, tmpForType(signature), BlockType::If, continuation, notTaken);
1479     return { };
1480 }
1481
1482 auto AirIRGenerator::addElse(ControlData& data, const ExpressionList& currentStack) -> PartialResult
1483 {
1484     unifyValuesWithBlock(currentStack, data.result);
1485     append(Jump);
1486     m_currentBlock->setSuccessors(data.continuation);
1487     return addElseToUnreachable(data);
1488 }
1489
1490 auto AirIRGenerator::addElseToUnreachable(ControlData& data) -> PartialResult
1491 {
1492     ASSERT(data.type() == BlockType::If);
1493     m_currentBlock = data.special;
1494     data.convertIfToBlock();
1495     return { };
1496 }
1497
1498 auto AirIRGenerator::addReturn(const ControlData& data, const ExpressionList& returnValues) -> PartialResult
1499 {
1500     ASSERT(returnValues.size() <= 1);
1501     if (returnValues.size()) {
1502         Tmp returnValueGPR = Tmp(GPRInfo::returnValueGPR);
1503         Tmp returnValueFPR = Tmp(FPRInfo::returnValueFPR);
1504         switch (data.signature()) {
1505         case Type::I32:
1506             append(Move32, returnValues[0], returnValueGPR);
1507             append(Ret32, returnValueGPR);
1508             break;
1509         case Type::I64:
1510             append(Move, returnValues[0], returnValueGPR);
1511             append(Ret64, returnValueGPR);
1512             break;
1513         case Type::F32:
1514             append(MoveFloat, returnValues[0], returnValueFPR);
1515             append(RetFloat, returnValueFPR);
1516             break;
1517         case Type::F64:
1518             append(MoveDouble, returnValues[0], returnValueFPR);
1519             append(RetFloat, returnValueFPR);
1520             break;
1521         default:
1522             RELEASE_ASSERT_NOT_REACHED();
1523         }
1524     } else
1525         append(RetVoid);
1526     return { };
1527 }
1528
1529 // NOTE: All branches in Wasm are on 32-bit ints
1530
1531 auto AirIRGenerator::addBranch(ControlData& data, ExpressionType condition, const ExpressionList& returnValues) -> PartialResult
1532 {
1533     unifyValuesWithBlock(returnValues, data.resultForBranch());
1534
1535     BasicBlock* target = data.targetBlockForBranch();
1536     if (condition) {
1537         BasicBlock* continuation = m_code.addBlock();
1538         append(BranchTest32, Arg::resCond(MacroAssembler::NonZero), condition, condition);
1539         m_currentBlock->setSuccessors(target, continuation);
1540         m_currentBlock = continuation;
1541     } else {
1542         append(Jump);
1543         m_currentBlock->setSuccessors(target);
1544     }
1545
1546     return { };
1547 }
1548
1549 auto AirIRGenerator::addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack) -> PartialResult
1550 {
1551     auto& successors = m_currentBlock->successors();
1552     ASSERT(successors.isEmpty());
1553     for (const auto& target : targets) {
1554         unifyValuesWithBlock(expressionStack, target->resultForBranch());
1555         successors.append(target->targetBlockForBranch());
1556     }
1557     unifyValuesWithBlock(expressionStack, defaultTarget.resultForBranch());
1558     successors.append(defaultTarget.targetBlockForBranch());
1559
1560     ASSERT(condition.type() == Type::I32);
1561
1562     // FIXME: We should consider dynamically switching between a jump table
1563     // and a binary switch depending on the number of successors.
1564     // https://bugs.webkit.org/show_bug.cgi?id=194477
1565
1566     size_t numTargets = targets.size();
1567
1568     auto* patchpoint = addPatchpoint(B3::Void);
1569     patchpoint->effects = B3::Effects::none();
1570     patchpoint->effects.terminal = true;
1571     patchpoint->clobber(RegisterSet::macroScratchRegisters());
1572
1573     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
1574         AllowMacroScratchRegisterUsage allowScratch(jit);
1575
1576         Vector<int64_t> cases;
1577         cases.reserveInitialCapacity(numTargets);
1578         for (size_t i = 0; i < numTargets; ++i)
1579             cases.uncheckedAppend(i);
1580
1581         GPRReg valueReg = params[0].gpr();
1582         BinarySwitch binarySwitch(valueReg, cases, BinarySwitch::Int32);
1583
1584         Vector<CCallHelpers::Jump> caseJumps;
1585         caseJumps.resize(numTargets);
1586
1587         while (binarySwitch.advance(jit)) {
1588             unsigned value = binarySwitch.caseValue();
1589             unsigned index = binarySwitch.caseIndex();
1590             ASSERT_UNUSED(value, value == index);
1591             ASSERT(index < numTargets);
1592             caseJumps[index] = jit.jump();
1593         }
1594
1595         CCallHelpers::JumpList fallThrough = binarySwitch.fallThrough();
1596
1597         Vector<Box<CCallHelpers::Label>> successorLabels = params.successorLabels();
1598         ASSERT(successorLabels.size() == caseJumps.size() + 1);
1599
1600         params.addLatePath([=, caseJumps = WTFMove(caseJumps), successorLabels = WTFMove(successorLabels)] (CCallHelpers& jit) {
1601             for (size_t i = 0; i < numTargets; ++i)
1602                 caseJumps[i].linkTo(*successorLabels[i], &jit);                
1603             fallThrough.linkTo(*successorLabels[numTargets], &jit);
1604         });
1605     });
1606
1607     emitPatchpoint(patchpoint, TypedTmp(), condition);
1608
1609     return { };
1610 }
1611
1612 auto AirIRGenerator::endBlock(ControlEntry& entry, ExpressionList& expressionStack) -> PartialResult
1613 {
1614     ControlData& data = entry.controlData;
1615
1616     unifyValuesWithBlock(expressionStack, data.result);
1617     append(Jump);
1618     m_currentBlock->setSuccessors(data.continuation);
1619
1620     return addEndToUnreachable(entry);
1621 }
1622
1623
1624 auto AirIRGenerator::addEndToUnreachable(ControlEntry& entry) -> PartialResult
1625 {
1626     ControlData& data = entry.controlData;
1627     m_currentBlock = data.continuation;
1628
1629     if (data.type() == BlockType::If) {
1630         append(data.special, Jump);
1631         data.special->setSuccessors(m_currentBlock);
1632     }
1633
1634     for (const auto& result : data.result)
1635         entry.enclosedExpressionStack.append(result);
1636
1637     // TopLevel does not have any code after this so we need to make sure we emit a return here.
1638     if (data.type() == BlockType::TopLevel)
1639         return addReturn(data, entry.enclosedExpressionStack);
1640
1641     return { };
1642 }
1643
1644 auto AirIRGenerator::addCall(uint32_t functionIndex, const Signature& signature, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult
1645 {
1646     ASSERT(signature.argumentCount() == args.size());
1647
1648     m_makesCalls = true;
1649
1650     Type returnType = signature.returnType();
1651     if (returnType != Type::Void)
1652         result = tmpForType(returnType);
1653
1654     Vector<UnlinkedWasmToWasmCall>* unlinkedWasmToWasmCalls = &m_unlinkedWasmToWasmCalls;
1655
1656     if (m_info.isImportedFunctionFromFunctionIndexSpace(functionIndex)) {
1657         m_maxNumJSCallArguments = std::max(m_maxNumJSCallArguments, static_cast<uint32_t>(args.size()));
1658
1659         auto currentInstance = g64();
1660         append(Move, instanceValue(), currentInstance);
1661
1662         auto targetInstance = g64();
1663
1664         // FIXME: We should have better isel here.
1665         // https://bugs.webkit.org/show_bug.cgi?id=193999
1666         append(Move, Arg::bigImm(Instance::offsetOfTargetInstance(functionIndex)), targetInstance);
1667         append(Add64, instanceValue(), targetInstance);
1668         append(Move, Arg::addr(targetInstance), targetInstance);
1669
1670         BasicBlock* isWasmBlock = m_code.addBlock();
1671         BasicBlock* isEmbedderBlock = m_code.addBlock();
1672         BasicBlock* continuation = m_code.addBlock();
1673
1674         append(BranchTest64, Arg::resCond(MacroAssembler::NonZero), targetInstance, targetInstance);
1675         m_currentBlock->setSuccessors(isWasmBlock, isEmbedderBlock);
1676
1677         {
1678             auto* patchpoint = addPatchpoint(toB3Type(returnType));
1679             patchpoint->effects.writesPinned = true;
1680             patchpoint->effects.readsPinned = true;
1681             // We need to clobber all potential pinned registers since we might be leaving the instance.
1682             // We pessimistically assume we could be calling to something that is bounds checking.
1683             // FIXME: We shouldn't have to do this: https://bugs.webkit.org/show_bug.cgi?id=172181
1684             patchpoint->clobberLate(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
1685
1686             Vector<ConstrainedTmp> patchArgs;
1687             wasmCallingConventionAir().setupCall(m_code, returnType, patchpoint, toTmpVector(args), [&] (Tmp tmp, B3::ValueRep rep) {
1688                 patchArgs.append({ tmp, rep });
1689             });
1690
1691             patchpoint->setGenerator([unlinkedWasmToWasmCalls, functionIndex] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1692                 AllowMacroScratchRegisterUsage allowScratch(jit);
1693                 CCallHelpers::Call call = jit.threadSafePatchableNearCall();
1694                 jit.addLinkTask([unlinkedWasmToWasmCalls, call, functionIndex] (LinkBuffer& linkBuffer) {
1695                     unlinkedWasmToWasmCalls->append({ linkBuffer.locationOfNearCall<WasmEntryPtrTag>(call), functionIndex });
1696                 });
1697             });
1698
1699             emitPatchpoint(isWasmBlock, patchpoint, result, WTFMove(patchArgs));
1700             append(isWasmBlock, Jump);
1701             isWasmBlock->setSuccessors(continuation);
1702         }
1703
1704         {
1705             auto jumpDestination = g64();
1706             append(isEmbedderBlock, Move, Arg::bigImm(Instance::offsetOfWasmToEmbedderStub(functionIndex)), jumpDestination);
1707             append(isEmbedderBlock, Add64, instanceValue(), jumpDestination);
1708             append(isEmbedderBlock, Move, Arg::addr(jumpDestination), jumpDestination);
1709
1710             auto* patchpoint = addPatchpoint(toB3Type(returnType));
1711             patchpoint->effects.writesPinned = true;
1712             patchpoint->effects.readsPinned = true;
1713             // We need to clobber all potential pinned registers since we might be leaving the instance.
1714             // We pessimistically assume we could be calling to something that is bounds checking.
1715             // FIXME: We shouldn't have to do this: https://bugs.webkit.org/show_bug.cgi?id=172181
1716             patchpoint->clobberLate(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
1717
1718             Vector<ConstrainedTmp> patchArgs;
1719             patchArgs.append(jumpDestination);
1720
1721             wasmCallingConventionAir().setupCall(m_code, returnType, patchpoint, toTmpVector(args), [&] (Tmp tmp, B3::ValueRep rep) {
1722                 patchArgs.append({ tmp, rep });
1723             });
1724
1725             patchpoint->setGenerator([returnType] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
1726                 AllowMacroScratchRegisterUsage allowScratch(jit);
1727                 jit.call(params[returnType == Void ? 0 : 1].gpr(), WasmEntryPtrTag);
1728             });
1729
1730             emitPatchpoint(isEmbedderBlock, patchpoint, result, WTFMove(patchArgs));
1731             append(isEmbedderBlock, Jump);
1732             isEmbedderBlock->setSuccessors(continuation);
1733         }
1734
1735         m_currentBlock = continuation;
1736         // The call could have been to another WebAssembly instance, and / or could have modified our Memory.
1737         restoreWebAssemblyGlobalState(RestoreCachedStackLimit::Yes, m_info.memory, currentInstance, continuation);
1738     } else {
1739         auto* patchpoint = addPatchpoint(toB3Type(returnType));
1740         patchpoint->effects.writesPinned = true;
1741         patchpoint->effects.readsPinned = true;
1742
1743         Vector<ConstrainedTmp> patchArgs;
1744         wasmCallingConventionAir().setupCall(m_code, returnType, patchpoint, toTmpVector(args), [&] (Tmp tmp, B3::ValueRep rep) {
1745             patchArgs.append({ tmp, rep });
1746         });
1747
1748         patchpoint->setGenerator([unlinkedWasmToWasmCalls, functionIndex] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1749             AllowMacroScratchRegisterUsage allowScratch(jit);
1750             CCallHelpers::Call call = jit.threadSafePatchableNearCall();
1751             jit.addLinkTask([unlinkedWasmToWasmCalls, call, functionIndex] (LinkBuffer& linkBuffer) {
1752                 unlinkedWasmToWasmCalls->append({ linkBuffer.locationOfNearCall<WasmEntryPtrTag>(call), functionIndex });
1753             });
1754         });
1755
1756         emitPatchpoint(m_currentBlock, patchpoint, result, WTFMove(patchArgs));
1757     }
1758
1759     return { };
1760 }
1761
1762 auto AirIRGenerator::addCallIndirect(const Signature& signature, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult
1763 {
1764     ExpressionType calleeIndex = args.takeLast();
1765     ASSERT(signature.argumentCount() == args.size());
1766
1767     m_makesCalls = true;
1768     // Note: call indirect can call either WebAssemblyFunction or WebAssemblyWrapperFunction. Because
1769     // WebAssemblyWrapperFunction is like calling into the embedder, we conservatively assume all call indirects
1770     // can be to the embedder for our stack check calculation.
1771     m_maxNumJSCallArguments = std::max(m_maxNumJSCallArguments, static_cast<uint32_t>(args.size()));
1772
1773     auto currentInstance = g64();
1774     append(Move, instanceValue(), currentInstance);
1775
1776     ExpressionType callableFunctionBuffer = g64();
1777     ExpressionType instancesBuffer = g64();
1778     ExpressionType callableFunctionBufferLength = g64();
1779     {
1780         RELEASE_ASSERT(Arg::isValidAddrForm(Instance::offsetOfTable(), B3::Width64));
1781         RELEASE_ASSERT(Arg::isValidAddrForm(Table::offsetOfFunctions(), B3::Width64));
1782         RELEASE_ASSERT(Arg::isValidAddrForm(Table::offsetOfInstances(), B3::Width64));
1783         RELEASE_ASSERT(Arg::isValidAddrForm(Table::offsetOfLength(), B3::Width64));
1784
1785         append(Move, Arg::addr(instanceValue(), Instance::offsetOfTable()), callableFunctionBufferLength);
1786         append(Move, Arg::addr(callableFunctionBufferLength, Table::offsetOfFunctions()), callableFunctionBuffer);
1787         append(Move, Arg::addr(callableFunctionBufferLength, Table::offsetOfInstances()), instancesBuffer);
1788         append(Move32, Arg::addr(callableFunctionBufferLength, Table::offsetOfLength()), callableFunctionBufferLength);
1789     }
1790
1791     append(Move32, calleeIndex, calleeIndex);
1792
1793     // Check the index we are looking for is valid.
1794     emitCheck([&] {
1795         return Inst(Branch32, nullptr, Arg::relCond(MacroAssembler::AboveOrEqual), calleeIndex, callableFunctionBufferLength);
1796     }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1797         this->emitThrowException(jit, ExceptionType::OutOfBoundsCallIndirect);
1798     });
1799
1800     ExpressionType calleeCode = g64();
1801     {
1802         ExpressionType calleeSignatureIndex = g64();
1803         // Compute the offset in the table index space we are looking for.
1804         append(Move, Arg::imm(sizeof(WasmToWasmImportableFunction)), calleeSignatureIndex);
1805         append(Mul64, calleeIndex, calleeSignatureIndex);
1806         append(Add64, callableFunctionBuffer, calleeSignatureIndex);
1807         
1808         append(Move, Arg::addr(calleeSignatureIndex, WasmToWasmImportableFunction::offsetOfEntrypointLoadLocation()), calleeCode); // Pointer to callee code.
1809
1810         // Check that the WasmToWasmImportableFunction is initialized. We trap if it isn't. An "invalid" SignatureIndex indicates it's not initialized.
1811         // FIXME: when we have trap handlers, we can just let the call fail because Signature::invalidIndex is 0. https://bugs.webkit.org/show_bug.cgi?id=177210
1812         static_assert(sizeof(WasmToWasmImportableFunction::signatureIndex) == sizeof(uint64_t), "Load codegen assumes i64");
1813
1814         // FIXME: This seems dumb to do two checks just for a nicer error message.
1815         // We should move just to use a single branch and then figure out what
1816         // error to use in the exception handler.
1817
1818         append(Move, Arg::addr(calleeSignatureIndex, WasmToWasmImportableFunction::offsetOfSignatureIndex()), calleeSignatureIndex);
1819
1820         emitCheck([&] {
1821             static_assert(Signature::invalidIndex == 0, "");
1822             return Inst(BranchTest64, nullptr, Arg::resCond(MacroAssembler::Zero), calleeSignatureIndex, calleeSignatureIndex);
1823         }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1824             this->emitThrowException(jit, ExceptionType::NullTableEntry);
1825         });
1826
1827         ExpressionType expectedSignatureIndex = g64();
1828         append(Move, Arg::bigImm(SignatureInformation::get(signature)), expectedSignatureIndex);
1829         emitCheck([&] {
1830             return Inst(Branch64, nullptr, Arg::relCond(MacroAssembler::NotEqual), calleeSignatureIndex, expectedSignatureIndex);
1831         }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1832             this->emitThrowException(jit, ExceptionType::BadSignature);
1833         });
1834     }
1835
1836     // Do a context switch if needed.
1837     {
1838         auto newContextInstance = g64();
1839         append(Move, Arg::index(instancesBuffer, calleeIndex, 8, 0), newContextInstance);
1840
1841         BasicBlock* doContextSwitch = m_code.addBlock();
1842         BasicBlock* continuation = m_code.addBlock();
1843
1844         append(Branch64, Arg::relCond(MacroAssembler::Equal), newContextInstance, instanceValue());
1845         m_currentBlock->setSuccessors(continuation, doContextSwitch);
1846
1847         auto* patchpoint = addPatchpoint(B3::Void);
1848         patchpoint->effects.writesPinned = true;
1849         // We pessimistically assume we're calling something with BoundsChecking memory.
1850         // FIXME: We shouldn't have to do this: https://bugs.webkit.org/show_bug.cgi?id=172181
1851         patchpoint->clobber(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
1852         patchpoint->clobber(RegisterSet::macroScratchRegisters());
1853         patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
1854             AllowMacroScratchRegisterUsage allowScratch(jit);
1855             GPRReg newContextInstance = params[0].gpr();
1856             GPRReg oldContextInstance = params[1].gpr();
1857             const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get();
1858             const auto& sizeRegs = pinnedRegs.sizeRegisters;
1859             GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
1860             ASSERT(newContextInstance != baseMemory);
1861             jit.loadPtr(CCallHelpers::Address(oldContextInstance, Instance::offsetOfCachedStackLimit()), baseMemory);
1862             jit.storePtr(baseMemory, CCallHelpers::Address(newContextInstance, Instance::offsetOfCachedStackLimit()));
1863             jit.storeWasmContextInstance(newContextInstance);
1864             ASSERT(sizeRegs[0].sizeRegister != baseMemory);
1865             // FIXME: We should support more than one memory size register
1866             //   see: https://bugs.webkit.org/show_bug.cgi?id=162952
1867             ASSERT(sizeRegs.size() == 1);
1868             ASSERT(sizeRegs[0].sizeRegister != newContextInstance);
1869             ASSERT(!sizeRegs[0].sizeOffset);
1870             jit.loadPtr(CCallHelpers::Address(newContextInstance, Instance::offsetOfCachedMemorySize()), sizeRegs[0].sizeRegister); // Memory size.
1871             jit.loadPtr(CCallHelpers::Address(newContextInstance, Instance::offsetOfCachedMemory()), baseMemory); // Memory::void*.
1872         });
1873
1874         emitPatchpoint(doContextSwitch, patchpoint, Tmp(), newContextInstance, instanceValue());
1875         append(doContextSwitch, Jump);
1876         doContextSwitch->setSuccessors(continuation);
1877
1878         m_currentBlock = continuation;
1879     }
1880
1881     append(Move, Arg::addr(calleeCode), calleeCode);
1882
1883     Type returnType = signature.returnType();
1884     if (returnType != Type::Void)
1885         result = tmpForType(returnType);
1886
1887     auto* patch = addPatchpoint(toB3Type(returnType));
1888     patch->effects.writesPinned = true;
1889     patch->effects.readsPinned = true;
1890     // We need to clobber all potential pinned registers since we might be leaving the instance.
1891     // We pessimistically assume we're always calling something that is bounds checking so
1892     // because the wasm->wasm thunk unconditionally overrides the size registers.
1893     // FIXME: We should not have to do this, but the wasm->wasm stub assumes it can
1894     // use all the pinned registers as scratch: https://bugs.webkit.org/show_bug.cgi?id=172181
1895     patch->clobberLate(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
1896
1897     Vector<ConstrainedTmp> emitArgs;
1898     emitArgs.append(calleeCode);
1899     wasmCallingConventionAir().setupCall(m_code, returnType, patch, toTmpVector(args), [&] (Tmp tmp, B3::ValueRep rep) {
1900         emitArgs.append({ tmp, rep });
1901     });
1902     patch->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
1903         AllowMacroScratchRegisterUsage allowScratch(jit);
1904         jit.call(params[returnType == Void ? 0 : 1].gpr(), WasmEntryPtrTag);
1905     });
1906
1907     emitPatchpoint(m_currentBlock, patch, result, WTFMove(emitArgs));
1908
1909     // The call could have been to another WebAssembly instance, and / or could have modified our Memory.
1910     restoreWebAssemblyGlobalState(RestoreCachedStackLimit::Yes, m_info.memory, currentInstance, m_currentBlock);
1911
1912     return { };
1913 }
1914
1915 void AirIRGenerator::unify(const ExpressionType& dst, const ExpressionType& source)
1916 {
1917     ASSERT(dst.type() == source.type());
1918     append(moveOpForValueType(dst.type()), source, dst);
1919 }
1920
1921 void AirIRGenerator::unifyValuesWithBlock(const ExpressionList& resultStack, const ResultList& result)
1922 {
1923     ASSERT(result.size() <= resultStack.size());
1924
1925     for (size_t i = 0; i < result.size(); ++i)
1926         unify(result[result.size() - 1 - i], resultStack[resultStack.size() - 1 - i]);
1927 }
1928
1929 void AirIRGenerator::dump(const Vector<ControlEntry>&, const ExpressionList*)
1930 {
1931 }
1932
1933 auto AirIRGenerator::origin() -> B3::Origin
1934 {
1935     // FIXME: We should implement a way to give Inst's an origin.
1936     return B3::Origin();
1937 }
1938
1939 Expected<std::unique_ptr<InternalFunction>, String> parseAndCompileAir(CompilationContext& compilationContext, const uint8_t* functionStart, size_t functionLength, const Signature& signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ModuleInformation& info, MemoryMode mode, uint32_t functionIndex, TierUpCount* tierUp, ThrowWasmException throwWasmException)
1940 {
1941     auto result = std::make_unique<InternalFunction>();
1942
1943     compilationContext.embedderEntrypointJIT = std::make_unique<CCallHelpers>();
1944     compilationContext.wasmEntrypointJIT = std::make_unique<CCallHelpers>();
1945
1946     B3::Procedure procedure;
1947     Code& code = procedure.code();
1948
1949     procedure.setOriginPrinter([] (PrintStream& out, B3::Origin origin) {
1950         if (origin.data())
1951             out.print("Wasm: ", bitwise_cast<OpcodeOrigin>(origin));
1952     });
1953     
1954     // This means we cannot use either StackmapGenerationParams::usedRegisters() or
1955     // StackmapGenerationParams::unavailableRegisters(). In exchange for this concession, we
1956     // don't strictly need to run Air::reportUsedRegisters(), which saves a bit of CPU time at
1957     // optLevel=1.
1958     procedure.setNeedsUsedRegisters(false);
1959     
1960     procedure.setOptLevel(Options::webAssemblyBBQOptimizationLevel());
1961
1962     AirIRGenerator irGenerator(info, procedure, result.get(), unlinkedWasmToWasmCalls, mode, functionIndex, tierUp, throwWasmException, signature);
1963     FunctionParser<AirIRGenerator> parser(irGenerator, functionStart, functionLength, signature, info);
1964     WASM_FAIL_IF_HELPER_FAILS(parser.parse());
1965
1966
1967     for (BasicBlock* block : code) {
1968         for (size_t i = 0; i < block->numSuccessors(); ++i)
1969             block->successorBlock(i)->addPredecessor(block);
1970     }
1971
1972     {
1973         B3::Air::prepareForGeneration(code);
1974         B3::Air::generate(code, *compilationContext.wasmEntrypointJIT);
1975         compilationContext.wasmEntrypointByproducts = procedure.releaseByproducts();
1976         result->entrypoint.calleeSaveRegisters = code.calleeSaveRegisterAtOffsetList();
1977     }
1978
1979     return result;
1980 }
1981
1982 template <typename IntType>
1983 void AirIRGenerator::emitChecksForModOrDiv(bool isSignedDiv, ExpressionType left, ExpressionType right)
1984 {
1985     static_assert(sizeof(IntType) == 4 || sizeof(IntType) == 8, "");
1986
1987     emitCheck([&] {
1988         return Inst(sizeof(IntType) == 4 ? BranchTest32 : BranchTest64, nullptr, Arg::resCond(MacroAssembler::Zero), right, right);
1989     }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1990         this->emitThrowException(jit, ExceptionType::DivisionByZero);
1991     });
1992
1993     if (isSignedDiv) {
1994         ASSERT(std::is_signed<IntType>::value);
1995         IntType min = std::numeric_limits<IntType>::min();
1996
1997         // FIXME: Better isel for compare with imms here.
1998         // https://bugs.webkit.org/show_bug.cgi?id=193999
1999         auto minTmp = sizeof(IntType) == 4 ? g32() : g64();
2000         auto negOne = sizeof(IntType) == 4 ? g32() : g64();
2001
2002         B3::Air::Opcode op = sizeof(IntType) == 4 ? Compare32 : Compare64;
2003         append(Move, Arg::bigImm(static_cast<uint64_t>(min)), minTmp);
2004         append(op, Arg::relCond(MacroAssembler::Equal), left, minTmp, minTmp);
2005
2006         append(Move, Arg::imm(-1), negOne);
2007         append(op, Arg::relCond(MacroAssembler::Equal), right, negOne, negOne);
2008
2009         emitCheck([&] {
2010             return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::NonZero), minTmp, negOne);
2011         },
2012         [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
2013             this->emitThrowException(jit, ExceptionType::IntegerOverflow);
2014         });
2015     }
2016 }
2017
2018 template <typename IntType>
2019 void AirIRGenerator::emitModOrDiv(bool isDiv, ExpressionType lhs, ExpressionType rhs, ExpressionType& result)
2020 {
2021     static_assert(sizeof(IntType) == 4 || sizeof(IntType) == 8, "");
2022
2023     result = sizeof(IntType) == 4 ? g32() : g64();
2024
2025     bool isSigned = std::is_signed<IntType>::value;
2026
2027     if (isARM64()) {
2028         B3::Air::Opcode div;
2029         switch (sizeof(IntType)) {
2030         case 4:
2031             div = isSigned ? Div32 : UDiv32;
2032             break;
2033         case 8:
2034             div = isSigned ? Div64 : UDiv64;
2035             break;
2036         }
2037
2038         append(div, lhs, rhs, result);
2039
2040         if (!isDiv) {
2041             append(sizeof(IntType) == 4 ? Mul32 : Mul64, result, rhs, result);
2042             append(sizeof(IntType) == 4 ? Sub32 : Sub64, lhs, result, result);
2043         }
2044
2045         return;
2046     }
2047
2048 #if CPU(X86) || CPU(X86_64)
2049     Tmp eax(X86Registers::eax);
2050     Tmp edx(X86Registers::edx);
2051
2052     if (isSigned) {
2053         B3::Air::Opcode convertToDoubleWord;
2054         B3::Air::Opcode div;
2055         switch (sizeof(IntType)) {
2056         case 4:
2057             convertToDoubleWord = X86ConvertToDoubleWord32;
2058             div = X86Div32;
2059             break;
2060         case 8:
2061             convertToDoubleWord = X86ConvertToQuadWord64;
2062             div = X86Div64;
2063             break;
2064         default:
2065             RELEASE_ASSERT_NOT_REACHED();
2066         }
2067
2068         // We implement "res = Div<Chill>/Mod<Chill>(num, den)" as follows:
2069         //
2070         //     if (den + 1 <=_unsigned 1) {
2071         //         if (!den) {
2072         //             res = 0;
2073         //             goto done;
2074         //         }
2075         //         if (num == -2147483648) {
2076         //             res = isDiv ? num : 0;
2077         //             goto done;
2078         //         }
2079         //     }
2080         //     res = num (/ or %) dev;
2081         // done:
2082
2083         BasicBlock* denIsGood = m_code.addBlock();
2084         BasicBlock* denMayBeBad = m_code.addBlock();
2085         BasicBlock* denNotZero = m_code.addBlock();
2086         BasicBlock* continuation = m_code.addBlock();
2087
2088         auto temp = sizeof(IntType) == 4 ? g32() : g64();
2089         auto one = addConstant(sizeof(IntType) == 4 ? Type::I32 : Type::I64, 1);
2090
2091         append(sizeof(IntType) == 4 ? Add32 : Add64, rhs, one, temp);
2092         append(sizeof(IntType) == 4 ? Branch32 : Branch64, Arg::relCond(MacroAssembler::Above), temp, one);
2093         m_currentBlock->setSuccessors(denIsGood, denMayBeBad);
2094
2095         append(denMayBeBad, Xor64, result, result);
2096         append(denMayBeBad, sizeof(IntType) == 4 ? BranchTest32 : BranchTest64, Arg::resCond(MacroAssembler::Zero), rhs, rhs);
2097         denMayBeBad->setSuccessors(continuation, denNotZero);
2098
2099         auto min = addConstant(denNotZero, sizeof(IntType) == 4 ? Type::I32 : Type::I64, std::numeric_limits<IntType>::min());
2100         if (isDiv)
2101             append(denNotZero, sizeof(IntType) == 4 ? Move32 : Move, min, result);
2102         else {
2103             // Result is zero, as set above...
2104         }
2105         append(denNotZero, sizeof(IntType) == 4 ? Branch32 : Branch64, Arg::relCond(MacroAssembler::Equal), lhs, min);
2106         denNotZero->setSuccessors(continuation, denIsGood);
2107
2108         auto divResult = isDiv ? eax : edx;
2109         append(denIsGood, Move, lhs, eax);
2110         append(denIsGood, convertToDoubleWord, eax, edx);
2111         append(denIsGood, div, eax, edx, rhs);
2112         append(denIsGood, sizeof(IntType) == 4 ? Move32 : Move, divResult, result);
2113         append(denIsGood, Jump);
2114         denIsGood->setSuccessors(continuation);
2115
2116         m_currentBlock = continuation;
2117         return;
2118     }
2119
2120     B3::Air::Opcode div = sizeof(IntType) == 4 ? X86UDiv32 : X86UDiv64;
2121
2122     Tmp divResult = isDiv ? eax : edx;
2123
2124     append(Move, lhs, eax);
2125     append(Xor64, edx, edx);
2126     append(div, eax, edx, rhs);
2127     append(sizeof(IntType) == 4 ? Move32 : Move, divResult, result);
2128 #else
2129     RELEASE_ASSERT_NOT_REACHED();
2130 #endif
2131 }
2132
2133 template<>
2134 auto AirIRGenerator::addOp<OpType::I32DivS>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult
2135 {
2136     emitChecksForModOrDiv<int32_t>(true, left, right);
2137     emitModOrDiv<int32_t>(true, left, right, result);
2138     return { };
2139 }
2140
2141 template<>
2142 auto AirIRGenerator::addOp<OpType::I32RemS>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult
2143 {
2144     emitChecksForModOrDiv<int32_t>(false, left, right);
2145     emitModOrDiv<int32_t>(false, left, right, result);
2146     return { };
2147 }
2148
2149 template<>
2150 auto AirIRGenerator::addOp<OpType::I32DivU>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult
2151 {
2152     emitChecksForModOrDiv<uint32_t>(false, left, right);
2153     emitModOrDiv<uint32_t>(true, left, right, result);
2154     return { };
2155 }
2156
2157 template<>
2158 auto AirIRGenerator::addOp<OpType::I32RemU>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult
2159 {
2160     emitChecksForModOrDiv<uint32_t>(false, left, right);
2161     emitModOrDiv<uint32_t>(false, left, right, result);
2162     return { };
2163 }
2164
2165 template<>
2166 auto AirIRGenerator::addOp<OpType::I64DivS>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult
2167 {
2168     emitChecksForModOrDiv<int64_t>(true, left, right);
2169     emitModOrDiv<int64_t>(true, left, right, result);
2170     return { };
2171 }
2172
2173 template<>
2174 auto AirIRGenerator::addOp<OpType::I64RemS>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult
2175 {
2176     emitChecksForModOrDiv<int64_t>(false, left, right);
2177     emitModOrDiv<int64_t>(false, left, right, result);
2178     return { };
2179 }
2180
2181 template<>
2182 auto AirIRGenerator::addOp<OpType::I64DivU>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult
2183 {
2184     emitChecksForModOrDiv<uint64_t>(false, left, right);
2185     emitModOrDiv<uint64_t>(true, left, right, result);
2186     return { };
2187 }
2188
2189 template<>
2190 auto AirIRGenerator::addOp<OpType::I64RemU>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult
2191 {
2192     emitChecksForModOrDiv<uint64_t>(false, left, right);
2193     emitModOrDiv<uint64_t>(false, left, right, result);
2194     return { };
2195 }
2196
2197 template<>
2198 auto AirIRGenerator::addOp<OpType::I32Ctz>(ExpressionType arg, ExpressionType& result) -> PartialResult
2199 {
2200     auto* patchpoint = addPatchpoint(B3::Int32);
2201     patchpoint->effects = B3::Effects::none();
2202     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2203         jit.countTrailingZeros32(params[1].gpr(), params[0].gpr());
2204     });
2205     result = g32();
2206     emitPatchpoint(patchpoint, result, arg);
2207     return { };
2208 }
2209
2210 template<>
2211 auto AirIRGenerator::addOp<OpType::I64Ctz>(ExpressionType arg, ExpressionType& result) -> PartialResult
2212 {
2213     auto* patchpoint = addPatchpoint(B3::Int64);
2214     patchpoint->effects = B3::Effects::none();
2215     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2216         jit.countTrailingZeros64(params[1].gpr(), params[0].gpr());
2217     });
2218     result = g64();
2219     emitPatchpoint(patchpoint, result, arg);
2220     return { };
2221 }
2222
2223 template<>
2224 auto AirIRGenerator::addOp<OpType::I32Popcnt>(ExpressionType arg, ExpressionType& result) -> PartialResult
2225 {
2226     result = g32();
2227
2228 #if CPU(X86_64)
2229     if (MacroAssembler::supportsCountPopulation()) {
2230         auto* patchpoint = addPatchpoint(B3::Int32);
2231         patchpoint->effects = B3::Effects::none();
2232         patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2233             jit.countPopulation32(params[1].gpr(), params[0].gpr());
2234         });
2235         emitPatchpoint(patchpoint, result, arg);
2236         return { };
2237     }
2238 #endif
2239
2240     uint32_t (*popcount)(int32_t) = [] (int32_t value) -> uint32_t { return __builtin_popcount(value); };
2241     emitCCall(popcount, result, arg);
2242     return { };
2243 }
2244
2245 template<>
2246 auto AirIRGenerator::addOp<OpType::I64Popcnt>(ExpressionType arg, ExpressionType& result) -> PartialResult
2247 {
2248     result = g64();
2249
2250 #if CPU(X86_64)
2251     if (MacroAssembler::supportsCountPopulation()) {
2252         auto* patchpoint = addPatchpoint(B3::Int64);
2253         patchpoint->effects = B3::Effects::none();
2254         patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2255             jit.countPopulation64(params[1].gpr(), params[0].gpr());
2256         });
2257         emitPatchpoint(patchpoint, result, arg);
2258         return { };
2259     }
2260 #endif
2261
2262     uint64_t (*popcount)(int64_t) = [] (int64_t value) -> uint64_t { return __builtin_popcountll(value); };
2263     emitCCall(popcount, result, arg);
2264     return { };
2265 }
2266
2267 template<>
2268 auto AirIRGenerator::addOp<F64ConvertUI64>(ExpressionType arg, ExpressionType& result) -> PartialResult
2269 {
2270     auto* patchpoint = addPatchpoint(B3::Double);
2271     patchpoint->effects = B3::Effects::none();
2272     if (isX86())
2273         patchpoint->numGPScratchRegisters = 1;
2274     patchpoint->clobber(RegisterSet::macroScratchRegisters());
2275     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2276         AllowMacroScratchRegisterUsage allowScratch(jit);
2277 #if CPU(X86_64)
2278         jit.convertUInt64ToDouble(params[1].gpr(), params[0].fpr(), params.gpScratch(0));
2279 #else
2280         jit.convertUInt64ToDouble(params[1].gpr(), params[0].fpr());
2281 #endif
2282     });
2283     result = f64();
2284     emitPatchpoint(patchpoint, result, arg);
2285     return { };
2286 }
2287
2288 template<>
2289 auto AirIRGenerator::addOp<OpType::F32ConvertUI64>(ExpressionType arg, ExpressionType& result) -> PartialResult
2290 {
2291     auto* patchpoint = addPatchpoint(B3::Float);
2292     patchpoint->effects = B3::Effects::none();
2293     if (isX86())
2294         patchpoint->numGPScratchRegisters = 1;
2295     patchpoint->clobber(RegisterSet::macroScratchRegisters());
2296     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2297         AllowMacroScratchRegisterUsage allowScratch(jit);
2298 #if CPU(X86_64)
2299         jit.convertUInt64ToFloat(params[1].gpr(), params[0].fpr(), params.gpScratch(0));
2300 #else
2301         jit.convertUInt64ToFloat(params[1].gpr(), params[0].fpr());
2302 #endif
2303     });
2304     result = f32();
2305     emitPatchpoint(patchpoint, result, arg);
2306     return { };
2307 }
2308
2309 template<>
2310 auto AirIRGenerator::addOp<OpType::F64Nearest>(ExpressionType arg, ExpressionType& result) -> PartialResult
2311 {
2312     auto* patchpoint = addPatchpoint(B3::Double);
2313     patchpoint->effects = B3::Effects::none();
2314     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2315         jit.roundTowardNearestIntDouble(params[1].fpr(), params[0].fpr());
2316     });
2317     result = f64();
2318     emitPatchpoint(patchpoint, result, arg);
2319     return { };
2320 }
2321
2322 template<>
2323 auto AirIRGenerator::addOp<OpType::F32Nearest>(ExpressionType arg, ExpressionType& result) -> PartialResult
2324 {
2325     auto* patchpoint = addPatchpoint(B3::Float);
2326     patchpoint->effects = B3::Effects::none();
2327     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2328         jit.roundTowardNearestIntFloat(params[1].fpr(), params[0].fpr());
2329     });
2330     result = f32();
2331     emitPatchpoint(patchpoint, result, arg);
2332     return { };
2333 }
2334
2335 template<>
2336 auto AirIRGenerator::addOp<OpType::F64Trunc>(ExpressionType arg, ExpressionType& result) -> PartialResult
2337 {
2338     auto* patchpoint = addPatchpoint(B3::Double);
2339     patchpoint->effects = B3::Effects::none();
2340     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2341         jit.roundTowardZeroDouble(params[1].fpr(), params[0].fpr());
2342     });
2343     result = f64();
2344     emitPatchpoint(patchpoint, result, arg);
2345     return { };
2346 }
2347
2348 template<>
2349 auto AirIRGenerator::addOp<OpType::F32Trunc>(ExpressionType arg, ExpressionType& result) -> PartialResult
2350 {
2351     auto* patchpoint = addPatchpoint(B3::Float);
2352     patchpoint->effects = B3::Effects::none();
2353     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2354         jit.roundTowardZeroFloat(params[1].fpr(), params[0].fpr());
2355     });
2356     result = f32();
2357     emitPatchpoint(patchpoint, result, arg);
2358     return { };
2359 }
2360
2361 template<>
2362 auto AirIRGenerator::addOp<OpType::I32TruncSF64>(ExpressionType arg, ExpressionType& result) -> PartialResult
2363 {
2364     auto max = addConstant(Type::F64, bitwise_cast<uint64_t>(-static_cast<double>(std::numeric_limits<int32_t>::min())));
2365     auto min = addConstant(Type::F64, bitwise_cast<uint64_t>(static_cast<double>(std::numeric_limits<int32_t>::min())));
2366
2367     auto temp1 = g32();
2368     auto temp2 = g32();
2369     append(CompareDouble, Arg::doubleCond(MacroAssembler::DoubleLessThanOrUnordered), arg, min, temp1);
2370     append(CompareDouble, Arg::doubleCond(MacroAssembler::DoubleGreaterThanOrEqualOrUnordered), arg, max, temp2);
2371     append(Or32, temp1, temp2);
2372
2373     emitCheck([&] {
2374         return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::NonZero), temp2, temp2);
2375     }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
2376         this->emitThrowException(jit, ExceptionType::OutOfBoundsTrunc);
2377     });
2378
2379     auto* patchpoint = addPatchpoint(B3::Int32);
2380     patchpoint->effects = B3::Effects::none();
2381     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2382         jit.truncateDoubleToInt32(params[1].fpr(), params[0].gpr());
2383     });
2384     result = g32();
2385     emitPatchpoint(patchpoint, result, arg);
2386
2387     return { };
2388 }
2389
2390 template<>
2391 auto AirIRGenerator::addOp<OpType::I32TruncSF32>(ExpressionType arg, ExpressionType& result) -> PartialResult
2392 {
2393     auto max = addConstant(Type::F32, bitwise_cast<uint32_t>(-static_cast<float>(std::numeric_limits<int32_t>::min())));
2394     auto min = addConstant(Type::F32, bitwise_cast<uint32_t>(static_cast<float>(std::numeric_limits<int32_t>::min())));
2395
2396     auto temp1 = g32();
2397     auto temp2 = g32();
2398     append(CompareFloat, Arg::doubleCond(MacroAssembler::DoubleLessThanOrUnordered), arg, min, temp1);
2399     append(CompareFloat, Arg::doubleCond(MacroAssembler::DoubleGreaterThanOrEqualOrUnordered), arg, max, temp2);
2400     append(Or32, temp1, temp2);
2401
2402     emitCheck([&] {
2403         return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::NonZero), temp2, temp2);
2404     }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
2405         this->emitThrowException(jit, ExceptionType::OutOfBoundsTrunc);
2406     });
2407
2408     auto* patchpoint = addPatchpoint(B3::Int32);
2409     patchpoint->effects = B3::Effects::none();
2410     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2411         jit.truncateFloatToInt32(params[1].fpr(), params[0].gpr());
2412     });
2413     result = g32();
2414     emitPatchpoint(patchpoint, result, arg);
2415     return { };
2416 }
2417
2418
2419 template<>
2420 auto AirIRGenerator::addOp<OpType::I32TruncUF64>(ExpressionType arg, ExpressionType& result) -> PartialResult
2421 {
2422     auto max = addConstant(Type::F64, bitwise_cast<uint64_t>(static_cast<double>(std::numeric_limits<int32_t>::min()) * -2.0));
2423     auto min = addConstant(Type::F64, bitwise_cast<uint64_t>(-1.0));
2424
2425     auto temp1 = g32();
2426     auto temp2 = g32();
2427     append(CompareDouble, Arg::doubleCond(MacroAssembler::DoubleLessThanOrEqualOrUnordered), arg, min, temp1);
2428     append(CompareDouble, Arg::doubleCond(MacroAssembler::DoubleGreaterThanOrEqualOrUnordered), arg, max, temp2);
2429     append(Or32, temp1, temp2);
2430
2431     emitCheck([&] {
2432         return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::NonZero), temp2, temp2);
2433     }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
2434         this->emitThrowException(jit, ExceptionType::OutOfBoundsTrunc);
2435     });
2436
2437     auto* patchpoint = addPatchpoint(B3::Int32);
2438     patchpoint->effects = B3::Effects::none();
2439     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2440         jit.truncateDoubleToUint32(params[1].fpr(), params[0].gpr());
2441     });
2442     result = g32();
2443     emitPatchpoint(patchpoint, result, arg);
2444     return { };
2445 }
2446
2447 template<>
2448 auto AirIRGenerator::addOp<OpType::I32TruncUF32>(ExpressionType arg, ExpressionType& result) -> PartialResult
2449 {
2450     auto max = addConstant(Type::F32, bitwise_cast<uint32_t>(static_cast<float>(std::numeric_limits<int32_t>::min()) * static_cast<float>(-2.0)));
2451     auto min = addConstant(Type::F32, bitwise_cast<uint32_t>(static_cast<float>(-1.0)));
2452
2453     auto temp1 = g32();
2454     auto temp2 = g32();
2455     append(CompareFloat, Arg::doubleCond(MacroAssembler::DoubleLessThanOrEqualOrUnordered), arg, min, temp1);
2456     append(CompareFloat, Arg::doubleCond(MacroAssembler::DoubleGreaterThanOrEqualOrUnordered), arg, max, temp2);
2457     append(Or32, temp1, temp2);
2458
2459     emitCheck([&] {
2460         return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::NonZero), temp2, temp2);
2461     }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
2462         this->emitThrowException(jit, ExceptionType::OutOfBoundsTrunc);
2463     });
2464
2465     auto* patchpoint = addPatchpoint(B3::Int32);
2466     patchpoint->effects = B3::Effects::none();
2467     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2468         jit.truncateFloatToUint32(params[1].fpr(), params[0].gpr());
2469     });
2470     result = g32();
2471     emitPatchpoint(patchpoint, result, arg);
2472     return { };
2473 }
2474
2475 template<>
2476 auto AirIRGenerator::addOp<OpType::I64TruncSF64>(ExpressionType arg, ExpressionType& result) -> PartialResult
2477 {
2478     auto max = addConstant(Type::F64, bitwise_cast<uint64_t>(-static_cast<double>(std::numeric_limits<int64_t>::min())));
2479     auto min = addConstant(Type::F64, bitwise_cast<uint64_t>(static_cast<double>(std::numeric_limits<int64_t>::min())));
2480
2481     auto temp1 = g32();
2482     auto temp2 = g32();
2483     append(CompareDouble, Arg::doubleCond(MacroAssembler::DoubleLessThanOrUnordered), arg, min, temp1);
2484     append(CompareDouble, Arg::doubleCond(MacroAssembler::DoubleGreaterThanOrEqualOrUnordered), arg, max, temp2);
2485     append(Or32, temp1, temp2);
2486
2487     emitCheck([&] {
2488         return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::NonZero), temp2, temp2);
2489     }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
2490         this->emitThrowException(jit, ExceptionType::OutOfBoundsTrunc);
2491     });
2492
2493     auto* patchpoint = addPatchpoint(B3::Int64);
2494     patchpoint->effects = B3::Effects::none();
2495     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2496         jit.truncateDoubleToInt64(params[1].fpr(), params[0].gpr());
2497     });
2498
2499     result = g64();
2500     emitPatchpoint(patchpoint, result, arg);
2501     return { };
2502 }
2503
2504 template<>
2505 auto AirIRGenerator::addOp<OpType::I64TruncUF64>(ExpressionType arg, ExpressionType& result) -> PartialResult
2506 {
2507     auto max = addConstant(Type::F64, bitwise_cast<uint64_t>(static_cast<double>(std::numeric_limits<int64_t>::min()) * -2.0));
2508     auto min = addConstant(Type::F64, bitwise_cast<uint64_t>(-1.0));
2509     
2510     auto temp1 = g32();
2511     auto temp2 = g32();
2512     append(CompareDouble, Arg::doubleCond(MacroAssembler::DoubleLessThanOrEqualOrUnordered), arg, min, temp1);
2513     append(CompareDouble, Arg::doubleCond(MacroAssembler::DoubleGreaterThanOrEqualOrUnordered), arg, max, temp2);
2514     append(Or32, temp1, temp2);
2515
2516     emitCheck([&] {
2517         return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::NonZero), temp2, temp2);
2518     }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
2519         this->emitThrowException(jit, ExceptionType::OutOfBoundsTrunc);
2520     });
2521
2522     TypedTmp signBitConstant;
2523     if (isX86())
2524         signBitConstant = addConstant(Type::F64, bitwise_cast<uint64_t>(static_cast<double>(std::numeric_limits<uint64_t>::max() - std::numeric_limits<int64_t>::max())));
2525
2526     Vector<ConstrainedTmp> args;
2527     auto* patchpoint = addPatchpoint(B3::Int64);
2528     patchpoint->effects = B3::Effects::none();
2529     patchpoint->clobber(RegisterSet::macroScratchRegisters());
2530     args.append(arg);
2531     if (isX86()) {
2532         args.append(signBitConstant);
2533         patchpoint->numFPScratchRegisters = 1;
2534     }
2535     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2536         AllowMacroScratchRegisterUsage allowScratch(jit);
2537         FPRReg scratch = InvalidFPRReg;
2538         FPRReg constant = InvalidFPRReg;
2539         if (isX86()) {
2540             scratch = params.fpScratch(0);
2541             constant = params[2].fpr();
2542         }
2543         jit.truncateDoubleToUint64(params[1].fpr(), params[0].gpr(), scratch, constant);
2544     });
2545
2546     result = g64();
2547     emitPatchpoint(m_currentBlock, patchpoint, result, WTFMove(args));
2548     return { };
2549 }
2550
2551 template<>
2552 auto AirIRGenerator::addOp<OpType::I64TruncSF32>(ExpressionType arg, ExpressionType& result) -> PartialResult
2553 {
2554     auto max = addConstant(Type::F32, bitwise_cast<uint32_t>(-static_cast<float>(std::numeric_limits<int64_t>::min())));
2555     auto min = addConstant(Type::F32, bitwise_cast<uint32_t>(static_cast<float>(std::numeric_limits<int64_t>::min())));
2556
2557     auto temp1 = g32();
2558     auto temp2 = g32();
2559     append(CompareFloat, Arg::doubleCond(MacroAssembler::DoubleLessThanOrUnordered), arg, min, temp1);
2560     append(CompareFloat, Arg::doubleCond(MacroAssembler::DoubleGreaterThanOrEqualOrUnordered), arg, max, temp2);
2561     append(Or32, temp1, temp2);
2562
2563     emitCheck([&] {
2564         return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::NonZero), temp2, temp2);
2565     }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
2566         this->emitThrowException(jit, ExceptionType::OutOfBoundsTrunc);
2567     });
2568
2569     auto* patchpoint = addPatchpoint(B3::Int64);
2570     patchpoint->effects = B3::Effects::none();
2571     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2572         jit.truncateFloatToInt64(params[1].fpr(), params[0].gpr());
2573     });
2574     result = g64();
2575     emitPatchpoint(patchpoint, result, arg);
2576     return { };
2577 }
2578
2579 template<>
2580 auto AirIRGenerator::addOp<OpType::I64TruncUF32>(ExpressionType arg, ExpressionType& result) -> PartialResult
2581 {
2582     auto max = addConstant(Type::F32, bitwise_cast<uint32_t>(static_cast<float>(std::numeric_limits<int64_t>::min()) * static_cast<float>(-2.0)));
2583     auto min = addConstant(Type::F32, bitwise_cast<uint32_t>(static_cast<float>(-1.0)));
2584     
2585     auto temp1 = g32();
2586     auto temp2 = g32();
2587     append(CompareFloat, Arg::doubleCond(MacroAssembler::DoubleLessThanOrEqualOrUnordered), arg, min, temp1);
2588     append(CompareFloat, Arg::doubleCond(MacroAssembler::DoubleGreaterThanOrEqualOrUnordered), arg, max, temp2);
2589     append(Or32, temp1, temp2);
2590
2591     emitCheck([&] {
2592         return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::NonZero), temp2, temp2);
2593     }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
2594         this->emitThrowException(jit, ExceptionType::OutOfBoundsTrunc);
2595     });
2596
2597     TypedTmp signBitConstant;
2598     if (isX86())
2599         signBitConstant = addConstant(Type::F32, bitwise_cast<uint32_t>(static_cast<float>(std::numeric_limits<uint64_t>::max() - std::numeric_limits<int64_t>::max())));
2600
2601     auto* patchpoint = addPatchpoint(B3::Int64);
2602     patchpoint->effects = B3::Effects::none();
2603     patchpoint->clobber(RegisterSet::macroScratchRegisters());
2604     Vector<ConstrainedTmp> args;
2605     args.append(arg);
2606     if (isX86()) {
2607         args.append(signBitConstant);
2608         patchpoint->numFPScratchRegisters = 1;
2609     }
2610     patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2611         AllowMacroScratchRegisterUsage allowScratch(jit);
2612         FPRReg scratch = InvalidFPRReg;
2613         FPRReg constant = InvalidFPRReg;
2614         if (isX86()) {
2615             scratch = params.fpScratch(0);
2616             constant = params[2].fpr();
2617         }
2618         jit.truncateFloatToUint64(params[1].fpr(), params[0].gpr(), scratch, constant);
2619     });
2620
2621     result = g64();
2622     emitPatchpoint(m_currentBlock, patchpoint, result, WTFMove(args));
2623
2624     return { };
2625 }
2626
2627 auto AirIRGenerator::addShift(Type type, B3::Air::Opcode op, ExpressionType value, ExpressionType shift, ExpressionType& result) -> PartialResult
2628 {
2629     ASSERT(type == Type::I64 || type == Type::I32);
2630     result = tmpForType(type);
2631
2632     if (isValidForm(op, Arg::Tmp, Arg::Tmp, Arg::Tmp)) {
2633         append(op, value, shift, result);
2634         return { };
2635     }
2636     
2637 #if CPU(X86_64)
2638     Tmp ecx = Tmp(X86Registers::ecx);
2639     append(Move, value, result);
2640     append(Move, shift, ecx);
2641     append(op, ecx, result);
2642 #else
2643     RELEASE_ASSERT_NOT_REACHED();
2644 #endif
2645     return { };
2646 }
2647
2648 auto AirIRGenerator::addIntegerSub(B3::Air::Opcode op, ExpressionType lhs, ExpressionType rhs, ExpressionType& result) -> PartialResult
2649 {
2650     ASSERT(op == Sub32 || op == Sub64);
2651
2652     result = op == Sub32 ? g32() : g64();
2653
2654     if (isValidForm(op, Arg::Tmp, Arg::Tmp, Arg::Tmp)) {
2655         append(op, lhs, rhs, result);
2656         return { };
2657     }
2658
2659     RELEASE_ASSERT(isX86());
2660     // Sub a, b
2661     // means
2662     // b = b Sub a
2663     append(Move, lhs, result);
2664     append(op, rhs, result);
2665     return { };
2666 }
2667
2668 auto AirIRGenerator::addFloatingPointAbs(B3::Air::Opcode op, ExpressionType value, ExpressionType& result) -> PartialResult
2669 {
2670     RELEASE_ASSERT(op == AbsFloat || op == AbsDouble);
2671
2672     result = op == AbsFloat ? f32() : f64();
2673
2674     if (isValidForm(op, Arg::Tmp, Arg::Tmp)) {
2675         append(op, value, result);
2676         return { };
2677     }
2678
2679     RELEASE_ASSERT(isX86());
2680
2681     if (op == AbsFloat) {
2682         auto constant = g32();
2683         append(Move, Arg::imm(static_cast<uint32_t>(~(1ull << 31))), constant);
2684         append(Move32ToFloat, constant, result);
2685         append(AndFloat, value, result);
2686     } else {
2687         auto constant = g64();
2688         append(Move, Arg::bigImm(~(1ull << 63)), constant);
2689         append(Move64ToDouble, constant, result);
2690         append(AndDouble, value, result);
2691     }
2692     return { };
2693 }
2694
2695 auto AirIRGenerator::addFloatingPointBinOp(Type type, B3::Air::Opcode op, ExpressionType lhs, ExpressionType rhs, ExpressionType& result) -> PartialResult
2696 {
2697     ASSERT(type == Type::F32 || type == Type::F64);
2698     result = tmpForType(type);
2699
2700     if (isValidForm(op, Arg::Tmp, Arg::Tmp, Arg::Tmp)) {
2701         append(op, lhs, rhs, result);
2702         return { };
2703     }
2704
2705     RELEASE_ASSERT(isX86());
2706
2707     // Op a, b
2708     // means
2709     // b = b Op a
2710     append(moveOpForValueType(type), lhs, result);
2711     append(op, rhs, result);
2712     return { };
2713 }
2714
2715 template<> auto AirIRGenerator::addOp<OpType::F32Ceil>(ExpressionType arg0, ExpressionType& result) -> PartialResult
2716 {
2717     result = f32();
2718     append(CeilFloat, arg0, result);
2719     return { };
2720 }
2721
2722 template<> auto AirIRGenerator::addOp<OpType::I32Mul>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2723 {
2724     result = g32();
2725     append(Mul32, arg0, arg1, result);
2726     return { };
2727 }
2728
2729 template<> auto AirIRGenerator::addOp<OpType::I32Sub>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2730 {
2731     return addIntegerSub(Sub32, arg0, arg1, result);
2732 }
2733
2734 template<> auto AirIRGenerator::addOp<OpType::F64Le>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2735 {
2736     result = g32();
2737     append(CompareDouble, Arg::doubleCond(MacroAssembler::DoubleLessThanOrEqual), arg0, arg1, result);
2738     return { };
2739 }
2740
2741 template<> auto AirIRGenerator::addOp<OpType::F32DemoteF64>(ExpressionType arg0, ExpressionType& result) -> PartialResult
2742 {
2743     result = f32();
2744     append(ConvertDoubleToFloat, arg0, result);
2745     return { };
2746 }
2747
2748 template<> auto AirIRGenerator::addOp<OpType::F32Min>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2749 {
2750     return addFloatingPointMinOrMax(F32, MinOrMax::Min, arg0, arg1, result);
2751 }
2752
2753 template<> auto AirIRGenerator::addOp<OpType::F64Ne>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2754 {
2755     result = g32();
2756     append(CompareDouble, Arg::doubleCond(MacroAssembler::DoubleNotEqualOrUnordered), arg0, arg1, result);
2757     return { };
2758 }
2759
2760 template<> auto AirIRGenerator::addOp<OpType::F64Lt>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2761 {
2762     result = g32();
2763     append(CompareDouble, Arg::doubleCond(MacroAssembler::DoubleLessThan), arg0, arg1, result);
2764     return { };
2765 }
2766
2767 auto AirIRGenerator::addFloatingPointMinOrMax(Type floatType, MinOrMax minOrMax, ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2768 {
2769     ASSERT(floatType == F32 || floatType == F64);
2770     result = tmpForType(floatType);
2771
2772     BasicBlock* isEqual = m_code.addBlock();
2773     BasicBlock* notEqual = m_code.addBlock();
2774     BasicBlock* isLessThan = m_code.addBlock();
2775     BasicBlock* notLessThan = m_code.addBlock();
2776     BasicBlock* isGreaterThan = m_code.addBlock();
2777     BasicBlock* isNaN = m_code.addBlock();
2778     BasicBlock* continuation = m_code.addBlock();
2779
2780     auto branchOp = floatType == F32 ? BranchFloat : BranchDouble;
2781     append(m_currentBlock, branchOp, Arg::doubleCond(MacroAssembler::DoubleEqual), arg0, arg1);
2782     m_currentBlock->setSuccessors(isEqual, notEqual);
2783
2784     append(notEqual, branchOp, Arg::doubleCond(MacroAssembler::DoubleLessThan), arg0, arg1);
2785     notEqual->setSuccessors(isLessThan, notLessThan);
2786
2787     append(notLessThan, branchOp, Arg::doubleCond(MacroAssembler::DoubleGreaterThan), arg0, arg1);
2788     notLessThan->setSuccessors(isGreaterThan, isNaN);
2789
2790     auto andOp = floatType == F32 ? AndFloat : AndDouble;
2791     auto orOp = floatType == F32 ? OrFloat : OrDouble;
2792     append(isEqual, minOrMax == MinOrMax::Max ? andOp : orOp, arg0, arg1, result);
2793     append(isEqual, Jump);
2794     isEqual->setSuccessors(continuation);
2795
2796     auto isLessThanResult = minOrMax == MinOrMax::Max ? arg1 : arg0;
2797     append(isLessThan, moveOpForValueType(floatType), isLessThanResult, result);
2798     append(isLessThan, Jump);
2799     isLessThan->setSuccessors(continuation);
2800
2801     auto isGreaterThanResult = minOrMax == MinOrMax::Max ? arg0 : arg1;
2802     append(isGreaterThan, moveOpForValueType(floatType), isGreaterThanResult, result);
2803     append(isGreaterThan, Jump);
2804     isGreaterThan->setSuccessors(continuation);
2805
2806     auto addOp = floatType == F32 ? AddFloat : AddDouble;
2807     append(isNaN, addOp, arg0, arg1, result);
2808     append(isNaN, Jump);
2809     isNaN->setSuccessors(continuation);
2810
2811     m_currentBlock = continuation;
2812
2813     return { };
2814 }
2815
2816 template<> auto AirIRGenerator::addOp<OpType::F32Max>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2817 {
2818     return addFloatingPointMinOrMax(F32, MinOrMax::Max, arg0, arg1, result);
2819 }
2820
2821 template<> auto AirIRGenerator::addOp<OpType::F64Mul>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2822 {
2823     return addFloatingPointBinOp(Type::F64, MulDouble, arg0, arg1, result);
2824 }
2825
2826 template<> auto AirIRGenerator::addOp<OpType::F32Div>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2827 {
2828     return addFloatingPointBinOp(Type::F32, DivFloat, arg0, arg1, result);
2829 }
2830
2831 template<> auto AirIRGenerator::addOp<OpType::I32Clz>(ExpressionType arg0, ExpressionType& result) -> PartialResult
2832 {
2833     result = g32();
2834     append(CountLeadingZeros32, arg0, result);
2835     return { };
2836 }
2837
2838 template<> auto AirIRGenerator::addOp<OpType::F32Copysign>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2839 {
2840     // FIXME: We can have better codegen here for the imms and two operand forms on x86
2841     // https://bugs.webkit.org/show_bug.cgi?id=193999
2842     result = f32();
2843     auto temp1 = g32();
2844     auto sign = g32();
2845     auto value = g32();
2846
2847     // FIXME: Try to use Imm where possible:
2848     // https://bugs.webkit.org/show_bug.cgi?id=193999
2849     append(MoveFloatTo32, arg1, temp1);
2850     append(Move, Arg::bigImm(0x80000000), sign);
2851     append(And32, temp1, sign, sign);
2852
2853     append(MoveDoubleTo64, arg0, temp1);
2854     append(Move, Arg::bigImm(0x7fffffff), value);
2855     append(And32, temp1, value, value);
2856
2857     append(Or32, sign, value, value);
2858     append(Move32ToFloat, value, result);
2859
2860     return { };
2861 }
2862
2863 template<> auto AirIRGenerator::addOp<OpType::F64ConvertUI32>(ExpressionType arg0, ExpressionType& result) -> PartialResult
2864 {
2865     result = f64();
2866     auto temp = g64();
2867     append(Move32, arg0, temp);
2868     append(ConvertInt64ToDouble, temp, result);
2869     return { };
2870 }
2871
2872 template<> auto AirIRGenerator::addOp<OpType::F32ReinterpretI32>(ExpressionType arg0, ExpressionType& result) -> PartialResult
2873 {
2874     result = f32();
2875     append(Move32ToFloat, arg0, result);
2876     return { };
2877 }
2878
2879 template<> auto AirIRGenerator::addOp<OpType::I64And>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2880 {
2881     result = g64();
2882     append(And64, arg0, arg1, result);
2883     return { };
2884 }
2885
2886 template<> auto AirIRGenerator::addOp<OpType::F32Ne>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2887 {
2888     result = g32();
2889     append(CompareFloat, Arg::doubleCond(MacroAssembler::DoubleNotEqualOrUnordered), arg0, arg1, result);
2890     return { };
2891 }
2892
2893 template<> auto AirIRGenerator::addOp<OpType::F64Gt>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2894 {
2895     result = g32();
2896     append(CompareDouble, Arg::doubleCond(MacroAssembler::DoubleGreaterThan), arg0, arg1, result);
2897     return { };
2898 }
2899
2900 template<> auto AirIRGenerator::addOp<OpType::F32Sqrt>(ExpressionType arg0, ExpressionType& result) -> PartialResult
2901 {
2902     result = f32();
2903     append(SqrtFloat, arg0, result);
2904     return { };
2905 }
2906
2907 template<> auto AirIRGenerator::addOp<OpType::F64Ge>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2908 {
2909     result = g32();
2910     append(CompareDouble, Arg::doubleCond(MacroAssembler::DoubleGreaterThanOrEqual), arg0, arg1, result);
2911     return { };
2912 }
2913
2914 template<> auto AirIRGenerator::addOp<OpType::I64GtS>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2915 {
2916     result = g32();
2917     append(Compare64, Arg::relCond(MacroAssembler::GreaterThan), arg0, arg1, result);
2918     return { };
2919 }
2920
2921 template<> auto AirIRGenerator::addOp<OpType::I64GtU>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2922 {
2923     result = g32();
2924     append(Compare64, Arg::relCond(MacroAssembler::Above), arg0, arg1, result);
2925     return { };
2926 }
2927
2928 template<> auto AirIRGenerator::addOp<OpType::I64Eqz>(ExpressionType arg0, ExpressionType& result) -> PartialResult
2929 {
2930     result = g32();
2931     append(Test64, Arg::resCond(MacroAssembler::Zero), arg0, arg0, result);
2932     return { };
2933 }
2934
2935 template<> auto AirIRGenerator::addOp<OpType::F64Div>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2936 {
2937     return addFloatingPointBinOp(Type::F64, DivDouble, arg0, arg1, result);
2938 }
2939
2940 template<> auto AirIRGenerator::addOp<OpType::F32Add>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2941 {
2942     result = f32();
2943     append(AddFloat, arg0, arg1, result);
2944     return { };
2945 }
2946
2947 template<> auto AirIRGenerator::addOp<OpType::I64Or>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2948 {
2949     result = g64();
2950     append(Or64, arg0, arg1, result);
2951     return { };
2952 }
2953
2954 template<> auto AirIRGenerator::addOp<OpType::I32LeU>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2955 {
2956     result = g32();
2957     append(Compare32, Arg::relCond(MacroAssembler::BelowOrEqual), arg0, arg1, result);
2958     return { };
2959 }
2960
2961 template<> auto AirIRGenerator::addOp<OpType::I32LeS>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2962 {
2963     result = g32();
2964     append(Compare32, Arg::relCond(MacroAssembler::LessThanOrEqual), arg0, arg1, result);
2965     return { };
2966 }
2967
2968 template<> auto AirIRGenerator::addOp<OpType::I64Ne>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2969 {
2970     result = g32();
2971     append(Compare64, Arg::relCond(MacroAssembler::NotEqual), arg0, arg1, result);
2972     return { };
2973 }
2974
2975 template<> auto AirIRGenerator::addOp<OpType::I64Clz>(ExpressionType arg0, ExpressionType& result) -> PartialResult
2976 {
2977     result = g64();
2978     append(CountLeadingZeros64, arg0, result);
2979     return { };
2980 }
2981
2982 template<> auto AirIRGenerator::addOp<OpType::F32Neg>(ExpressionType arg0, ExpressionType& result) -> PartialResult
2983 {
2984     result = f32();
2985     if (isValidForm(NegateFloat, Arg::Tmp, Arg::Tmp))
2986         append(NegateFloat, arg0, result);
2987     else {
2988         auto constant = addConstant(Type::I32, bitwise_cast<uint32_t>(static_cast<float>(-0.0)));
2989         auto temp = g32();
2990         append(MoveFloatTo32, arg0, temp);
2991         append(Xor32, constant, temp);
2992         append(Move32ToFloat, temp, result);
2993     }
2994     return { };
2995 }
2996
2997 template<> auto AirIRGenerator::addOp<OpType::I32And>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
2998 {
2999     result = g32();
3000     append(And32, arg0, arg1, result);
3001     return { };
3002 }
3003
3004 template<> auto AirIRGenerator::addOp<OpType::I32LtU>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3005 {
3006     result = g32();
3007     append(Compare32, Arg::relCond(MacroAssembler::Below), arg0, arg1, result);
3008     return { };
3009 }
3010
3011 template<> auto AirIRGenerator::addOp<OpType::I64Rotr>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3012 {
3013     return addShift(Type::I64, RotateRight64, arg0, arg1, result);
3014 }
3015
3016 template<> auto AirIRGenerator::addOp<OpType::F64Abs>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3017 {
3018     return addFloatingPointAbs(AbsDouble, arg0, result);
3019 }
3020
3021 template<> auto AirIRGenerator::addOp<OpType::I32LtS>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3022 {
3023     result = g32();
3024     append(Compare32, Arg::relCond(MacroAssembler::LessThan), arg0, arg1, result);
3025     return { };
3026 }
3027
3028 template<> auto AirIRGenerator::addOp<OpType::I32Eq>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3029 {
3030     result = g32();
3031     append(Compare32, Arg::relCond(MacroAssembler::Equal), arg0, arg1, result);
3032     return { };
3033 }
3034
3035 template<> auto AirIRGenerator::addOp<OpType::F64Copysign>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3036 {
3037     // FIXME: We can have better codegen here for the imms and two operand forms on x86
3038     // https://bugs.webkit.org/show_bug.cgi?id=193999
3039     result = f64();
3040     auto temp1 = g64();
3041     auto sign = g64();
3042     auto value = g64();
3043
3044     append(MoveDoubleTo64, arg1, temp1);
3045     append(Move, Arg::bigImm(0x8000000000000000), sign);
3046     append(And64, temp1, sign, sign);
3047
3048     append(MoveDoubleTo64, arg0, temp1);
3049     append(Move, Arg::bigImm(0x7fffffffffffffff), value);
3050     append(And64, temp1, value, value);
3051
3052     append(Or64, sign, value, value);
3053     append(Move64ToDouble, value, result);
3054
3055     return { };
3056 }
3057
3058 template<> auto AirIRGenerator::addOp<OpType::F32ConvertSI64>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3059 {
3060     result = f32();
3061     append(ConvertInt64ToFloat, arg0, result);
3062     return { };
3063 }
3064
3065 template<> auto AirIRGenerator::addOp<OpType::I64Rotl>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3066 {
3067     if (isARM64()) {
3068         // ARM64 doesn't have a rotate left.
3069         auto newShift = g64();
3070         append(Move, arg1, newShift);
3071         append(Neg64, newShift);
3072         return addShift(Type::I64, RotateRight64, arg0, newShift, result);
3073     } else
3074         return addShift(Type::I64, RotateLeft64, arg0, arg1, result);
3075 }
3076
3077 template<> auto AirIRGenerator::addOp<OpType::F32Lt>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3078 {
3079     result = g32();
3080     append(CompareFloat, Arg::doubleCond(MacroAssembler::DoubleLessThan), arg0, arg1, result);
3081     return { };
3082 }
3083
3084 template<> auto AirIRGenerator::addOp<OpType::F64ConvertSI32>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3085 {
3086     result = f64();
3087     append(ConvertInt32ToDouble, arg0, result);
3088     return { };
3089 }
3090
3091 template<> auto AirIRGenerator::addOp<OpType::F64Eq>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3092 {
3093     result = g32();
3094     append(CompareDouble, Arg::doubleCond(MacroAssembler::DoubleEqual), arg0, arg1, result);
3095     return { };
3096 }
3097
3098 template<> auto AirIRGenerator::addOp<OpType::F32Le>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3099 {
3100     result = g32();
3101     append(CompareFloat, Arg::doubleCond(MacroAssembler::DoubleLessThanOrEqual), arg0, arg1, result);
3102     return { };
3103 }
3104
3105 template<> auto AirIRGenerator::addOp<OpType::F32Ge>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3106 {
3107     result = g32();
3108     append(CompareFloat, Arg::doubleCond(MacroAssembler::DoubleGreaterThanOrEqual), arg0, arg1, result);
3109     return { };
3110 }
3111
3112 template<> auto AirIRGenerator::addOp<OpType::I32ShrU>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3113 {
3114     return addShift(Type::I32, Urshift32, arg0, arg1, result);
3115 }
3116
3117 template<> auto AirIRGenerator::addOp<OpType::F32ConvertUI32>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3118 {
3119     result = f32();
3120     auto temp = g64();
3121     append(Move32, arg0, temp);
3122     append(ConvertInt64ToFloat, temp, result);
3123     return { };
3124 }
3125
3126 template<> auto AirIRGenerator::addOp<OpType::I32ShrS>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3127 {
3128     return addShift(Type::I32, Rshift32, arg0, arg1, result);
3129 }
3130
3131 template<> auto AirIRGenerator::addOp<OpType::I32GeU>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3132 {
3133     result = g32();
3134     append(Compare32, Arg::relCond(MacroAssembler::AboveOrEqual), arg0, arg1, result);
3135     return { };
3136 }
3137
3138 template<> auto AirIRGenerator::addOp<OpType::F64Ceil>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3139 {
3140     result = f64();
3141     append(CeilDouble, arg0, result);
3142     return { };
3143 }
3144
3145 template<> auto AirIRGenerator::addOp<OpType::I32GeS>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3146 {
3147     result = g32();
3148     append(Compare32, Arg::relCond(MacroAssembler::GreaterThanOrEqual), arg0, arg1, result);
3149     return { };
3150 }
3151
3152 template<> auto AirIRGenerator::addOp<OpType::I32Shl>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3153 {
3154     return addShift(Type::I32, Lshift32, arg0, arg1, result);
3155 }
3156
3157 template<> auto AirIRGenerator::addOp<OpType::F64Floor>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3158 {
3159     result = f64();
3160     append(FloorDouble, arg0, result);
3161     return { };
3162 }
3163
3164 template<> auto AirIRGenerator::addOp<OpType::I32Xor>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3165 {
3166     result = g32();
3167     append(Xor32, arg0, arg1, result);
3168     return { };
3169 }
3170
3171 template<> auto AirIRGenerator::addOp<OpType::F32Abs>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3172 {
3173     return addFloatingPointAbs(AbsFloat, arg0, result);
3174 }
3175
3176 template<> auto AirIRGenerator::addOp<OpType::F64Min>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3177 {
3178     return addFloatingPointMinOrMax(F64, MinOrMax::Min, arg0, arg1, result);
3179 }
3180
3181 template<> auto AirIRGenerator::addOp<OpType::F32Mul>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3182 {
3183     result = f32();
3184     append(MulFloat, arg0, arg1, result);
3185     return { };
3186 }
3187
3188 template<> auto AirIRGenerator::addOp<OpType::I64Sub>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3189 {
3190     return addIntegerSub(Sub64, arg0, arg1, result);
3191 }
3192
3193 template<> auto AirIRGenerator::addOp<OpType::I32ReinterpretF32>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3194 {
3195     result = g32();
3196     append(MoveFloatTo32, arg0, result);
3197     return { };
3198 }
3199
3200 template<> auto AirIRGenerator::addOp<OpType::I32Add>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3201 {
3202     result = g32();
3203     append(Add32, arg0, arg1, result);
3204     return { };
3205 }
3206
3207 template<> auto AirIRGenerator::addOp<OpType::F64Sub>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3208 {
3209     return addFloatingPointBinOp(Type::F64, SubDouble, arg0, arg1, result);
3210 }
3211
3212 template<> auto AirIRGenerator::addOp<OpType::I32Or>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3213 {
3214     result = g32();
3215     append(Or32, arg0, arg1, result);
3216     return { };
3217 }
3218
3219 template<> auto AirIRGenerator::addOp<OpType::I64LtU>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3220 {
3221     result = g32();
3222     append(Compare64, Arg::relCond(MacroAssembler::Below), arg0, arg1, result);
3223     return { };
3224 }
3225
3226 template<> auto AirIRGenerator::addOp<OpType::I64LtS>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3227 {
3228     result = g32();
3229     append(Compare64, Arg::relCond(MacroAssembler::LessThan), arg0, arg1, result);
3230     return { };
3231 }
3232
3233 template<> auto AirIRGenerator::addOp<OpType::F64ConvertSI64>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3234 {
3235     result = f64();
3236     append(ConvertInt64ToDouble, arg0, result);
3237     return { };
3238 }
3239
3240 template<> auto AirIRGenerator::addOp<OpType::I64Xor>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3241 {
3242     result = g64();
3243     append(Xor64, arg0, arg1, result);
3244     return { };
3245 }
3246
3247 template<> auto AirIRGenerator::addOp<OpType::I64GeU>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3248 {
3249     result = g32();
3250     append(Compare64, Arg::relCond(MacroAssembler::AboveOrEqual), arg0, arg1, result);
3251     return { };
3252 }
3253
3254 template<> auto AirIRGenerator::addOp<OpType::I64Mul>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3255 {
3256     result = g64();
3257     append(Mul64, arg0, arg1, result);
3258     return { };
3259 }
3260
3261 template<> auto AirIRGenerator::addOp<OpType::F32Sub>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3262 {
3263     result = f32();
3264     if (isValidForm(SubFloat, Arg::Tmp, Arg::Tmp, Arg::Tmp))
3265         append(SubFloat, arg0, arg1, result);
3266     else {
3267         RELEASE_ASSERT(isX86());
3268         append(MoveFloat, arg0, result);
3269         append(SubFloat, arg1, result);
3270     }
3271     return { };
3272 }
3273
3274 template<> auto AirIRGenerator::addOp<OpType::F64PromoteF32>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3275 {
3276     result = f64();
3277     append(ConvertFloatToDouble, arg0, result);
3278     return { };
3279 }
3280
3281 template<> auto AirIRGenerator::addOp<OpType::F64Add>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3282 {
3283     result = f64();
3284     append(AddDouble, arg0, arg1, result);
3285     return { };
3286 }
3287
3288 template<> auto AirIRGenerator::addOp<OpType::I64GeS>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3289 {
3290     result = g32();
3291     append(Compare64, Arg::relCond(MacroAssembler::GreaterThanOrEqual), arg0, arg1, result);
3292     return { };
3293 }
3294
3295 template<> auto AirIRGenerator::addOp<OpType::I64ExtendUI32>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3296 {
3297     result = g64();
3298     append(Move32, arg0, result);
3299     return { };
3300 }
3301
3302 template<> auto AirIRGenerator::addOp<OpType::I32Ne>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3303 {
3304     result = g32();
3305     RELEASE_ASSERT(arg0 && arg1);
3306     append(Compare32, Arg::relCond(MacroAssembler::NotEqual), arg0, arg1, result);
3307     return { };
3308 }
3309
3310 template<> auto AirIRGenerator::addOp<OpType::F64ReinterpretI64>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3311 {
3312     result = f64();
3313     append(Move64ToDouble, arg0, result);
3314     return { };
3315 }
3316
3317 template<> auto AirIRGenerator::addOp<OpType::F32Eq>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3318 {
3319     result = g32();
3320     append(CompareFloat, Arg::doubleCond(MacroAssembler::DoubleEqual), arg0, arg1, result);
3321     return { };
3322 }
3323
3324 template<> auto AirIRGenerator::addOp<OpType::I64Eq>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3325 {
3326     result = g32();
3327     append(Compare64, Arg::relCond(MacroAssembler::Equal), arg0, arg1, result);
3328     return { };
3329 }
3330
3331 template<> auto AirIRGenerator::addOp<OpType::F32Floor>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3332 {
3333     result = f32();
3334     append(FloorFloat, arg0, result);
3335     return { };
3336 }
3337
3338 template<> auto AirIRGenerator::addOp<OpType::F32ConvertSI32>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3339 {
3340     result = f32();
3341     append(ConvertInt32ToFloat, arg0, result);
3342     return { };
3343 }
3344
3345 template<> auto AirIRGenerator::addOp<OpType::I32Eqz>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3346 {
3347     result = g32();
3348     append(Test32, Arg::resCond(MacroAssembler::Zero), arg0, arg0, result);
3349     return { };
3350 }
3351
3352 template<> auto AirIRGenerator::addOp<OpType::I64ReinterpretF64>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3353 {
3354     result = g64();
3355     append(MoveDoubleTo64, arg0, result);
3356     return { };
3357 }
3358
3359 template<> auto AirIRGenerator::addOp<OpType::I64ShrS>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3360 {
3361     return addShift(Type::I64, Rshift64, arg0, arg1, result);
3362 }
3363
3364 template<> auto AirIRGenerator::addOp<OpType::I64ShrU>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3365 {
3366     return addShift(Type::I64, Urshift64, arg0, arg1, result);
3367 }
3368
3369 template<> auto AirIRGenerator::addOp<OpType::F64Sqrt>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3370 {
3371     result = f64();
3372     append(SqrtDouble, arg0, result);
3373     return { };
3374 }
3375
3376 template<> auto AirIRGenerator::addOp<OpType::I64Shl>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3377 {
3378     return addShift(Type::I64, Lshift64, arg0, arg1, result);
3379 }
3380
3381 template<> auto AirIRGenerator::addOp<OpType::F32Gt>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3382 {
3383     result = g32();
3384     append(CompareFloat, Arg::doubleCond(MacroAssembler::DoubleGreaterThan), arg0, arg1, result);
3385     return { };
3386 }
3387
3388 template<> auto AirIRGenerator::addOp<OpType::I32WrapI64>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3389 {
3390     result = g32();
3391     append(Move32, arg0, result);
3392     return { };
3393 }
3394
3395 template<> auto AirIRGenerator::addOp<OpType::I32Rotl>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3396 {
3397     if (isARM64()) {
3398         // ARM64 doesn't have a rotate left.
3399         auto newShift = g64();
3400         append(Move, arg1, newShift);
3401         append(Neg64, newShift);
3402         return addShift(Type::I32, RotateRight32, arg0, newShift, result);
3403     } else
3404         return addShift(Type::I32, RotateLeft32, arg0, arg1, result);
3405 }
3406
3407 template<> auto AirIRGenerator::addOp<OpType::I32Rotr>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3408 {
3409     return addShift(Type::I32, RotateRight32, arg0, arg1, result);
3410 }
3411
3412 template<> auto AirIRGenerator::addOp<OpType::I32GtU>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3413 {
3414     result = g32();
3415     append(Compare32, Arg::relCond(MacroAssembler::Above), arg0, arg1, result);
3416     return { };
3417 }
3418
3419 template<> auto AirIRGenerator::addOp<OpType::I64ExtendSI32>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3420 {
3421     result = g64();
3422     append(SignExtend32ToPtr, arg0, result);
3423     return { };
3424 }
3425
3426 template<> auto AirIRGenerator::addOp<OpType::I32GtS>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3427 {
3428     result = g32();
3429     append(Compare32, Arg::relCond(MacroAssembler::GreaterThan), arg0, arg1, result);
3430     return { };
3431 }
3432
3433 template<> auto AirIRGenerator::addOp<OpType::F64Neg>(ExpressionType arg0, ExpressionType& result) -> PartialResult
3434 {
3435     result = f64();
3436     if (isValidForm(NegateDouble, Arg::Tmp, Arg::Tmp))
3437         append(NegateDouble, arg0, result);
3438     else {
3439         auto constant = addConstant(Type::I64, bitwise_cast<uint64_t>(static_cast<double>(-0.0)));
3440         auto temp = g64();
3441         append(MoveDoubleTo64, arg0, temp);
3442         append(Xor64, constant, temp);
3443         append(Move64ToDouble, temp, result);
3444     }
3445     return { };
3446 }
3447
3448 template<> auto AirIRGenerator::addOp<OpType::F64Max>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3449 {
3450     return addFloatingPointMinOrMax(F64, MinOrMax::Max, arg0, arg1, result);
3451 }
3452
3453 template<> auto AirIRGenerator::addOp<OpType::I64LeU>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3454 {
3455     result = g32();
3456     append(Compare64, Arg::relCond(MacroAssembler::BelowOrEqual), arg0, arg1, result);
3457     return { };
3458 }
3459
3460 template<> auto AirIRGenerator::addOp<OpType::I64LeS>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3461 {
3462     result = g32();
3463     append(Compare64, Arg::relCond(MacroAssembler::LessThanOrEqual), arg0, arg1, result);
3464     return { };
3465 }
3466
3467 template<> auto AirIRGenerator::addOp<OpType::I64Add>(ExpressionType arg0, ExpressionType arg1, ExpressionType& result) -> PartialResult
3468 {
3469     result = g64();
3470     append(Add64, arg0, arg1, result);
3471     return { };
3472 }
3473
3474 } } // namespace JSC::Wasm
3475
3476 #endif // ENABLE(WEBASSEMBLY)