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