1/*
2 * Copyright (C) 2015-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#pragma once
27
28#if ENABLE(B3_JIT)
29
30#include "AirArg.h"
31#include "AirKind.h"
32#include "B3StackmapSpecial.h"
33#include <wtf/HashMap.h>
34
35namespace JSC { namespace B3 {
36
37namespace Air {
38struct Inst;
39}
40
41// We want to lower Check instructions to a branch, but then we want to route that branch to our
42// out-of-line code instead of doing anything else. For this reason, a CheckSpecial will remember
43// which branch opcode we have selected along with the number of args in the overload we want. It
44// will create an Inst with that opcode plus the appropriate args from the owning Inst whenever you
45// call any of the callbacks.
46//
47// Note that for CheckAdd, CheckSub, and CheckMul we expect that the B3 arguments are the reverse
48// of the Air arguments (Add(a, b) => Add32 b, a). Except:
49// - CheckSub(0, x), which turns into BranchNeg32 x.
50// - CheckMul(a, b), which turns into Mul32 b, a but we pass Any for a's ValueRep.
51
52class CheckSpecial : public StackmapSpecial {
53public:
54 // Support for hash consing these things.
55 class Key {
56 public:
57 Key()
58 : m_stackmapRole(SameAsRep)
59 , m_numArgs(0)
60 {
61 }
62
63 Key(Air::Kind kind, unsigned numArgs, RoleMode stackmapRole = SameAsRep)
64 : m_kind(kind)
65 , m_stackmapRole(stackmapRole)
66 , m_numArgs(numArgs)
67 {
68 }
69
70 explicit Key(const Air::Inst&);
71
72 bool operator==(const Key& other) const
73 {
74 return m_kind == other.m_kind
75 && m_numArgs == other.m_numArgs
76 && m_stackmapRole == other.m_stackmapRole;
77 }
78
79 bool operator!=(const Key& other) const
80 {
81 return !(*this == other);
82 }
83
84 explicit operator bool() const { return *this != Key(); }
85
86 Air::Kind kind() const { return m_kind; }
87 unsigned numArgs() const { return m_numArgs; }
88 RoleMode stackmapRole() const { return m_stackmapRole; }
89
90 void dump(PrintStream& out) const;
91
92 Key(WTF::HashTableDeletedValueType)
93 : m_stackmapRole(SameAsRep)
94 , m_numArgs(1)
95 {
96 }
97
98 bool isHashTableDeletedValue() const
99 {
100 return *this == Key(WTF::HashTableDeletedValue);
101 }
102
103 unsigned hash() const
104 {
105 // Seriously, we don't need to be smart here. It just doesn't matter.
106 return m_kind.hash() + m_numArgs + m_stackmapRole;
107 }
108
109 private:
110 Air::Kind m_kind;
111 RoleMode m_stackmapRole;
112 unsigned m_numArgs;
113 };
114
115 CheckSpecial(Air::Kind, unsigned numArgs, RoleMode stackmapRole = SameAsRep);
116 CheckSpecial(const Key&);
117 ~CheckSpecial();
118
119protected:
120 // Constructs and returns the Inst representing the branch that this will use.
121 Air::Inst hiddenBranch(const Air::Inst&) const;
122
123 void forEachArg(Air::Inst&, const ScopedLambda<Air::Inst::EachArgCallback>&) final;
124 bool isValid(Air::Inst&) final;
125 bool admitsStack(Air::Inst&, unsigned argIndex) final;
126 bool admitsExtendedOffsetAddr(Air::Inst&, unsigned) final;
127 Optional<unsigned> shouldTryAliasingDef(Air::Inst&) final;
128
129 // NOTE: the generate method will generate the hidden branch and then register a LatePath that
130 // generates the stackmap. Super crazy dude!
131
132 CCallHelpers::Jump generate(Air::Inst&, CCallHelpers&, Air::GenerationContext&) final;
133
134 void dumpImpl(PrintStream&) const final;
135 void deepDumpImpl(PrintStream&) const final;
136
137private:
138 Air::Kind m_checkKind;
139 RoleMode m_stackmapRole;
140 unsigned m_numCheckArgs;
141};
142
143struct CheckSpecialKeyHash {
144 static unsigned hash(const CheckSpecial::Key& key) { return key.hash(); }
145 static bool equal(const CheckSpecial::Key& a, const CheckSpecial::Key& b) { return a == b; }
146 static constexpr bool safeToCompareToEmptyOrDeleted = true;
147};
148
149} } // namespace JSC::B3
150
151namespace WTF {
152
153template<typename T> struct DefaultHash;
154template<> struct DefaultHash<JSC::B3::CheckSpecial::Key> {
155 typedef JSC::B3::CheckSpecialKeyHash Hash;
156};
157
158template<typename T> struct HashTraits;
159template<> struct HashTraits<JSC::B3::CheckSpecial::Key> : SimpleClassHashTraits<JSC::B3::CheckSpecial::Key> {
160 // I don't want to think about this very hard, it's not worth it. I'm a be conservative.
161 static constexpr bool emptyValueIsZero = false;
162};
163
164} // namespace WTF
165
166#endif // ENABLE(B3_JIT)
167