IfEmitter.h (9701B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef frontend_IfEmitter_h 8 #define frontend_IfEmitter_h 9 10 #include "mozilla/Attributes.h" 11 #include "mozilla/Maybe.h" 12 13 #include <stdint.h> 14 15 #include "frontend/JumpList.h" 16 #include "frontend/TDZCheckCache.h" 17 18 namespace js { 19 namespace frontend { 20 21 struct BytecodeEmitter; 22 23 class MOZ_STACK_CLASS BranchEmitterBase { 24 public: 25 // Whether the then-clause, the else-clause, or else-if condition may 26 // contain declaration or access to lexical variables, which means they 27 // should have their own TDZCheckCache. Basically TDZCheckCache should be 28 // created for each basic block, which then-clause, else-clause, and 29 // else-if condition are, but for internally used branches which are 30 // known not to touch lexical variables we can skip creating TDZCheckCache 31 // for them. 32 // 33 // See the comment for TDZCheckCache class for more details. 34 enum class LexicalKind { 35 // For syntactic branches (if, if-else, and conditional expression), 36 // which basically may contain declaration or accesses to lexical 37 // variables inside then-clause, else-clause, and else-if condition. 38 MayContainLexicalAccessInBranch, 39 40 // For internally used branches which don't touch lexical variables 41 // inside then-clause, else-clause, nor else-if condition. 42 NoLexicalAccessInBranch 43 }; 44 45 protected: 46 BytecodeEmitter* bce_; 47 48 // Jump around the then clause, to the beginning of the else clause. 49 JumpList jumpAroundThen_; 50 51 // Jump around the else clause, to the end of the entire branch. 52 JumpList jumpsAroundElse_; 53 54 // The stack depth before emitting the then block. 55 // Used for restoring stack depth before emitting the else block. 56 // Also used for assertion to make sure then and else blocks pushed the 57 // same number of values. 58 int32_t thenDepth_ = 0; 59 60 enum class ConditionKind { Positive, Negative }; 61 LexicalKind lexicalKind_; 62 63 mozilla::Maybe<TDZCheckCache> tdzCache_; 64 65 #ifdef DEBUG 66 // The number of values pushed in the then and else blocks. 67 int32_t pushed_ = 0; 68 bool calculatedPushed_ = false; 69 #endif 70 71 protected: 72 BranchEmitterBase(BytecodeEmitter* bce, LexicalKind lexicalKind); 73 74 [[nodiscard]] bool emitThenInternal(ConditionKind conditionKind); 75 void calculateOrCheckPushed(); 76 [[nodiscard]] bool emitElseInternal(); 77 [[nodiscard]] bool emitEndInternal(); 78 79 public: 80 #ifdef DEBUG 81 // Returns the number of values pushed onto the value stack inside 82 // `then_block` and `else_block`. 83 // Can be used in assertion after emitting if-then-else. 84 int32_t pushed() const { return pushed_; } 85 86 // Returns the number of values popped onto the value stack inside 87 // `then_block` and `else_block`. 88 // Can be used in assertion after emitting if-then-else. 89 int32_t popped() const { return -pushed_; } 90 #endif 91 }; 92 93 // Class for emitting bytecode for blocks like if-then-else. 94 // 95 // This class can be used to emit single if-then-else block, or cascading 96 // else-if blocks. 97 // 98 // Usage: (check for the return value is omitted for simplicity) 99 // 100 // `if (cond) then_block` 101 // IfEmitter ifThen(this); 102 // ifThen.emitIf(Some(offset_of_if)); 103 // emit(cond); 104 // ifThen.emitThen(); 105 // emit(then_block); 106 // ifThen.emitEnd(); 107 // 108 // `if (!cond) then_block` 109 // IfEmitter ifThen(this); 110 // ifThen.emitIf(Some(offset_of_if)); 111 // emit(cond); 112 // ifThen.emitThen(IfEmitter::ConditionKind::Negative); 113 // emit(then_block); 114 // ifThen.emitEnd(); 115 // 116 // `if (cond) then_block else else_block` 117 // IfEmitter ifThenElse(this); 118 // ifThen.emitIf(Some(offset_of_if)); 119 // emit(cond); 120 // ifThenElse.emitThenElse(); 121 // emit(then_block); 122 // ifThenElse.emitElse(); 123 // emit(else_block); 124 // ifThenElse.emitEnd(); 125 // 126 // `if (c1) b1 else if (c2) b2 else if (c3) b3 else b4` 127 // IfEmitter ifThenElse(this); 128 // ifThen.emitIf(Some(offset_of_if)); 129 // emit(c1); 130 // ifThenElse.emitThenElse(); 131 // emit(b1); 132 // ifThenElse.emitElseIf(Some(offset_of_if)); 133 // emit(c2); 134 // ifThenElse.emitThenElse(); 135 // emit(b2); 136 // ifThenElse.emitElseIf(Some(offset_of_if)); 137 // emit(c3); 138 // ifThenElse.emitThenElse(); 139 // emit(b3); 140 // ifThenElse.emitElse(); 141 // emit(b4); 142 // ifThenElse.emitEnd(); 143 // 144 class MOZ_STACK_CLASS IfEmitter : public BranchEmitterBase { 145 public: 146 using ConditionKind = BranchEmitterBase::ConditionKind; 147 148 protected: 149 #ifdef DEBUG 150 // The state of this emitter. 151 // 152 // +-------+ emitIf +----+ 153 // | Start |------->| If |-+ 154 // +-------+ +----+ | 155 // | 156 // +--------------------+ 157 // | 158 // v emitThen +------+ emitEnd +-----+ 159 // +->+--------->| Then |---------------------------->+-------->| End | 160 // ^ | +------+ ^ +-----+ 161 // | | | 162 // | | | 163 // | | | 164 // | | emitThenElse +----------+ emitElse +------+ | 165 // | +------------->| ThenElse |-+--------->| Else |-+ 166 // | +----------+ | +------+ 167 // | | 168 // | | emitElseIf +--------+ 169 // | +----------->| ElseIf |-+ 170 // | +--------+ | 171 // | | 172 // +------------------------------------------------------+ 173 enum class State { 174 // The initial state. 175 Start, 176 177 // After calling emitIf. 178 If, 179 180 // After calling emitThen. 181 Then, 182 183 // After calling emitThenElse. 184 ThenElse, 185 186 // After calling emitElse. 187 Else, 188 189 // After calling emitElseIf. 190 ElseIf, 191 192 // After calling emitEnd. 193 End 194 }; 195 State state_ = State::Start; 196 #endif 197 198 protected: 199 // For InternalIfEmitter. 200 IfEmitter(BytecodeEmitter* bce, LexicalKind lexicalKind); 201 202 public: 203 explicit IfEmitter(BytecodeEmitter* bce); 204 205 // `ifPos` is the offset in the source code for the character below: 206 // 207 // if ( cond ) { ... } else if ( cond2 ) { ... } 208 // ^ ^ 209 // | | 210 // | ifPos for emitElseIf 211 // | 212 // ifPos for emitIf 213 // 214 // Can be Nothing() if not available. 215 [[nodiscard]] bool emitIf(const mozilla::Maybe<uint32_t>& ifPos); 216 217 [[nodiscard]] bool emitThen( 218 ConditionKind conditionKind = ConditionKind::Positive); 219 [[nodiscard]] bool emitThenElse( 220 ConditionKind conditionKind = ConditionKind::Positive); 221 222 [[nodiscard]] bool emitElseIf(const mozilla::Maybe<uint32_t>& ifPos); 223 [[nodiscard]] bool emitElse(); 224 225 [[nodiscard]] bool emitEnd(); 226 }; 227 228 // Class for emitting bytecode for blocks like if-then-else which doesn't touch 229 // lexical variables. 230 // 231 // See the comments above NoLexicalAccessInBranch for more details when to use 232 // this instead of IfEmitter. 233 // Compared to IfEmitter, this class doesn't have emitIf method, given that 234 // it doesn't have syntactic `if`, and also the `cond` value can be already 235 // on the stack. 236 // 237 // Usage: (check for the return value is omitted for simplicity) 238 // 239 // `if (cond) then_block else else_block` (effectively) 240 // emit(cond); 241 // InternalIfEmitter ifThenElse(this); 242 // ifThenElse.emitThenElse(); 243 // emit(then_block); 244 // ifThenElse.emitElse(); 245 // emit(else_block); 246 // ifThenElse.emitEnd(); 247 // 248 class MOZ_STACK_CLASS InternalIfEmitter : public IfEmitter { 249 public: 250 explicit InternalIfEmitter( 251 BytecodeEmitter* bce, 252 LexicalKind lexicalKind = 253 BranchEmitterBase::LexicalKind::NoLexicalAccessInBranch); 254 }; 255 256 // Class for emitting bytecode for conditional expression. 257 // 258 // Usage: (check for the return value is omitted for simplicity) 259 // 260 // `cond ? then_expr : else_expr` 261 // CondEmitter condElse(this); 262 // condElse.emitCond(); 263 // emit(cond); 264 // condElse.emitThenElse(); 265 // emit(then_expr); 266 // condElse.emitElse(); 267 // emit(else_expr); 268 // condElse.emitEnd(); 269 // 270 class MOZ_STACK_CLASS CondEmitter : public BranchEmitterBase { 271 #ifdef DEBUG 272 // The state of this emitter. 273 // 274 // +-------+ emitCond +------+ emitThenElse +----------+ 275 // | Start |--------->| Cond |------------->| ThenElse |-+ 276 // +-------+ +------+ +----------+ | 277 // | 278 // +-----------------+ 279 // | 280 // | emitElse +------+ emitEnd +-----+ 281 // +--------->| Else |-------->| End | 282 // +------+ +-----+ 283 enum class State { 284 // The initial state. 285 Start, 286 287 // After calling emitCond. 288 Cond, 289 290 // After calling emitThenElse. 291 ThenElse, 292 293 // After calling emitElse. 294 Else, 295 296 // After calling emitEnd. 297 End 298 }; 299 State state_ = State::Start; 300 #endif 301 302 public: 303 explicit CondEmitter(BytecodeEmitter* bce); 304 305 [[nodiscard]] bool emitCond(); 306 [[nodiscard]] bool emitThenElse( 307 ConditionKind conditionKind = ConditionKind::Positive); 308 [[nodiscard]] bool emitElse(); 309 [[nodiscard]] bool emitEnd(); 310 }; 311 312 } /* namespace frontend */ 313 } /* namespace js */ 314 315 #endif /* frontend_IfEmitter_h */