1/*
2 * Copyright (C) 2016-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#include "BytecodeStructs.h"
29#include "InterpreterInlines.h"
30#include "Opcode.h"
31#include "PreciseJumpTargets.h"
32
33namespace JSC {
34
35#define SWITCH_JMP(CASE_OP, SWITCH_CASE, SWITCH_DEFAULT_OFFSET) \
36 switch (instruction->opcodeID()) { \
37 CASE_OP(OpJmp) \
38 \
39 CASE_OP(OpJtrue) \
40 CASE_OP(OpJfalse) \
41 CASE_OP(OpJeqNull) \
42 CASE_OP(OpJneqNull) \
43 CASE_OP(OpJneqPtr) \
44 \
45 CASE_OP(OpJless) \
46 CASE_OP(OpJlesseq) \
47 CASE_OP(OpJgreater) \
48 CASE_OP(OpJgreatereq) \
49 CASE_OP(OpJnless) \
50 CASE_OP(OpJnlesseq) \
51 CASE_OP(OpJngreater) \
52 CASE_OP(OpJngreatereq) \
53 CASE_OP(OpJeq) \
54 CASE_OP(OpJneq) \
55 CASE_OP(OpJstricteq) \
56 CASE_OP(OpJnstricteq) \
57 CASE_OP(OpJbelow) \
58 CASE_OP(OpJbeloweq) \
59 case op_switch_imm: { \
60 auto bytecode = instruction->as<OpSwitchImm>(); \
61 auto& table = codeBlock->switchJumpTable(bytecode.m_tableIndex); \
62 for (unsigned i = table.branchOffsets.size(); i--;) \
63 SWITCH_CASE(table.branchOffsets[i]); \
64 SWITCH_DEFAULT_OFFSET(OpSwitchImm); \
65 break; \
66 } \
67 case op_switch_char: { \
68 auto bytecode = instruction->as<OpSwitchChar>(); \
69 auto& table = codeBlock->switchJumpTable(bytecode.m_tableIndex); \
70 for (unsigned i = table.branchOffsets.size(); i--;) \
71 SWITCH_CASE(table.branchOffsets[i]); \
72 SWITCH_DEFAULT_OFFSET(OpSwitchChar); \
73 break; \
74 } \
75 case op_switch_string: { \
76 auto bytecode = instruction->as<OpSwitchString>(); \
77 auto& table = codeBlock->stringSwitchJumpTable(bytecode.m_tableIndex); \
78 auto iter = table.offsetTable.begin(); \
79 auto end = table.offsetTable.end(); \
80 for (; iter != end; ++iter) \
81 SWITCH_CASE(iter->value.branchOffset); \
82 SWITCH_DEFAULT_OFFSET(OpSwitchString); \
83 break; \
84 } \
85 default: \
86 break; \
87 } \
88
89
90template<typename Block>
91inline int jumpTargetForInstruction(Block* codeBlock, const InstructionStream::Ref& instruction, unsigned target)
92{
93 if (target)
94 return target;
95 return codeBlock->outOfLineJumpOffset(instruction);
96}
97
98template<typename HashMap>
99inline int jumpTargetForInstruction(HashMap& outOfLineJumpTargets, const InstructionStream::Ref& instruction, unsigned target)
100{
101 if (target)
102 return target;
103 ASSERT(outOfLineJumpTargets.contains(instruction.offset()));
104 return outOfLineJumpTargets.get(instruction.offset());
105}
106
107template<typename Op, typename Block>
108inline int jumpTargetForInstruction(Block&& codeBlock, const InstructionStream::Ref& instruction)
109{
110 auto bytecode = instruction->as<Op>();
111 return jumpTargetForInstruction(codeBlock, instruction, bytecode.m_targetLabel);
112}
113
114template<typename Block, typename Function>
115inline void extractStoredJumpTargetsForInstruction(Block&& codeBlock, const InstructionStream::Ref& instruction, const Function& function)
116{
117#define CASE_OP(__op) \
118 case __op::opcodeID: \
119 function(jumpTargetForInstruction<__op>(codeBlock, instruction)); \
120 break;
121
122#define SWITCH_CASE(__target) \
123 function(__target)
124
125#define SWITCH_DEFAULT_OFFSET(__op) \
126 function(jumpTargetForInstruction(codeBlock, instruction, bytecode.m_defaultOffset)) \
127
128SWITCH_JMP(CASE_OP, SWITCH_CASE, SWITCH_DEFAULT_OFFSET)
129
130#undef CASE_OP
131#undef SWITCH_CASE
132#undef SWITCH_DEFAULT_OFFSET
133}
134
135template<typename Block, typename Function, typename CodeBlockOrHashMap>
136inline void updateStoredJumpTargetsForInstruction(Block&& codeBlock, unsigned finalOffset, InstructionStream::MutableRef instruction, const Function& function, CodeBlockOrHashMap& codeBlockOrHashMap)
137{
138#define CASE_OP(__op) \
139 case __op::opcodeID: { \
140 int32_t target = jumpTargetForInstruction<__op>(codeBlockOrHashMap, instruction); \
141 int32_t newTarget = function(target); \
142 instruction->cast<__op>()->setTargetLabel(BoundLabel(newTarget), [&]() { \
143 codeBlock->addOutOfLineJumpTarget(finalOffset + instruction.offset(), newTarget); \
144 return BoundLabel(); \
145 }); \
146 break; \
147 }
148
149#define SWITCH_CASE(__target) \
150 do { \
151 int32_t target = __target; \
152 __target = function(target); \
153 } while (false)
154
155#define SWITCH_DEFAULT_OFFSET(__op) \
156 do { \
157 int32_t target = jumpTargetForInstruction(codeBlockOrHashMap, instruction, bytecode.m_defaultOffset); \
158 int32_t newTarget = function(target); \
159 instruction->cast<__op>()->setDefaultOffset(BoundLabel(newTarget), [&]() { \
160 codeBlock->addOutOfLineJumpTarget(finalOffset + instruction.offset(), newTarget); \
161 return BoundLabel(); \
162 }); \
163 } while (false)
164
165SWITCH_JMP(CASE_OP, SWITCH_CASE, SWITCH_DEFAULT_OFFSET)
166
167#undef CASE_OP
168#undef JMP_TARGET
169}
170
171template<typename Block, typename Function>
172inline void updateStoredJumpTargetsForInstruction(Block* codeBlock, unsigned finalOffset, InstructionStream::MutableRef instruction, Function function)
173{
174 updateStoredJumpTargetsForInstruction(codeBlock, finalOffset, instruction, function, codeBlock);
175}
176
177} // namespace JSC
178