[WTF] Import std::optional reference implementation as WTF::Optional
[WebKit-https.git] / Source / JavaScriptCore / b3 / air / AirArg.h
1 /*
2  * Copyright (C) 2015-2016 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 #pragma once
27
28 #if ENABLE(B3_JIT)
29
30 #include "AirTmp.h"
31 #include "B3Common.h"
32 #include "B3Type.h"
33 #include <wtf/Optional.h>
34
35 #if COMPILER(GCC) && ASSERT_DISABLED
36 #pragma GCC diagnostic push
37 #pragma GCC diagnostic ignored "-Wreturn-type"
38 #endif // COMPILER(GCC) && ASSERT_DISABLED
39
40 namespace JSC { namespace B3 {
41
42 class Value;
43
44 namespace Air {
45
46 class Special;
47 class StackSlot;
48
49 // This class name is also intentionally terse because we will say it a lot. You'll see code like
50 // Inst(..., Arg::imm(5), Arg::addr(thing, blah), ...)
51 class Arg {
52 public:
53     // These enum members are intentionally terse because we have to mention them a lot.
54     enum Kind : int8_t {
55         Invalid,
56
57         // This is either an unassigned temporary or a register. All unassigned temporaries
58         // eventually become registers.
59         Tmp,
60
61         // This is an immediate that the instruction will materialize. Imm is the immediate that can be
62         // inlined into most instructions, while BigImm indicates a constant materialization and is
63         // usually only usable with Move. Specials may also admit it, for example for stackmaps used for
64         // OSR exit and tail calls.
65         // BitImm is an immediate for Bitwise operation (And, Xor, etc).
66         Imm,
67         BigImm,
68         BitImm,
69         BitImm64,
70
71         // These are the addresses. Instructions may load from (Use), store to (Def), or evaluate
72         // (UseAddr) addresses.
73         Addr,
74         Stack,
75         CallArg,
76         Index,
77
78         // Immediate operands that customize the behavior of an operation. You can think of them as
79         // secondary opcodes. They are always "Use"'d.
80         RelCond,
81         ResCond,
82         DoubleCond,
83         Special,
84         WidthArg
85     };
86
87     enum Role : int8_t {
88         // Use means that the Inst will read from this value before doing anything else.
89         //
90         // For Tmp: The Inst will read this Tmp.
91         // For Arg::addr and friends: The Inst will load from this address.
92         // For Arg::imm and friends: The Inst will materialize and use this immediate.
93         // For RelCond/ResCond/Special: This is the only valid role for these kinds.
94         //
95         // Note that Use of an address does not mean escape. It only means that the instruction will
96         // load from the address before doing anything else. This is a bit tricky; for example
97         // Specials could theoretically squirrel away the address and effectively escape it. However,
98         // this is not legal. On the other hand, any address other than Stack is presumed to be
99         // always escaping, and Stack is presumed to be always escaping if it's Locked.
100         Use,
101
102         // Exactly like Use, except that it also implies that the use is cold: that is, replacing the
103         // use with something on the stack is free.
104         ColdUse,
105
106         // LateUse means that the Inst will read from this value after doing its Def's. Note that LateUse
107         // on an Addr or Index still means Use on the internal temporaries. Note that specifying the
108         // same Tmp once as Def and once as LateUse has undefined behavior: the use may happen before
109         // the def, or it may happen after it.
110         LateUse,
111
112         // Combination of LateUse and ColdUse.
113         LateColdUse,
114
115         // Def means that the Inst will write to this value after doing everything else.
116         //
117         // For Tmp: The Inst will write to this Tmp.
118         // For Arg::addr and friends: The Inst will store to this address.
119         // This isn't valid for any other kinds.
120         //
121         // Like Use of address, Def of address does not mean escape.
122         Def,
123
124         // This is a special variant of Def that implies that the upper bits of the target register are
125         // zero-filled. Specifically, if the Width of a ZDef is less than the largest possible width of
126         // the argument (for example, we're on a 64-bit machine and we have a Width32 ZDef of a GPR) then
127         // this has different implications for the upper bits (i.e. the top 32 bits in our example)
128         // depending on the kind of the argument:
129         //
130         // For register: the upper bits are zero-filled.
131         // For anonymous stack slot: the upper bits are zero-filled.
132         // For address: the upper bits are not touched (i.e. we do a 32-bit store in our example).
133         // For tmp: either the upper bits are not touched or they are zero-filled, and we won't know
134         // which until we lower the tmp to either a StackSlot or a Reg.
135         //
136         // The behavior of ZDef is consistent with what happens when you perform 32-bit operations on a
137         // 64-bit GPR. It's not consistent with what happens with 8-bit or 16-bit Defs on x86 GPRs, or
138         // what happens with float Defs in ARM NEON or X86 SSE. Hence why we have both Def and ZDef.
139         ZDef,
140
141         // This is a combined Use and Def. It means that both things happen.
142         UseDef,
143
144         // This is a combined Use and ZDef. It means that both things happen.
145         UseZDef,
146
147         // This is like Def, but implies that the assignment occurs before the start of the Inst's
148         // execution rather than after. Note that specifying the same Tmp once as EarlyDef and once
149         // as Use has undefined behavior: the use may happen before the def, or it may happen after
150         // it.
151         EarlyDef,
152
153         // Some instructions need a scratch register. We model this by saying that the temporary is
154         // defined early and used late. This role implies that.
155         Scratch,
156
157         // This is a special kind of use that is only valid for addresses. It means that the
158         // instruction will evaluate the address expression and consume the effective address, but it
159         // will neither load nor store. This is an escaping use, because now the address may be
160         // passed along to who-knows-where. Note that this isn't really a Use of the Arg, but it does
161         // imply that we're Use'ing any registers that the Arg contains.
162         UseAddr
163     };
164
165     enum Type : int8_t {
166         GP,
167         FP
168     };
169
170     static const unsigned numTypes = 2;
171
172     template<typename Functor>
173     static void forEachType(const Functor& functor)
174     {
175         functor(GP);
176         functor(FP);
177     }
178
179     enum Width : int8_t {
180         Width8,
181         Width16,
182         Width32,
183         Width64
184     };
185
186     static Width pointerWidth()
187     {
188         if (sizeof(void*) == 8)
189             return Width64;
190         return Width32;
191     }
192
193     enum Signedness : int8_t {
194         Signed,
195         Unsigned
196     };
197
198     // Returns true if the Role implies that the Inst will Use the Arg. It's deliberately false for
199     // UseAddr, since isAnyUse() for an Arg::addr means that we are loading from the address.
200     static bool isAnyUse(Role role)
201     {
202         switch (role) {
203         case Use:
204         case ColdUse:
205         case UseDef:
206         case UseZDef:
207         case LateUse:
208         case LateColdUse:
209         case Scratch:
210             return true;
211         case Def:
212         case ZDef:
213         case UseAddr:
214         case EarlyDef:
215             return false;
216         }
217         ASSERT_NOT_REACHED();
218     }
219
220     static bool isColdUse(Role role)
221     {
222         switch (role) {
223         case ColdUse:
224         case LateColdUse:
225             return true;
226         case Use:
227         case UseDef:
228         case UseZDef:
229         case LateUse:
230         case Def:
231         case ZDef:
232         case UseAddr:
233         case Scratch:
234         case EarlyDef:
235             return false;
236         }
237         ASSERT_NOT_REACHED();
238     }
239
240     static bool isWarmUse(Role role)
241     {
242         return isAnyUse(role) && !isColdUse(role);
243     }
244
245     static Role cooled(Role role)
246     {
247         switch (role) {
248         case ColdUse:
249         case LateColdUse:
250         case UseDef:
251         case UseZDef:
252         case Def:
253         case ZDef:
254         case UseAddr:
255         case Scratch:
256         case EarlyDef:
257             return role;
258         case Use:
259             return ColdUse;
260         case LateUse:
261             return LateColdUse;
262         }
263         ASSERT_NOT_REACHED();
264     }
265
266     // Returns true if the Role implies that the Inst will Use the Arg before doing anything else.
267     static bool isEarlyUse(Role role)
268     {
269         switch (role) {
270         case Use:
271         case ColdUse:
272         case UseDef:
273         case UseZDef:
274             return true;
275         case Def:
276         case ZDef:
277         case UseAddr:
278         case LateUse:
279         case LateColdUse:
280         case Scratch:
281         case EarlyDef:
282             return false;
283         }
284         ASSERT_NOT_REACHED();
285     }
286
287     // Returns true if the Role implies that the Inst will Use the Arg after doing everything else.
288     static bool isLateUse(Role role)
289     {
290         switch (role) {
291         case LateUse:
292         case LateColdUse:
293         case Scratch:
294             return true;
295         case ColdUse:
296         case Use:
297         case UseDef:
298         case UseZDef:
299         case Def:
300         case ZDef:
301         case UseAddr:
302         case EarlyDef:
303             return false;
304         }
305         ASSERT_NOT_REACHED();
306     }
307
308     // Returns true if the Role implies that the Inst will Def the Arg.
309     static bool isAnyDef(Role role)
310     {
311         switch (role) {
312         case Use:
313         case ColdUse:
314         case UseAddr:
315         case LateUse:
316         case LateColdUse:
317             return false;
318         case Def:
319         case UseDef:
320         case ZDef:
321         case UseZDef:
322         case EarlyDef:
323         case Scratch:
324             return true;
325         }
326         ASSERT_NOT_REACHED();
327     }
328
329     // Returns true if the Role implies that the Inst will Def the Arg before start of execution.
330     static bool isEarlyDef(Role role)
331     {
332         switch (role) {
333         case Use:
334         case ColdUse:
335         case UseAddr:
336         case LateUse:
337         case Def:
338         case UseDef:
339         case ZDef:
340         case UseZDef:
341         case LateColdUse:
342             return false;
343         case EarlyDef:
344         case Scratch:
345             return true;
346         }
347         ASSERT_NOT_REACHED();
348     }
349
350     // Returns true if the Role implies that the Inst will Def the Arg after the end of execution.
351     static bool isLateDef(Role role)
352     {
353         switch (role) {
354         case Use:
355         case ColdUse:
356         case UseAddr:
357         case LateUse:
358         case EarlyDef:
359         case Scratch:
360         case LateColdUse:
361             return false;
362         case Def:
363         case UseDef:
364         case ZDef:
365         case UseZDef:
366             return true;
367         }
368         ASSERT_NOT_REACHED();
369     }
370
371     // Returns true if the Role implies that the Inst will ZDef the Arg.
372     static bool isZDef(Role role)
373     {
374         switch (role) {
375         case Use:
376         case ColdUse:
377         case UseAddr:
378         case LateUse:
379         case Def:
380         case UseDef:
381         case EarlyDef:
382         case Scratch:
383         case LateColdUse:
384             return false;
385         case ZDef:
386         case UseZDef:
387             return true;
388         }
389         ASSERT_NOT_REACHED();
390     }
391
392     static Type typeForB3Type(B3::Type type)
393     {
394         switch (type) {
395         case Void:
396             ASSERT_NOT_REACHED();
397             return GP;
398         case Int32:
399         case Int64:
400             return GP;
401         case Float:
402         case Double:
403             return FP;
404         }
405         ASSERT_NOT_REACHED();
406         return GP;
407     }
408
409     static Width widthForB3Type(B3::Type type)
410     {
411         switch (type) {
412         case Void:
413             ASSERT_NOT_REACHED();
414             return Width8;
415         case Int32:
416         case Float:
417             return Width32;
418         case Int64:
419         case Double:
420             return Width64;
421         }
422         ASSERT_NOT_REACHED();
423     }
424
425     static Width conservativeWidth(Type type)
426     {
427         return type == GP ? pointerWidth() : Width64;
428     }
429
430     static Width minimumWidth(Type type)
431     {
432         return type == GP ? Width8 : Width32;
433     }
434
435     static unsigned bytes(Width width)
436     {
437         return 1 << width;
438     }
439
440     static Width widthForBytes(unsigned bytes)
441     {
442         switch (bytes) {
443         case 0:
444         case 1:
445             return Width8;
446         case 2:
447             return Width16;
448         case 3:
449         case 4:
450             return Width32;
451         default:
452             return Width64;
453         }
454     }
455
456     Arg()
457         : m_kind(Invalid)
458     {
459     }
460
461     Arg(Air::Tmp tmp)
462         : m_kind(Tmp)
463         , m_base(tmp)
464     {
465     }
466
467     Arg(Reg reg)
468         : Arg(Air::Tmp(reg))
469     {
470     }
471
472     static Arg imm(int64_t value)
473     {
474         Arg result;
475         result.m_kind = Imm;
476         result.m_offset = value;
477         return result;
478     }
479
480     static Arg bigImm(int64_t value)
481     {
482         Arg result;
483         result.m_kind = BigImm;
484         result.m_offset = value;
485         return result;
486     }
487
488     static Arg bitImm(int64_t value)
489     {
490         Arg result;
491         result.m_kind = BitImm;
492         result.m_offset = value;
493         return result;
494     }
495
496     static Arg bitImm64(int64_t value)
497     {
498         Arg result;
499         result.m_kind = BitImm64;
500         result.m_offset = value;
501         return result;
502     }
503
504     static Arg immPtr(const void* address)
505     {
506         return bigImm(bitwise_cast<intptr_t>(address));
507     }
508
509     static Arg addr(Air::Tmp base, int32_t offset = 0)
510     {
511         ASSERT(base.isGP());
512         Arg result;
513         result.m_kind = Addr;
514         result.m_base = base;
515         result.m_offset = offset;
516         return result;
517     }
518
519     static Arg stack(StackSlot* value, int32_t offset = 0)
520     {
521         Arg result;
522         result.m_kind = Stack;
523         result.m_offset = bitwise_cast<intptr_t>(value);
524         result.m_scale = offset; // I know, yuck.
525         return result;
526     }
527
528     static Arg callArg(int32_t offset)
529     {
530         Arg result;
531         result.m_kind = CallArg;
532         result.m_offset = offset;
533         return result;
534     }
535
536     static Arg stackAddr(int32_t offsetFromFP, unsigned frameSize, Width width)
537     {
538         Arg result = Arg::addr(Air::Tmp(GPRInfo::callFrameRegister), offsetFromFP);
539         if (!result.isValidForm(width)) {
540             result = Arg::addr(
541                 Air::Tmp(MacroAssembler::stackPointerRegister),
542                 offsetFromFP + frameSize);
543         }
544         return result;
545     }
546
547     // If you don't pass a Width, this optimistically assumes that you're using the right width.
548     static bool isValidScale(unsigned scale, std::optional<Width> width = std::nullopt)
549     {
550         switch (scale) {
551         case 1:
552             if (isX86() || isARM64())
553                 return true;
554             return false;
555         case 2:
556         case 4:
557         case 8:
558             if (isX86())
559                 return true;
560             if (isARM64()) {
561                 if (!width)
562                     return true;
563                 return scale == 1 || scale == bytes(*width);
564             }
565             return false;
566         default:
567             return false;
568         }
569     }
570
571     static unsigned logScale(unsigned scale)
572     {
573         switch (scale) {
574         case 1:
575             return 0;
576         case 2:
577             return 1;
578         case 4:
579             return 2;
580         case 8:
581             return 3;
582         default:
583             ASSERT_NOT_REACHED();
584             return 0;
585         }
586     }
587
588     static Arg index(Air::Tmp base, Air::Tmp index, unsigned scale = 1, int32_t offset = 0)
589     {
590         ASSERT(base.isGP());
591         ASSERT(index.isGP());
592         ASSERT(isValidScale(scale));
593         Arg result;
594         result.m_kind = Index;
595         result.m_base = base;
596         result.m_index = index;
597         result.m_scale = static_cast<int32_t>(scale);
598         result.m_offset = offset;
599         return result;
600     }
601
602     static Arg relCond(MacroAssembler::RelationalCondition condition)
603     {
604         Arg result;
605         result.m_kind = RelCond;
606         result.m_offset = condition;
607         return result;
608     }
609
610     static Arg resCond(MacroAssembler::ResultCondition condition)
611     {
612         Arg result;
613         result.m_kind = ResCond;
614         result.m_offset = condition;
615         return result;
616     }
617
618     static Arg doubleCond(MacroAssembler::DoubleCondition condition)
619     {
620         Arg result;
621         result.m_kind = DoubleCond;
622         result.m_offset = condition;
623         return result;
624     }
625
626     static Arg special(Air::Special* special)
627     {
628         Arg result;
629         result.m_kind = Special;
630         result.m_offset = bitwise_cast<intptr_t>(special);
631         return result;
632     }
633
634     static Arg widthArg(Width width)
635     {
636         Arg result;
637         result.m_kind = WidthArg;
638         result.m_offset = width;
639         return result;
640     }
641
642     bool operator==(const Arg& other) const
643     {
644         return m_offset == other.m_offset
645             && m_kind == other.m_kind
646             && m_base == other.m_base
647             && m_index == other.m_index
648             && m_scale == other.m_scale;
649     }
650
651     bool operator!=(const Arg& other) const
652     {
653         return !(*this == other);
654     }
655
656     explicit operator bool() const { return *this != Arg(); }
657
658     Kind kind() const
659     {
660         return m_kind;
661     }
662
663     bool isTmp() const
664     {
665         return kind() == Tmp;
666     }
667
668     bool isImm() const
669     {
670         return kind() == Imm;
671     }
672
673     bool isBigImm() const
674     {
675         return kind() == BigImm;
676     }
677
678     bool isBitImm() const
679     {
680         return kind() == BitImm;
681     }
682
683     bool isBitImm64() const
684     {
685         return kind() == BitImm64;
686     }
687
688     bool isSomeImm() const
689     {
690         switch (kind()) {
691         case Imm:
692         case BigImm:
693         case BitImm:
694         case BitImm64:
695             return true;
696         default:
697             return false;
698         }
699     }
700
701     bool isAddr() const
702     {
703         return kind() == Addr;
704     }
705
706     bool isStack() const
707     {
708         return kind() == Stack;
709     }
710
711     bool isCallArg() const
712     {
713         return kind() == CallArg;
714     }
715
716     bool isIndex() const
717     {
718         return kind() == Index;
719     }
720
721     bool isMemory() const
722     {
723         switch (kind()) {
724         case Addr:
725         case Stack:
726         case CallArg:
727         case Index:
728             return true;
729         default:
730             return false;
731         }
732     }
733
734     bool isStackMemory() const;
735
736     bool isRelCond() const
737     {
738         return kind() == RelCond;
739     }
740
741     bool isResCond() const
742     {
743         return kind() == ResCond;
744     }
745
746     bool isDoubleCond() const
747     {
748         return kind() == DoubleCond;
749     }
750
751     bool isCondition() const
752     {
753         switch (kind()) {
754         case RelCond:
755         case ResCond:
756         case DoubleCond:
757             return true;
758         default:
759             return false;
760         }
761     }
762
763     bool isSpecial() const
764     {
765         return kind() == Special;
766     }
767
768     bool isWidthArg() const
769     {
770         return kind() == WidthArg;
771     }
772
773     bool isAlive() const
774     {
775         return isTmp() || isStack();
776     }
777
778     Air::Tmp tmp() const
779     {
780         ASSERT(kind() == Tmp);
781         return m_base;
782     }
783
784     int64_t value() const
785     {
786         ASSERT(isSomeImm());
787         return m_offset;
788     }
789
790     template<typename T>
791     bool isRepresentableAs() const
792     {
793         return B3::isRepresentableAs<T>(value());
794     }
795     
796     static bool isRepresentableAs(Width width, Signedness signedness, int64_t value)
797     {
798         switch (signedness) {
799         case Signed:
800             switch (width) {
801             case Width8:
802                 return B3::isRepresentableAs<int8_t>(value);
803             case Width16:
804                 return B3::isRepresentableAs<int16_t>(value);
805             case Width32:
806                 return B3::isRepresentableAs<int32_t>(value);
807             case Width64:
808                 return B3::isRepresentableAs<int64_t>(value);
809             }
810         case Unsigned:
811             switch (width) {
812             case Width8:
813                 return B3::isRepresentableAs<uint8_t>(value);
814             case Width16:
815                 return B3::isRepresentableAs<uint16_t>(value);
816             case Width32:
817                 return B3::isRepresentableAs<uint32_t>(value);
818             case Width64:
819                 return B3::isRepresentableAs<uint64_t>(value);
820             }
821         }
822         ASSERT_NOT_REACHED();
823     }
824
825     bool isRepresentableAs(Width, Signedness) const;
826     
827     static int64_t castToType(Width width, Signedness signedness, int64_t value)
828     {
829         switch (signedness) {
830         case Signed:
831             switch (width) {
832             case Width8:
833                 return static_cast<int8_t>(value);
834             case Width16:
835                 return static_cast<int16_t>(value);
836             case Width32:
837                 return static_cast<int32_t>(value);
838             case Width64:
839                 return static_cast<int64_t>(value);
840             }
841         case Unsigned:
842             switch (width) {
843             case Width8:
844                 return static_cast<uint8_t>(value);
845             case Width16:
846                 return static_cast<uint16_t>(value);
847             case Width32:
848                 return static_cast<uint32_t>(value);
849             case Width64:
850                 return static_cast<uint64_t>(value);
851             }
852         }
853         ASSERT_NOT_REACHED();
854     }
855
856     template<typename T>
857     T asNumber() const
858     {
859         return static_cast<T>(value());
860     }
861
862     void* pointerValue() const
863     {
864         ASSERT(kind() == BigImm);
865         return bitwise_cast<void*>(static_cast<intptr_t>(m_offset));
866     }
867
868     Air::Tmp base() const
869     {
870         ASSERT(kind() == Addr || kind() == Index);
871         return m_base;
872     }
873
874     bool hasOffset() const { return isMemory(); }
875     
876     int32_t offset() const
877     {
878         if (kind() == Stack)
879             return static_cast<int32_t>(m_scale);
880         ASSERT(kind() == Addr || kind() == CallArg || kind() == Index);
881         return static_cast<int32_t>(m_offset);
882     }
883
884     StackSlot* stackSlot() const
885     {
886         ASSERT(kind() == Stack);
887         return bitwise_cast<StackSlot*>(m_offset);
888     }
889
890     Air::Tmp index() const
891     {
892         ASSERT(kind() == Index);
893         return m_index;
894     }
895
896     unsigned scale() const
897     {
898         ASSERT(kind() == Index);
899         return m_scale;
900     }
901
902     unsigned logScale() const
903     {
904         return logScale(scale());
905     }
906
907     Air::Special* special() const
908     {
909         ASSERT(kind() == Special);
910         return bitwise_cast<Air::Special*>(m_offset);
911     }
912
913     Width width() const
914     {
915         ASSERT(kind() == WidthArg);
916         return static_cast<Width>(m_offset);
917     }
918
919     bool isGPTmp() const
920     {
921         return isTmp() && tmp().isGP();
922     }
923
924     bool isFPTmp() const
925     {
926         return isTmp() && tmp().isFP();
927     }
928     
929     // Tells us if this Arg can be used in a position that requires a GP value.
930     bool isGP() const
931     {
932         switch (kind()) {
933         case Imm:
934         case BigImm:
935         case BitImm:
936         case BitImm64:
937         case Addr:
938         case Index:
939         case Stack:
940         case CallArg:
941         case RelCond:
942         case ResCond:
943         case DoubleCond:
944         case Special:
945         case WidthArg:
946             return true;
947         case Tmp:
948             return isGPTmp();
949         case Invalid:
950             return false;
951         }
952         ASSERT_NOT_REACHED();
953     }
954
955     // Tells us if this Arg can be used in a position that requires a FP value.
956     bool isFP() const
957     {
958         switch (kind()) {
959         case Imm:
960         case BitImm:
961         case BitImm64:
962         case RelCond:
963         case ResCond:
964         case DoubleCond:
965         case Special:
966         case WidthArg:
967         case Invalid:
968             return false;
969         case Addr:
970         case Index:
971         case Stack:
972         case CallArg:
973         case BigImm: // Yes, we allow BigImm as a double immediate. We use this for implementing stackmaps.
974             return true;
975         case Tmp:
976             return isFPTmp();
977         }
978         ASSERT_NOT_REACHED();
979     }
980
981     bool hasType() const
982     {
983         switch (kind()) {
984         case Imm:
985         case BitImm:
986         case BitImm64:
987         case Special:
988         case Tmp:
989             return true;
990         default:
991             return false;
992         }
993     }
994     
995     // The type is ambiguous for some arg kinds. Call with care.
996     Type type() const
997     {
998         return isGP() ? GP : FP;
999     }
1000
1001     bool isType(Type type) const
1002     {
1003         switch (type) {
1004         case GP:
1005             return isGP();
1006         case FP:
1007             return isFP();
1008         }
1009         ASSERT_NOT_REACHED();
1010     }
1011
1012     bool canRepresent(Value* value) const;
1013
1014     bool isCompatibleType(const Arg& other) const;
1015
1016     bool isGPR() const
1017     {
1018         return isTmp() && tmp().isGPR();
1019     }
1020
1021     GPRReg gpr() const
1022     {
1023         return tmp().gpr();
1024     }
1025
1026     bool isFPR() const
1027     {
1028         return isTmp() && tmp().isFPR();
1029     }
1030
1031     FPRReg fpr() const
1032     {
1033         return tmp().fpr();
1034     }
1035     
1036     bool isReg() const
1037     {
1038         return isTmp() && tmp().isReg();
1039     }
1040
1041     Reg reg() const
1042     {
1043         return tmp().reg();
1044     }
1045
1046     unsigned gpTmpIndex() const
1047     {
1048         return tmp().gpTmpIndex();
1049     }
1050
1051     unsigned fpTmpIndex() const
1052     {
1053         return tmp().fpTmpIndex();
1054     }
1055
1056     unsigned tmpIndex() const
1057     {
1058         return tmp().tmpIndex();
1059     }
1060
1061     static bool isValidImmForm(int64_t value)
1062     {
1063         if (isX86())
1064             return B3::isRepresentableAs<int32_t>(value);
1065         if (isARM64())
1066             return isUInt12(value);
1067         return false;
1068     }
1069
1070     static bool isValidBitImmForm(int64_t value)
1071     {
1072         if (isX86())
1073             return B3::isRepresentableAs<int32_t>(value);
1074         if (isARM64())
1075             return ARM64LogicalImmediate::create32(value).isValid();
1076         return false;
1077     }
1078
1079     static bool isValidBitImm64Form(int64_t value)
1080     {
1081         if (isX86())
1082             return B3::isRepresentableAs<int32_t>(value);
1083         if (isARM64())
1084             return ARM64LogicalImmediate::create64(value).isValid();
1085         return false;
1086     }
1087
1088     static bool isValidAddrForm(int32_t offset, std::optional<Width> width = std::nullopt)
1089     {
1090         if (isX86())
1091             return true;
1092         if (isARM64()) {
1093             if (!width)
1094                 return true;
1095
1096             if (isValidSignedImm9(offset))
1097                 return true;
1098
1099             switch (*width) {
1100             case Width8:
1101                 return isValidScaledUImm12<8>(offset);
1102             case Width16:
1103                 return isValidScaledUImm12<16>(offset);
1104             case Width32:
1105                 return isValidScaledUImm12<32>(offset);
1106             case Width64:
1107                 return isValidScaledUImm12<64>(offset);
1108             }
1109         }
1110         return false;
1111     }
1112
1113     static bool isValidIndexForm(unsigned scale, int32_t offset, std::optional<Width> width = std::nullopt)
1114     {
1115         if (!isValidScale(scale, width))
1116             return false;
1117         if (isX86())
1118             return true;
1119         if (isARM64())
1120             return !offset;
1121         return false;
1122     }
1123
1124     // If you don't pass a width then this optimistically assumes that you're using the right width. But
1125     // the width is relevant to validity, so passing a null width is only useful for assertions. Don't
1126     // pass null widths when cascading through Args in the instruction selector!
1127     bool isValidForm(std::optional<Width> width = std::nullopt) const
1128     {
1129         switch (kind()) {
1130         case Invalid:
1131             return false;
1132         case Tmp:
1133             return true;
1134         case Imm:
1135             return isValidImmForm(value());
1136         case BigImm:
1137             return true;
1138         case BitImm:
1139             return isValidBitImmForm(value());
1140         case BitImm64:
1141             return isValidBitImm64Form(value());
1142         case Addr:
1143         case Stack:
1144         case CallArg:
1145             return isValidAddrForm(offset(), width);
1146         case Index:
1147             return isValidIndexForm(scale(), offset(), width);
1148         case RelCond:
1149         case ResCond:
1150         case DoubleCond:
1151         case Special:
1152         case WidthArg:
1153             return true;
1154         }
1155         ASSERT_NOT_REACHED();
1156     }
1157
1158     template<typename Functor>
1159     void forEachTmpFast(const Functor& functor)
1160     {
1161         switch (m_kind) {
1162         case Tmp:
1163         case Addr:
1164             functor(m_base);
1165             break;
1166         case Index:
1167             functor(m_base);
1168             functor(m_index);
1169             break;
1170         default:
1171             break;
1172         }
1173     }
1174
1175     bool usesTmp(Air::Tmp tmp) const;
1176
1177     template<typename Thing>
1178     bool is() const;
1179
1180     template<typename Thing>
1181     Thing as() const;
1182
1183     template<typename Thing, typename Functor>
1184     void forEachFast(const Functor&);
1185
1186     template<typename Thing, typename Functor>
1187     void forEach(Role, Type, Width, const Functor&);
1188
1189     // This is smart enough to know that an address arg in a Def or UseDef rule will use its
1190     // tmps and never def them. For example, this:
1191     //
1192     // mov %rax, (%rcx)
1193     //
1194     // This defs (%rcx) but uses %rcx.
1195     template<typename Functor>
1196     void forEachTmp(Role argRole, Type argType, Width argWidth, const Functor& functor)
1197     {
1198         switch (m_kind) {
1199         case Tmp:
1200             ASSERT(isAnyUse(argRole) || isAnyDef(argRole));
1201             functor(m_base, argRole, argType, argWidth);
1202             break;
1203         case Addr:
1204             functor(m_base, Use, GP, argRole == UseAddr ? argWidth : pointerWidth());
1205             break;
1206         case Index:
1207             functor(m_base, Use, GP, argRole == UseAddr ? argWidth : pointerWidth());
1208             functor(m_index, Use, GP, argRole == UseAddr ? argWidth : pointerWidth());
1209             break;
1210         default:
1211             break;
1212         }
1213     }
1214
1215     MacroAssembler::TrustedImm32 asTrustedImm32() const
1216     {
1217         ASSERT(isImm() || isBitImm());
1218         return MacroAssembler::TrustedImm32(static_cast<int32_t>(m_offset));
1219     }
1220
1221 #if USE(JSVALUE64)
1222     MacroAssembler::TrustedImm64 asTrustedImm64() const
1223     {
1224         ASSERT(isBigImm() || isBitImm64());
1225         return MacroAssembler::TrustedImm64(value());
1226     }
1227 #endif
1228
1229     MacroAssembler::TrustedImmPtr asTrustedImmPtr() const
1230     {
1231         if (is64Bit())
1232             ASSERT(isBigImm());
1233         else
1234             ASSERT(isImm());
1235         return MacroAssembler::TrustedImmPtr(pointerValue());
1236     }
1237
1238     MacroAssembler::Address asAddress() const
1239     {
1240         ASSERT(isAddr());
1241         return MacroAssembler::Address(m_base.gpr(), static_cast<int32_t>(m_offset));
1242     }
1243
1244     MacroAssembler::BaseIndex asBaseIndex() const
1245     {
1246         ASSERT(isIndex());
1247         return MacroAssembler::BaseIndex(
1248             m_base.gpr(), m_index.gpr(), static_cast<MacroAssembler::Scale>(logScale()),
1249             static_cast<int32_t>(m_offset));
1250     }
1251
1252     MacroAssembler::RelationalCondition asRelationalCondition() const
1253     {
1254         ASSERT(isRelCond());
1255         return static_cast<MacroAssembler::RelationalCondition>(m_offset);
1256     }
1257
1258     MacroAssembler::ResultCondition asResultCondition() const
1259     {
1260         ASSERT(isResCond());
1261         return static_cast<MacroAssembler::ResultCondition>(m_offset);
1262     }
1263
1264     MacroAssembler::DoubleCondition asDoubleCondition() const
1265     {
1266         ASSERT(isDoubleCond());
1267         return static_cast<MacroAssembler::DoubleCondition>(m_offset);
1268     }
1269     
1270     // Tells you if the Arg is invertible. Only condition arguments are invertible, and even for those, there
1271     // are a few exceptions - notably Overflow and Signed.
1272     bool isInvertible() const
1273     {
1274         switch (kind()) {
1275         case RelCond:
1276         case DoubleCond:
1277             return true;
1278         case ResCond:
1279             return MacroAssembler::isInvertible(asResultCondition());
1280         default:
1281             return false;
1282         }
1283     }
1284
1285     // This is valid for condition arguments. It will invert them.
1286     Arg inverted(bool inverted = true) const
1287     {
1288         if (!inverted)
1289             return *this;
1290         switch (kind()) {
1291         case RelCond:
1292             return relCond(MacroAssembler::invert(asRelationalCondition()));
1293         case ResCond:
1294             return resCond(MacroAssembler::invert(asResultCondition()));
1295         case DoubleCond:
1296             return doubleCond(MacroAssembler::invert(asDoubleCondition()));
1297         default:
1298             RELEASE_ASSERT_NOT_REACHED();
1299             return Arg();
1300         }
1301     }
1302
1303     Arg flipped(bool flipped = true) const
1304     {
1305         if (!flipped)
1306             return Arg();
1307         return relCond(MacroAssembler::flip(asRelationalCondition()));
1308     }
1309
1310     bool isSignedCond() const
1311     {
1312         return isRelCond() && MacroAssembler::isSigned(asRelationalCondition());
1313     }
1314
1315     bool isUnsignedCond() const
1316     {
1317         return isRelCond() && MacroAssembler::isUnsigned(asRelationalCondition());
1318     }
1319
1320     // This computes a hash for comparing this to JSAir's Arg.
1321     unsigned jsHash() const;
1322     
1323     void dump(PrintStream&) const;
1324
1325     Arg(WTF::HashTableDeletedValueType)
1326         : m_base(WTF::HashTableDeletedValue)
1327     {
1328     }
1329
1330     bool isHashTableDeletedValue() const
1331     {
1332         return *this == Arg(WTF::HashTableDeletedValue);
1333     }
1334
1335     unsigned hash() const
1336     {
1337         // This really doesn't have to be that great.
1338         return WTF::IntHash<int64_t>::hash(m_offset) + m_kind + m_scale + m_base.hash() +
1339             m_index.hash();
1340     }
1341
1342 private:
1343     int64_t m_offset { 0 };
1344     Kind m_kind { Invalid };
1345     int32_t m_scale { 1 };
1346     Air::Tmp m_base;
1347     Air::Tmp m_index;
1348 };
1349
1350 struct ArgHash {
1351     static unsigned hash(const Arg& key) { return key.hash(); }
1352     static bool equal(const Arg& a, const Arg& b) { return a == b; }
1353     static const bool safeToCompareToEmptyOrDeleted = true;
1354 };
1355
1356 } } } // namespace JSC::B3::Air
1357
1358 namespace WTF {
1359
1360 JS_EXPORT_PRIVATE void printInternal(PrintStream&, JSC::B3::Air::Arg::Kind);
1361 JS_EXPORT_PRIVATE void printInternal(PrintStream&, JSC::B3::Air::Arg::Role);
1362 JS_EXPORT_PRIVATE void printInternal(PrintStream&, JSC::B3::Air::Arg::Type);
1363 JS_EXPORT_PRIVATE void printInternal(PrintStream&, JSC::B3::Air::Arg::Width);
1364 JS_EXPORT_PRIVATE void printInternal(PrintStream&, JSC::B3::Air::Arg::Signedness);
1365
1366 template<typename T> struct DefaultHash;
1367 template<> struct DefaultHash<JSC::B3::Air::Arg> {
1368     typedef JSC::B3::Air::ArgHash Hash;
1369 };
1370
1371 template<typename T> struct HashTraits;
1372 template<> struct HashTraits<JSC::B3::Air::Arg> : SimpleClassHashTraits<JSC::B3::Air::Arg> {
1373     // Because m_scale is 1 in the empty value.
1374     static const bool emptyValueIsZero = false;
1375 };
1376
1377 } // namespace WTF
1378
1379 #if COMPILER(GCC) && ASSERT_DISABLED
1380 #pragma GCC diagnostic pop
1381 #endif // COMPILER(GCC) && ASSERT_DISABLED
1382
1383 #endif // ENABLE(B3_JIT)