| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2017 The Qt Company Ltd. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the QtQml module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU Lesser General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the | 
| 21 | ** packaging of this file. Please review the following information to | 
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements | 
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. | 
| 24 | ** | 
| 25 | ** GNU General Public License Usage | 
| 26 | ** Alternatively, this file may be used under the terms of the GNU | 
| 27 | ** General Public License version 2.0 or (at your option) the GNU General | 
| 28 | ** Public license version 3 or any later version approved by the KDE Free | 
| 29 | ** Qt Foundation. The licenses are as published by the Free Software | 
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 | 
| 31 | ** included in the packaging of this file. Please review the following | 
| 32 | ** information to ensure the GNU General Public License requirements will | 
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and | 
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. | 
| 35 | ** | 
| 36 | ** $QT_END_LICENSE$ | 
| 37 | ** | 
| 38 | ****************************************************************************/ | 
| 39 |  | 
| 40 | #include <QBuffer> | 
| 41 | #include <QFile> | 
| 42 |  | 
| 43 | #include "qv4engine_p.h" | 
| 44 | #include "qv4baselineassembler_p.h" | 
| 45 | #include "qv4assemblercommon_p.h" | 
| 46 | #include <private/qv4function_p.h> | 
| 47 | #include <private/qv4runtime_p.h> | 
| 48 | #include <private/qv4stackframe_p.h> | 
| 49 |  | 
| 50 | #include <wtf/Vector.h> | 
| 51 | #include <assembler/MacroAssembler.h> | 
| 52 | #include <assembler/MacroAssemblerCodeRef.h> | 
| 53 | #include <assembler/LinkBuffer.h> | 
| 54 | #include <WTFStubs.h> | 
| 55 |  | 
| 56 | #undef ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES | 
| 57 |  | 
| 58 | #if QT_CONFIG(qml_jit) | 
| 59 |  | 
| 60 | QT_BEGIN_NAMESPACE | 
| 61 | namespace QV4 { | 
| 62 | namespace JIT { | 
| 63 |  | 
| 64 | #define ASM_GENERATE_RUNTIME_CALL(function, destination) \ | 
| 65 |     pasm()->GENERATE_RUNTIME_CALL(function, destination) | 
| 66 | #define callHelper(x) \ | 
| 67 |     PlatformAssemblerCommon::callRuntimeUnchecked(reinterpret_cast<void *>(&x), #x) | 
| 68 |  | 
| 69 | const QV4::Value::ValueTypeInternal IntegerTag = QV4::Value::ValueTypeInternal::Integer; | 
| 70 |  | 
| 71 | static ReturnedValue toNumberHelper(ReturnedValue v) | 
| 72 | { | 
| 73 |     return Encode(Value::fromReturnedValue(val: v).toNumber()); | 
| 74 | } | 
| 75 |  | 
| 76 | static ReturnedValue toInt32Helper(ReturnedValue v) | 
| 77 | { | 
| 78 |     return Encode(Value::fromReturnedValue(val: v).toInt32()); | 
| 79 | } | 
| 80 |  | 
| 81 | #if QT_POINTER_SIZE == 8 || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES) | 
| 82 | class PlatformAssembler64 : public PlatformAssemblerCommon | 
| 83 | { | 
| 84 | public: | 
| 85 |     PlatformAssembler64(const Value *constantTable) | 
| 86 |         : PlatformAssemblerCommon(constantTable) | 
| 87 |     {} | 
| 88 |  | 
| 89 |     void callRuntime(const void *funcPtr, CallResultDestination dest) | 
| 90 |     { | 
| 91 |         PlatformAssemblerCommon::callRuntime(funcPtr); | 
| 92 |         if (dest == CallResultDestination::InAccumulator) | 
| 93 |             saveReturnValueInAccumulator(); | 
| 94 |         else if (AccumulatorRegister == ReturnValueRegister) | 
| 95 |             loadUndefined(); | 
| 96 |     } | 
| 97 |  | 
| 98 |     void saveReturnValueInAccumulator() | 
| 99 |     { | 
| 100 |         move(src: ReturnValueRegister, dest: AccumulatorRegister); | 
| 101 |     } | 
| 102 |  | 
| 103 |     void loadUndefined(RegisterID dest = AccumulatorRegister) | 
| 104 |     { | 
| 105 |         move(imm: TrustedImm64(0), dest); | 
| 106 |     } | 
| 107 |  | 
| 108 |     void copyConst(int constIndex, Address dest) | 
| 109 |     { | 
| 110 |         //### | 
| 111 |         if (constant(idx: constIndex).isUndefined()) { | 
| 112 |             loadUndefined(dest: ScratchRegister); | 
| 113 |         } else { | 
| 114 |             load64(address: loadConstAddress(constIndex, baseReg: ScratchRegister), dest: ScratchRegister); | 
| 115 |         } | 
| 116 |         store64(src: ScratchRegister, address: dest); | 
| 117 |     } | 
| 118 |  | 
| 119 |     void copyReg(Address src, Address dst) | 
| 120 |     { | 
| 121 |         load64(address: src, dest: ScratchRegister); | 
| 122 |         store64(src: ScratchRegister, address: dst); | 
| 123 |     } | 
| 124 |  | 
| 125 |     void loadPointerFromValue(Address addr, RegisterID dest = AccumulatorRegister) | 
| 126 |     { | 
| 127 |         load64(address: addr, dest); | 
| 128 |     } | 
| 129 |  | 
| 130 |     void loadAccumulator(Address addr) | 
| 131 |     { | 
| 132 |         load64(address: addr, dest: AccumulatorRegister); | 
| 133 |     } | 
| 134 |  | 
| 135 |     void storeAccumulator(Address addr) | 
| 136 |     { | 
| 137 |         store64(src: AccumulatorRegister, address: addr); | 
| 138 |     } | 
| 139 |  | 
| 140 |     void moveReg(Address sourceRegAddress, Address destRegAddress) | 
| 141 |     { | 
| 142 |         load64(address: sourceRegAddress, dest: ScratchRegister); | 
| 143 |         store64(src: ScratchRegister, address: destRegAddress); | 
| 144 |     } | 
| 145 |  | 
| 146 |     void loadString(int stringId) | 
| 147 |     { | 
| 148 |         loadAccumulator(addr: loadStringAddress(stringId)); | 
| 149 |     } | 
| 150 |  | 
| 151 |     void loadValue(ReturnedValue value) | 
| 152 |     { | 
| 153 |         move(imm: TrustedImm64(value), dest: AccumulatorRegister); | 
| 154 |     } | 
| 155 |  | 
| 156 |     void storeHeapObject(RegisterID source, Address addr) | 
| 157 |     { | 
| 158 |         store64(src: source, address: addr); | 
| 159 |     } | 
| 160 |  | 
| 161 |     void generateCatchTrampoline() | 
| 162 |     { | 
| 163 |         PlatformAssemblerCommon::generateCatchTrampoline(loadUndefined: [this](){loadUndefined();}); | 
| 164 |     } | 
| 165 |  | 
| 166 |     void jumpNotUndefined(int offset) | 
| 167 |     { | 
| 168 |         auto jump = branch64(cond: NotEqual, left: AccumulatorRegister, right: TrustedImm64(0)); | 
| 169 |         addJumpToOffset(jump, offset); | 
| 170 |     } | 
| 171 |  | 
| 172 |     Jump jumpEmpty() | 
| 173 |     { | 
| 174 |         return branch64(cond: Equal, left: AccumulatorRegister, right: TrustedImm64(Value::emptyValue().asReturnedValue())); | 
| 175 |     } | 
| 176 |  | 
| 177 |     Jump jumpNotEmpty() | 
| 178 |     { | 
| 179 |         return branch64(cond: NotEqual, left: AccumulatorRegister, right: TrustedImm64(Value::emptyValue().asReturnedValue())); | 
| 180 |     } | 
| 181 |  | 
| 182 |     void toBoolean(std::function<void(RegisterID)> continuation) | 
| 183 |     { | 
| 184 |         urshift64(src: AccumulatorRegister, imm: TrustedImm32(Value::IsIntegerConvertible_Shift), dest: ScratchRegister); | 
| 185 |         auto needsConversion = branch32(cond: NotEqual, left: TrustedImm32(1), right: ScratchRegister); | 
| 186 |         continuation(AccumulatorRegister); | 
| 187 |         Jump done = jump(); | 
| 188 |  | 
| 189 |         // slow path: | 
| 190 |         needsConversion.link(masm: this); | 
| 191 |         push(src: AccumulatorRegister); | 
| 192 |         move(src: AccumulatorRegister, dest: registerForArg(arg: 0)); | 
| 193 |         callHelper(Value::toBooleanImpl); | 
| 194 |         and32(imm: TrustedImm32(1), src: ReturnValueRegister, dest: ScratchRegister); | 
| 195 |         pop(dest: AccumulatorRegister); | 
| 196 |         continuation(ScratchRegister); | 
| 197 |  | 
| 198 |         done.link(masm: this); | 
| 199 |     } | 
| 200 |  | 
| 201 |     void toNumber() | 
| 202 |     { | 
| 203 |         urshift64(src: AccumulatorRegister, imm: TrustedImm32(Value::QuickType_Shift), dest: ScratchRegister); | 
| 204 |         auto isNumber = branch32(cond: GreaterThanOrEqual, left: ScratchRegister, right: TrustedImm32(Value::QT_Int)); | 
| 205 |  | 
| 206 |         move(src: AccumulatorRegister, dest: registerForArg(arg: 0)); | 
| 207 |         callHelper(toNumberHelper); | 
| 208 |         saveReturnValueInAccumulator(); | 
| 209 |  | 
| 210 |         isNumber.link(masm: this); | 
| 211 |     } | 
| 212 |  | 
| 213 |     // this converts both the lhs and the accumulator to int32 | 
| 214 |     void toInt32LhsAcc(Address lhs, RegisterID lhsTarget) | 
| 215 |     { | 
| 216 |         load64(address: lhs, dest: lhsTarget); | 
| 217 |         urshift64(src: lhsTarget, imm: TrustedImm32(Value::QuickType_Shift), dest: ScratchRegister2); | 
| 218 |         auto lhsIsInt = branch32(cond: Equal, left: TrustedImm32(Value::QT_Int), right: ScratchRegister2); | 
| 219 |  | 
| 220 |         const Address accumulatorStackAddress(JSStackFrameRegister, | 
| 221 |                                               offsetof(CallData, accumulator)); | 
| 222 |         storeAccumulator(addr: accumulatorStackAddress); | 
| 223 |         move(src: lhsTarget, dest: registerForArg(arg: 0)); | 
| 224 |         callHelper(toInt32Helper); | 
| 225 |         move(src: ReturnValueRegister, dest: lhsTarget); | 
| 226 |         loadAccumulator(addr: accumulatorStackAddress); | 
| 227 |  | 
| 228 |         lhsIsInt.link(masm: this); | 
| 229 |         urshift64(src: AccumulatorRegister, imm: TrustedImm32(Value::QuickType_Shift), dest: ScratchRegister2); | 
| 230 |         auto isInt = branch32(cond: Equal, left: TrustedImm32(Value::QT_Int), right: ScratchRegister2); | 
| 231 |  | 
| 232 |         pushAligned(reg: lhsTarget); | 
| 233 |         move(src: AccumulatorRegister, dest: registerForArg(arg: 0)); | 
| 234 |         callHelper(toInt32Helper); | 
| 235 |         saveReturnValueInAccumulator(); | 
| 236 |         popAligned(reg: lhsTarget); | 
| 237 |  | 
| 238 |         isInt.link(masm: this); | 
| 239 |     } | 
| 240 |  | 
| 241 |     void toInt32() | 
| 242 |     { | 
| 243 |         urshift64(src: AccumulatorRegister, imm: TrustedImm32(Value::QuickType_Shift), dest: ScratchRegister2); | 
| 244 |         auto isInt = branch32(cond: Equal, left: TrustedImm32(Value::QT_Int), right: ScratchRegister2); | 
| 245 |  | 
| 246 |         move(src: AccumulatorRegister, dest: registerForArg(arg: 0)); | 
| 247 |         callHelper(toInt32Helper); | 
| 248 |         saveReturnValueInAccumulator(); | 
| 249 |  | 
| 250 |         isInt.link(masm: this); | 
| 251 |     } | 
| 252 |  | 
| 253 |     void regToInt32(Address srcReg, RegisterID targetReg) | 
| 254 |     { | 
| 255 |         load64(address: srcReg, dest: targetReg); | 
| 256 |         urshift64(src: targetReg, imm: TrustedImm32(Value::QuickType_Shift), dest: ScratchRegister2); | 
| 257 |         auto isInt = branch32(cond: Equal, left: TrustedImm32(Value::QT_Int), right: ScratchRegister2); | 
| 258 |  | 
| 259 |         pushAligned(reg: AccumulatorRegister); | 
| 260 |         move(src: targetReg, dest: registerForArg(arg: 0)); | 
| 261 |         callHelper(toInt32Helper); | 
| 262 |         move(src: ReturnValueRegister, dest: targetReg); | 
| 263 |         popAligned(reg: AccumulatorRegister); | 
| 264 |  | 
| 265 |         isInt.link(masm: this); | 
| 266 |     } | 
| 267 |  | 
| 268 |     void isNullOrUndefined() | 
| 269 |     { | 
| 270 |         move(src: AccumulatorRegister, dest: ScratchRegister); | 
| 271 |         compare64(cond: Equal, left: ScratchRegister, right: TrustedImm32(0), dest: AccumulatorRegister); | 
| 272 |         Jump isUndef = branch32(cond: NotEqual, left: TrustedImm32(0), right: AccumulatorRegister); | 
| 273 |  | 
| 274 |         // not undefined | 
| 275 |         rshift64(imm: TrustedImm32(32), dest: ScratchRegister); | 
| 276 |         compare32(cond: Equal, left: ScratchRegister, right: TrustedImm32(int(QV4::Value::ValueTypeInternal::Null)), | 
| 277 |                   dest: AccumulatorRegister); | 
| 278 |  | 
| 279 |         isUndef.link(masm: this); | 
| 280 |     } | 
| 281 |  | 
| 282 |     Jump isIntOrBool() | 
| 283 |     { | 
| 284 |         urshift64(src: AccumulatorRegister, imm: TrustedImm32(Value::IsIntegerOrBool_Shift), dest: ScratchRegister); | 
| 285 |         return branch32(cond: Equal, left: TrustedImm32(3), right: ScratchRegister); | 
| 286 |     } | 
| 287 |  | 
| 288 |     void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset) | 
| 289 |     { | 
| 290 |         Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value))); | 
| 291 |         load64(address: lhsAddr, dest: ScratchRegister); | 
| 292 |         Jump isUndef = branch64(cond: Equal, left: ScratchRegister, right: TrustedImm64(0)); | 
| 293 |         Jump equal = branch32(cond: Equal, left: TrustedImm32(rhs), right: ScratchRegister); | 
| 294 |         addJumpToOffset(jump: equal, offset); | 
| 295 |         isUndef.link(masm: this); | 
| 296 |     } | 
| 297 |  | 
| 298 |     void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset) | 
| 299 |     { | 
| 300 |         Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value))); | 
| 301 |         load64(address: lhsAddr, dest: ScratchRegister); | 
| 302 |         Jump isUndef = branch64(cond: Equal, left: ScratchRegister, right: TrustedImm64(0)); | 
| 303 |         addJumpToOffset(jump: isUndef, offset); | 
| 304 |         Jump notEqual = branch32(cond: NotEqual, left: TrustedImm32(rhs), right: ScratchRegister); | 
| 305 |         addJumpToOffset(jump: notEqual, offset); | 
| 306 |     } | 
| 307 |  | 
| 308 |     void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister) | 
| 309 |     { | 
| 310 |         if (sourceReg == NoRegister) | 
| 311 |             or64(imm: TrustedImm64(int64_t(tag) << 32), dest: AccumulatorRegister); | 
| 312 |         else | 
| 313 |             or64(imm: TrustedImm64(int64_t(tag) << 32), src: sourceReg, dest: AccumulatorRegister); | 
| 314 |     } | 
| 315 |  | 
| 316 |     void encodeDoubleIntoAccumulator(FPRegisterID src) | 
| 317 |     { | 
| 318 |         moveDoubleTo64(src, dest: AccumulatorRegister); | 
| 319 |         move(imm: TrustedImm64(Value::NaNEncodeMask), dest: ScratchRegister); | 
| 320 |         xor64(src: ScratchRegister, dest: AccumulatorRegister); | 
| 321 |     } | 
| 322 |  | 
| 323 |     void pushValueAligned(ReturnedValue v) | 
| 324 |     { | 
| 325 |         loadValue(value: v); | 
| 326 |         pushAligned(reg: AccumulatorRegister); | 
| 327 |     } | 
| 328 |  | 
| 329 |     void popValueAligned() | 
| 330 |     { | 
| 331 |         addPtr(imm: TrustedImm32(2 * PointerSize), srcDest: StackPointerRegister); | 
| 332 |     } | 
| 333 |  | 
| 334 |     Jump binopBothIntPath(Address lhsAddr, std::function<Jump(void)> fastPath) | 
| 335 |     { | 
| 336 |         urshift64(src: AccumulatorRegister, imm: TrustedImm32(32), dest: ScratchRegister); | 
| 337 |         Jump accNotInt = branch32(cond: NotEqual, left: TrustedImm32(int(IntegerTag)), right: ScratchRegister); | 
| 338 |         load64(address: lhsAddr, dest: ScratchRegister); | 
| 339 |         urshift64(src: ScratchRegister, imm: TrustedImm32(32), dest: ScratchRegister2); | 
| 340 |         Jump lhsNotInt = branch32(cond: NotEqual, left: TrustedImm32(int(IntegerTag)), right: ScratchRegister2); | 
| 341 |  | 
| 342 |         // both integer | 
| 343 |         Jump failure = fastPath(); | 
| 344 |         Jump done = jump(); | 
| 345 |  | 
| 346 |         // all other cases | 
| 347 |         if (failure.isSet()) | 
| 348 |             failure.link(masm: this); | 
| 349 |         accNotInt.link(masm: this); | 
| 350 |         lhsNotInt.link(masm: this); | 
| 351 |  | 
| 352 |         return done; | 
| 353 |     } | 
| 354 |  | 
| 355 |     Jump unopIntPath(std::function<Jump(void)> fastPath) | 
| 356 |     { | 
| 357 |         urshift64(src: AccumulatorRegister, imm: TrustedImm32(Value::IsIntegerConvertible_Shift), dest: ScratchRegister); | 
| 358 |         Jump accNotIntConvertible = branch32(cond: NotEqual, left: TrustedImm32(1), right: ScratchRegister); | 
| 359 |  | 
| 360 |         // both integer | 
| 361 |         Jump failure = fastPath(); | 
| 362 |         Jump done = jump(); | 
| 363 |  | 
| 364 |         // all other cases | 
| 365 |         if (failure.isSet()) | 
| 366 |             failure.link(masm: this); | 
| 367 |         accNotIntConvertible.link(masm: this); | 
| 368 |  | 
| 369 |         return done; | 
| 370 |     } | 
| 371 |  | 
| 372 |     void callWithAccumulatorByValueAsFirstArgument(std::function<void()> doCall) | 
| 373 |     { | 
| 374 |         passAsArg(src: AccumulatorRegister, arg: 0); | 
| 375 |         doCall(); | 
| 376 |     } | 
| 377 | }; | 
| 378 |  | 
| 379 | typedef PlatformAssembler64 PlatformAssembler; | 
| 380 | #endif | 
| 381 |  | 
| 382 | #if QT_POINTER_SIZE == 4 || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES) | 
| 383 | class PlatformAssembler32 : public PlatformAssemblerCommon | 
| 384 | { | 
| 385 | public: | 
| 386 |     PlatformAssembler32(const Value *constantTable) | 
| 387 |         : PlatformAssemblerCommon(constantTable) | 
| 388 |     {} | 
| 389 |  | 
| 390 |     void callRuntime(const void *funcPtr, CallResultDestination dest) | 
| 391 |     { | 
| 392 |         PlatformAssemblerCommon::callRuntime(funcPtr); | 
| 393 |         if (dest == CallResultDestination::InAccumulator) | 
| 394 |             saveReturnValueInAccumulator(); | 
| 395 |         else if (AccumulatorRegisterValue == ReturnValueRegisterValue) | 
| 396 |             loadUndefined(); | 
| 397 |     } | 
| 398 |  | 
| 399 |     void saveReturnValueInAccumulator() | 
| 400 |     { | 
| 401 |         move(ReturnValueRegisterValue, AccumulatorRegisterValue); | 
| 402 |         move(ReturnValueRegisterTag, AccumulatorRegisterTag); | 
| 403 |     } | 
| 404 |  | 
| 405 |     void loadUndefined() | 
| 406 |     { | 
| 407 |         move(TrustedImm32(0), AccumulatorRegisterValue); | 
| 408 |         move(TrustedImm32(0), AccumulatorRegisterTag); | 
| 409 |     } | 
| 410 |  | 
| 411 |     void copyConst(int constIndex, Address destRegAddr) | 
| 412 |     { | 
| 413 |         //### | 
| 414 |         if (constant(constIndex).isUndefined()) { | 
| 415 |             move(TrustedImm32(0), ScratchRegister); | 
| 416 |             store32(ScratchRegister, destRegAddr); | 
| 417 |             destRegAddr.offset += 4; | 
| 418 |             store32(ScratchRegister, destRegAddr); | 
| 419 |         } else { | 
| 420 |             Address src = loadConstAddress(constIndex); | 
| 421 |             loadDouble(src, FPScratchRegister); | 
| 422 |             storeDouble(FPScratchRegister, destRegAddr); | 
| 423 |         } | 
| 424 |     } | 
| 425 |  | 
| 426 |     void copyReg(Address src, Address dest) | 
| 427 |     { | 
| 428 |         loadDouble(src, FPScratchRegister); | 
| 429 |         storeDouble(FPScratchRegister, dest); | 
| 430 |     } | 
| 431 |  | 
| 432 |     void loadPointerFromValue(Address addr, RegisterID dest = AccumulatorRegisterValue) | 
| 433 |     { | 
| 434 |         load32(addr, dest); | 
| 435 |     } | 
| 436 |  | 
| 437 |     void loadAccumulator(Address src) | 
| 438 |     { | 
| 439 |         load32(src, AccumulatorRegisterValue); | 
| 440 |         src.offset += 4; | 
| 441 |         load32(src, AccumulatorRegisterTag); | 
| 442 |     } | 
| 443 |  | 
| 444 |     void storeAccumulator(Address addr) | 
| 445 |     { | 
| 446 |         store32(AccumulatorRegisterValue, addr); | 
| 447 |         addr.offset += 4; | 
| 448 |         store32(AccumulatorRegisterTag, addr); | 
| 449 |     } | 
| 450 |  | 
| 451 |     void moveReg(Address sourceRegAddress, Address destRegAddress) | 
| 452 |     { | 
| 453 |         load32(sourceRegAddress, ScratchRegister); | 
| 454 |         store32(ScratchRegister, destRegAddress); | 
| 455 |         sourceRegAddress.offset += 4; | 
| 456 |         destRegAddress.offset += 4; | 
| 457 |         load32(sourceRegAddress, ScratchRegister); | 
| 458 |         store32(ScratchRegister, destRegAddress); | 
| 459 |     } | 
| 460 |  | 
| 461 |     void loadString(int stringId) | 
| 462 |     { | 
| 463 |         load32(loadStringAddress(stringId), AccumulatorRegisterValue); | 
| 464 |         move(TrustedImm32(0), AccumulatorRegisterTag); | 
| 465 |     } | 
| 466 |  | 
| 467 |     void loadValue(ReturnedValue value) | 
| 468 |     { | 
| 469 |         move(TrustedImm32(Value::fromReturnedValue(value).value()), AccumulatorRegisterValue); | 
| 470 |         move(TrustedImm32(Value::fromReturnedValue(value).tag()), AccumulatorRegisterTag); | 
| 471 |     } | 
| 472 |  | 
| 473 |     void storeHeapObject(RegisterID source, Address addr) | 
| 474 |     { | 
| 475 |         store32(source, addr); | 
| 476 |         addr.offset += 4; | 
| 477 |         store32(TrustedImm32(0), addr); | 
| 478 |     } | 
| 479 |  | 
| 480 |  | 
| 481 |     void generateCatchTrampoline() | 
| 482 |     { | 
| 483 |         PlatformAssemblerCommon::generateCatchTrampoline([this](){loadUndefined();}); | 
| 484 |     } | 
| 485 |  | 
| 486 |     void toNumber() | 
| 487 |     { | 
| 488 |         urshift32(AccumulatorRegisterTag, TrustedImm32(Value::QuickType_Shift - 32), ScratchRegister); | 
| 489 |         auto isNumber = branch32(GreaterThanOrEqual, ScratchRegister, TrustedImm32(Value::QT_Int)); | 
| 490 |  | 
| 491 |         if (ArgInRegCount < 2) { | 
| 492 |             subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); // stack alignment | 
| 493 |             push(AccumulatorRegisterTag); | 
| 494 |             push(AccumulatorRegisterValue); | 
| 495 |         } else { | 
| 496 |             move(AccumulatorRegisterValue, registerForArg(0)); | 
| 497 |             move(AccumulatorRegisterTag, registerForArg(1)); | 
| 498 |         } | 
| 499 |         callHelper(toNumberHelper); | 
| 500 |         saveReturnValueInAccumulator(); | 
| 501 |         if (ArgInRegCount < 2) | 
| 502 |             addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister); | 
| 503 |  | 
| 504 |         isNumber.link(this); | 
| 505 |     } | 
| 506 |  | 
| 507 |     // this converts both the lhs and the accumulator to int32 | 
| 508 |     void toInt32LhsAcc(Address lhs, RegisterID lhsTarget) | 
| 509 |     { | 
| 510 |         bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue | 
| 511 |                 || AccumulatorRegisterTag == ReturnValueRegisterTag; | 
| 512 |         lhs.offset += 4; | 
| 513 |         load32(lhs, lhsTarget); | 
| 514 |         lhs.offset -= 4; | 
| 515 |         auto lhsIsNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), lhsTarget); | 
| 516 |         load32(lhs, lhsTarget); | 
| 517 |         auto lhsIsInt = jump(); | 
| 518 |  | 
| 519 |         lhsIsNotInt.link(this); | 
| 520 |  | 
| 521 |         // Save accumulator from being garbage collected, no matter if we will reuse the register. | 
| 522 |         const Address accumulatorStackAddress(JSStackFrameRegister, | 
| 523 |                                               offsetof(CallData, accumulator)); | 
| 524 |         storeAccumulator(accumulatorStackAddress); | 
| 525 |  | 
| 526 |         if (ArgInRegCount < 2) { | 
| 527 |             subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); | 
| 528 |             push(lhsTarget); | 
| 529 |             load32(lhs, lhsTarget); | 
| 530 |             push(lhsTarget); | 
| 531 |         } else { | 
| 532 |             move(lhsTarget, registerForArg(1)); | 
| 533 |             load32(lhs, registerForArg(0)); | 
| 534 |         } | 
| 535 |         callHelper(toInt32Helper); | 
| 536 |         move(ReturnValueRegisterValue, lhsTarget); | 
| 537 |         if (ArgInRegCount < 2) | 
| 538 |             addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister); | 
| 539 |  | 
| 540 |         if (accumulatorNeedsSaving) // otherwise it's still the same | 
| 541 |             loadAccumulator(accumulatorStackAddress); | 
| 542 |  | 
| 543 |         lhsIsInt.link(this); | 
| 544 |  | 
| 545 |         auto rhsIsInt = branch32(Equal, TrustedImm32(int(IntegerTag)), AccumulatorRegisterTag); | 
| 546 |  | 
| 547 |         pushAligned(lhsTarget); | 
| 548 |         if (ArgInRegCount < 2) { | 
| 549 |             subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); | 
| 550 |             push(AccumulatorRegisterTag); | 
| 551 |             push(AccumulatorRegisterValue); | 
| 552 |         } else { | 
| 553 |             move(AccumulatorRegisterValue, registerForArg(0)); | 
| 554 |             move(AccumulatorRegisterTag, registerForArg(1)); | 
| 555 |         } | 
| 556 |         callHelper(toInt32Helper); | 
| 557 |         saveReturnValueInAccumulator(); | 
| 558 |         if (ArgInRegCount < 2) | 
| 559 |             addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister); | 
| 560 |         popAligned(lhsTarget); | 
| 561 |  | 
| 562 |         rhsIsInt.link(this); | 
| 563 |     } | 
| 564 |  | 
| 565 |     void toInt32() | 
| 566 |     { | 
| 567 |         urshift32(AccumulatorRegisterTag, TrustedImm32(Value::QuickType_Shift - 32), ScratchRegister); | 
| 568 |         auto isInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister); | 
| 569 |  | 
| 570 |         if (ArgInRegCount < 2) { | 
| 571 |             subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); // align the stack on a 16-byte boundary | 
| 572 |             push(AccumulatorRegisterTag); | 
| 573 |             push(AccumulatorRegisterValue); | 
| 574 |         } else { | 
| 575 |             move(AccumulatorRegisterValue, registerForArg(0)); | 
| 576 |             move(AccumulatorRegisterTag, registerForArg(1)); | 
| 577 |         } | 
| 578 |         callHelper(toInt32Helper); | 
| 579 |         saveReturnValueInAccumulator(); | 
| 580 |         if (ArgInRegCount < 2) | 
| 581 |             addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister); | 
| 582 |  | 
| 583 |         isInt.link(this); | 
| 584 |     } | 
| 585 |  | 
| 586 |     void regToInt32(Address srcReg, RegisterID targetReg) | 
| 587 |     { | 
| 588 |         bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue | 
| 589 |                 || AccumulatorRegisterTag == ReturnValueRegisterTag; | 
| 590 |         if (accumulatorNeedsSaving) { | 
| 591 |             push(AccumulatorRegisterTag); | 
| 592 |             push(AccumulatorRegisterValue); | 
| 593 |         } | 
| 594 |         if (ArgInRegCount < 2) { | 
| 595 |             if (!accumulatorNeedsSaving) | 
| 596 |                 subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); | 
| 597 |             srcReg.offset += 4; | 
| 598 |             load32(srcReg, targetReg); | 
| 599 |             push(targetReg); | 
| 600 |             srcReg.offset -= 4; | 
| 601 |             load32(srcReg, targetReg); | 
| 602 |             push(targetReg); | 
| 603 |         } else { | 
| 604 |             if (accumulatorNeedsSaving) | 
| 605 |                 subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); | 
| 606 |             load32(srcReg, registerForArg(0)); | 
| 607 |             srcReg.offset += 4; | 
| 608 |             load32(srcReg, registerForArg(1)); | 
| 609 |         } | 
| 610 |         callHelper(toInt32Helper); | 
| 611 |         move(ReturnValueRegisterValue, targetReg); | 
| 612 |         if (accumulatorNeedsSaving) { | 
| 613 |             addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); | 
| 614 |             pop(AccumulatorRegisterValue); | 
| 615 |             pop(AccumulatorRegisterTag); | 
| 616 |         } else if (ArgInRegCount < 2) { | 
| 617 |             addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister); | 
| 618 |         } | 
| 619 |     } | 
| 620 |  | 
| 621 |     void isNullOrUndefined() | 
| 622 |     { | 
| 623 |         Jump notUndefOrPtr = branch32(NotEqual, TrustedImm32(0), AccumulatorRegisterTag); | 
| 624 |         compare32(Equal, AccumulatorRegisterValue, TrustedImm32(0), AccumulatorRegisterValue); | 
| 625 |         auto done = jump(); | 
| 626 |  | 
| 627 |         // not undefined or managed | 
| 628 |         notUndefOrPtr.link(this); | 
| 629 |         compare32(Equal, AccumulatorRegisterTag, TrustedImm32(int(QV4::Value::ValueTypeInternal::Null)), | 
| 630 |                   AccumulatorRegisterValue); | 
| 631 |  | 
| 632 |         done.link(this); | 
| 633 |     } | 
| 634 |  | 
| 635 |     Jump isIntOrBool() | 
| 636 |     { | 
| 637 |         urshift32(AccumulatorRegisterTag, TrustedImm32(Value::IsIntegerOrBool_Shift - 32), ScratchRegister); | 
| 638 |         return branch32(Equal, TrustedImm32(3), ScratchRegister); | 
| 639 |     } | 
| 640 |  | 
| 641 |     void pushValue(ReturnedValue v) | 
| 642 |     { | 
| 643 |         push(TrustedImm32(v >> 32)); | 
| 644 |         push(TrustedImm32(v)); | 
| 645 |     } | 
| 646 |  | 
| 647 |     void jumpNotUndefined(int offset) | 
| 648 |     { | 
| 649 |         move(AccumulatorRegisterTag, ScratchRegister); | 
| 650 |         or32(AccumulatorRegisterValue, ScratchRegister); | 
| 651 |         auto jump = branch32(NotEqual, ScratchRegister, TrustedImm32(0)); | 
| 652 |         addJumpToOffset(jump, offset); | 
| 653 |     } | 
| 654 |  | 
| 655 |     Jump jumpEmpty() | 
| 656 |     { | 
| 657 |         return branch32(Equal, AccumulatorRegisterTag, TrustedImm32(Value::emptyValue().asReturnedValue() >> 32)); | 
| 658 |     } | 
| 659 |  | 
| 660 |     Jump jumpNotEmpty() | 
| 661 |     { | 
| 662 |         return branch32(NotEqual, AccumulatorRegisterTag, TrustedImm32(Value::emptyValue().asReturnedValue() >> 32)); | 
| 663 |     } | 
| 664 |  | 
| 665 |     void toBoolean(std::function<void(RegisterID)> continuation) | 
| 666 |     { | 
| 667 |         urshift32(AccumulatorRegisterTag, TrustedImm32(Value::IsIntegerConvertible_Shift - 32), | 
| 668 |                   ScratchRegister); | 
| 669 |         auto needsConversion = branch32(NotEqual, TrustedImm32(1), ScratchRegister); | 
| 670 |         continuation(AccumulatorRegisterValue); | 
| 671 |         Jump done = jump(); | 
| 672 |  | 
| 673 |         // slow path: | 
| 674 |         needsConversion.link(this); | 
| 675 |  | 
| 676 |         bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue | 
| 677 |                 || AccumulatorRegisterTag == ReturnValueRegisterTag; | 
| 678 |         if (accumulatorNeedsSaving) { | 
| 679 |             push(AccumulatorRegisterTag); | 
| 680 |             push(AccumulatorRegisterValue); | 
| 681 |         } | 
| 682 |  | 
| 683 |         if (ArgInRegCount < 2) { | 
| 684 |             if (!accumulatorNeedsSaving) | 
| 685 |                 subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); | 
| 686 |             push(AccumulatorRegisterTag); | 
| 687 |             push(AccumulatorRegisterValue); | 
| 688 |         } else { | 
| 689 |             if (accumulatorNeedsSaving) | 
| 690 |                 subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); | 
| 691 |             move(AccumulatorRegisterValue, registerForArg(0)); | 
| 692 |             move(AccumulatorRegisterTag, registerForArg(1)); | 
| 693 |         } | 
| 694 |         callHelper(Value::toBooleanImpl); | 
| 695 |         and32(TrustedImm32(1), ReturnValueRegisterValue, ScratchRegister); | 
| 696 |         if (accumulatorNeedsSaving) { | 
| 697 |             addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); | 
| 698 |             pop(AccumulatorRegisterValue); | 
| 699 |             pop(AccumulatorRegisterTag); | 
| 700 |         } else if (ArgInRegCount < 2) { | 
| 701 |             addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister); | 
| 702 |         } | 
| 703 |         continuation(ScratchRegister); | 
| 704 |  | 
| 705 |         done.link(this); | 
| 706 |     } | 
| 707 |  | 
| 708 |     void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset) | 
| 709 |     { | 
| 710 |         Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value))); | 
| 711 |         load32(lhsAddr, ScratchRegister); | 
| 712 |         Jump notEqInt = branch32(NotEqual, ScratchRegister, TrustedImm32(rhs)); | 
| 713 |         Jump notEqUndefVal = branch32(NotEqual, ScratchRegister, TrustedImm32(0)); | 
| 714 |         addJumpToOffset(notEqUndefVal, offset); | 
| 715 |         lhsAddr.offset += 4; | 
| 716 |         load32(lhsAddr, ScratchRegister); | 
| 717 |         Jump notEqUndefTag = branch32(NotEqual, ScratchRegister, TrustedImm32(0)); | 
| 718 |         addJumpToOffset(notEqUndefTag, offset); | 
| 719 |         notEqInt.link(this); | 
| 720 |     } | 
| 721 |  | 
| 722 |     void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset) | 
| 723 |     { | 
| 724 |         Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value))); | 
| 725 |         load32(lhsAddr, ScratchRegister); | 
| 726 |         Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister); | 
| 727 |         addJumpToOffset(notEqual, offset); | 
| 728 |         Jump notUndefValue = branch32(NotEqual, TrustedImm32(0), ScratchRegister); | 
| 729 |         lhsAddr.offset += 4; | 
| 730 |         load32(lhsAddr, ScratchRegister); | 
| 731 |         Jump equalUndef = branch32(Equal, TrustedImm32(0), ScratchRegister); | 
| 732 |         addJumpToOffset(equalUndef, offset); | 
| 733 |         notUndefValue.link(this); | 
| 734 |     } | 
| 735 |  | 
| 736 |     void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister) | 
| 737 |     { | 
| 738 |         if (sourceReg != NoRegister) | 
| 739 |             move(sourceReg, AccumulatorRegisterValue); | 
| 740 |         move(TrustedImm32(int(tag)), AccumulatorRegisterTag); | 
| 741 |     } | 
| 742 |  | 
| 743 |     void encodeDoubleIntoAccumulator(FPRegisterID src) | 
| 744 |     { | 
| 745 |         moveDoubleToInts(src, AccumulatorRegisterValue, AccumulatorRegisterTag); | 
| 746 |         xor32(TrustedImm32(Value::NaNEncodeMask >> 32), AccumulatorRegisterTag); | 
| 747 |     } | 
| 748 |  | 
| 749 |     void pushValueAligned(ReturnedValue v) | 
| 750 |     { | 
| 751 |         pushValue(v); | 
| 752 |     } | 
| 753 |  | 
| 754 |     void popValueAligned() | 
| 755 |     { | 
| 756 |         popValue(); | 
| 757 |     } | 
| 758 |  | 
| 759 |     Jump binopBothIntPath(Address lhsAddr, std::function<Jump(void)> fastPath) | 
| 760 |     { | 
| 761 |         Jump accNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), AccumulatorRegisterTag); | 
| 762 |         Address lhsAddrTag = lhsAddr; lhsAddrTag.offset += Value::tagOffset(); | 
| 763 |         load32(lhsAddrTag, ScratchRegister); | 
| 764 |         Jump lhsNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), ScratchRegister); | 
| 765 |  | 
| 766 |         // both integer | 
| 767 |         Address lhsAddrValue = lhsAddr; lhsAddrValue.offset += Value::valueOffset(); | 
| 768 |         load32(lhsAddrValue, ScratchRegister); | 
| 769 |         Jump failure = fastPath(); | 
| 770 |         Jump done = jump(); | 
| 771 |  | 
| 772 |         // all other cases | 
| 773 |         if (failure.isSet()) | 
| 774 |             failure.link(this); | 
| 775 |         accNotInt.link(this); | 
| 776 |         lhsNotInt.link(this); | 
| 777 |  | 
| 778 |         return done; | 
| 779 |     } | 
| 780 |  | 
| 781 |     Jump unopIntPath(std::function<Jump(void)> fastPath) | 
| 782 |     { | 
| 783 |         Jump accNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), AccumulatorRegisterTag); | 
| 784 |  | 
| 785 |         // both integer | 
| 786 |         Jump failure = fastPath(); | 
| 787 |         Jump done = jump(); | 
| 788 |  | 
| 789 |         // all other cases | 
| 790 |         if (failure.isSet()) | 
| 791 |             failure.link(this); | 
| 792 |         accNotInt.link(this); | 
| 793 |  | 
| 794 |         return done; | 
| 795 |     } | 
| 796 |  | 
| 797 |     void callWithAccumulatorByValueAsFirstArgument(std::function<void()> doCall) | 
| 798 |     { | 
| 799 |         if (ArgInRegCount < 2) { | 
| 800 |             subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); | 
| 801 |             push(AccumulatorRegisterTag); | 
| 802 |             push(AccumulatorRegisterValue); | 
| 803 |         } else { | 
| 804 |             move(AccumulatorRegisterValue, registerForArg(0)); | 
| 805 |             move(AccumulatorRegisterTag, registerForArg(1)); | 
| 806 |         } | 
| 807 |         doCall(); | 
| 808 |         if (ArgInRegCount < 2) | 
| 809 |             addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister); | 
| 810 |     } | 
| 811 | }; | 
| 812 |  | 
| 813 | typedef PlatformAssembler32 PlatformAssembler; | 
| 814 | #endif | 
| 815 |  | 
| 816 | #define pasm() reinterpret_cast<PlatformAssembler *>(this->d) | 
| 817 |  | 
| 818 | typedef PlatformAssembler::TrustedImmPtr TrustedImmPtr; | 
| 819 | typedef PlatformAssembler::TrustedImm32 TrustedImm32; | 
| 820 | typedef PlatformAssembler::TrustedImm64 TrustedImm64; | 
| 821 | typedef PlatformAssembler::Address Address; | 
| 822 | typedef PlatformAssembler::RegisterID RegisterID; | 
| 823 | typedef PlatformAssembler::FPRegisterID FPRegisterID; | 
| 824 |  | 
| 825 | static Address regAddr(int reg) | 
| 826 | { | 
| 827 |     return Address(PlatformAssembler::JSStackFrameRegister, reg * int(sizeof(QV4::Value))); | 
| 828 | } | 
| 829 |  | 
| 830 | BaselineAssembler::BaselineAssembler(const Value *constantTable) | 
| 831 |     : d(new PlatformAssembler(constantTable)) | 
| 832 | { | 
| 833 | } | 
| 834 |  | 
| 835 | BaselineAssembler::~BaselineAssembler() | 
| 836 | { | 
| 837 |     delete pasm(); | 
| 838 | } | 
| 839 |  | 
| 840 | void BaselineAssembler::generatePrologue() | 
| 841 | { | 
| 842 |     pasm()->generateFunctionEntry(); | 
| 843 | } | 
| 844 |  | 
| 845 | void BaselineAssembler::generateEpilogue() | 
| 846 | { | 
| 847 |     pasm()->generateCatchTrampoline(); | 
| 848 | } | 
| 849 |  | 
| 850 | void BaselineAssembler::link(Function *function) | 
| 851 | { | 
| 852 |     pasm()->link(function, jitKind: "BaselineJIT" ); | 
| 853 | } | 
| 854 |  | 
| 855 | void BaselineAssembler::addLabel(int offset) | 
| 856 | { | 
| 857 |     pasm()->addLabelForOffset(offset); | 
| 858 | } | 
| 859 |  | 
| 860 | void BaselineAssembler::loadConst(int constIndex) | 
| 861 | { | 
| 862 |     //### | 
| 863 |     if (pasm()->constant(idx: constIndex).isUndefined()) { | 
| 864 |         pasm()->loadUndefined(); | 
| 865 |     } else { | 
| 866 |         pasm()->loadAccumulator(pasm()->loadConstAddress(constIndex)); | 
| 867 |     } | 
| 868 | } | 
| 869 |  | 
| 870 | void BaselineAssembler::copyConst(int constIndex, int destReg) | 
| 871 | { | 
| 872 |     pasm()->copyConst(constIndex, dest: regAddr(reg: destReg)); | 
| 873 | } | 
| 874 |  | 
| 875 | void BaselineAssembler::loadReg(int reg) | 
| 876 | { | 
| 877 |     pasm()->loadAccumulator(addr: regAddr(reg)); | 
| 878 | } | 
| 879 |  | 
| 880 | void JIT::BaselineAssembler::moveReg(int sourceReg, int destReg) | 
| 881 | { | 
| 882 |     pasm()->moveReg(sourceRegAddress: regAddr(reg: sourceReg), destRegAddress: regAddr(reg: destReg)); | 
| 883 | } | 
| 884 |  | 
| 885 | void BaselineAssembler::storeReg(int reg) | 
| 886 | { | 
| 887 |     pasm()->storeAccumulator(addr: regAddr(reg)); | 
| 888 | } | 
| 889 |  | 
| 890 | void BaselineAssembler::loadLocal(int index, int level) | 
| 891 | { | 
| 892 |     Heap::CallContext ctx; | 
| 893 |     Q_UNUSED(ctx) | 
| 894 |     pasm()->loadPointerFromValue(addr: regAddr(reg: CallData::Context), dest: PlatformAssembler::ScratchRegister); | 
| 895 |     while (level) { | 
| 896 |         pasm()->loadPtr(address: Address(PlatformAssembler::ScratchRegister, ctx.outer.offset), dest: PlatformAssembler::ScratchRegister); | 
| 897 |         --level; | 
| 898 |     } | 
| 899 |     pasm()->loadAccumulator(addr: Address(PlatformAssembler::ScratchRegister, ctx.locals.offset + offsetof(ValueArray<0>, values) + sizeof(Value)*index)); | 
| 900 | } | 
| 901 |  | 
| 902 | void BaselineAssembler::storeLocal(int index, int level) | 
| 903 | { | 
| 904 |     Heap::CallContext ctx; | 
| 905 |     Q_UNUSED(ctx) | 
| 906 |     pasm()->loadPtr(address: regAddr(reg: CallData::Context), dest: PlatformAssembler::ScratchRegister); | 
| 907 |     while (level) { | 
| 908 |         pasm()->loadPtr(address: Address(PlatformAssembler::ScratchRegister, ctx.outer.offset), dest: PlatformAssembler::ScratchRegister); | 
| 909 |         --level; | 
| 910 |     } | 
| 911 |     pasm()->storeAccumulator(addr: Address(PlatformAssembler::ScratchRegister, ctx.locals.offset + offsetof(ValueArray<0>, values) + sizeof(Value)*index)); | 
| 912 | } | 
| 913 |  | 
| 914 | void BaselineAssembler::loadString(int stringId) | 
| 915 | { | 
| 916 |     pasm()->loadString(stringId); | 
| 917 | } | 
| 918 |  | 
| 919 | void BaselineAssembler::loadValue(ReturnedValue value) | 
| 920 | { | 
| 921 |     pasm()->loadValue(value); | 
| 922 | } | 
| 923 |  | 
| 924 | void BaselineAssembler::storeHeapObject(int reg) | 
| 925 | { | 
| 926 |     pasm()->storeHeapObject(source: PlatformAssembler::ReturnValueRegisterValue, addr: regAddr(reg)); | 
| 927 | } | 
| 928 |  | 
| 929 | void BaselineAssembler::loadImport(int index) | 
| 930 | { | 
| 931 |     Address addr = pasm()->loadCompilationUnitPtr(target: PlatformAssembler::ScratchRegister); | 
| 932 |     addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, imports); | 
| 933 |     pasm()->loadPtr(address: addr, dest: PlatformAssembler::ScratchRegister); | 
| 934 |     addr.offset = index * int(sizeof(QV4::Value*)); | 
| 935 |     pasm()->loadPtr(address: addr, dest: PlatformAssembler::ScratchRegister); | 
| 936 |     pasm()->loadAccumulator(addr: Address(PlatformAssembler::ScratchRegister)); | 
| 937 | } | 
| 938 |  | 
| 939 | void BaselineAssembler::toNumber() | 
| 940 | { | 
| 941 |     pasm()->toNumber(); | 
| 942 | } | 
| 943 |  | 
| 944 | void BaselineAssembler::uminus() | 
| 945 | { | 
| 946 |     saveAccumulatorInFrame(); | 
| 947 |     pasm()->prepareCallWithArgCount(argc: 1); | 
| 948 |     pasm()->passAccumulatorAsArg(arg: 0); | 
| 949 |     ASM_GENERATE_RUNTIME_CALL(UMinus, CallResultDestination::InAccumulator); | 
| 950 |     checkException(); | 
| 951 | } | 
| 952 |  | 
| 953 | void BaselineAssembler::ucompl() | 
| 954 | { | 
| 955 |     pasm()->toInt32(); | 
| 956 |     pasm()->xor32(imm: TrustedImm32(-1), dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 957 |     pasm()->setAccumulatorTag(tag: IntegerTag); | 
| 958 | } | 
| 959 |  | 
| 960 | static ReturnedValue incHelper(const Value v) | 
| 961 | { | 
| 962 |     double d; | 
| 963 |     if (Q_LIKELY(v.isDouble())) | 
| 964 |         d =  v.doubleValue(); | 
| 965 |     else | 
| 966 |         d = v.toNumberImpl(); | 
| 967 |     return Encode(d + 1.); | 
| 968 | } | 
| 969 |  | 
| 970 | void BaselineAssembler::inc() | 
| 971 | { | 
| 972 |     auto done = pasm()->unopIntPath(fastPath: [this](){ | 
| 973 |         auto overflowed = pasm()->branchAdd32(cond: PlatformAssembler::Overflow, | 
| 974 |                                               src: PlatformAssembler::AccumulatorRegisterValue, | 
| 975 |                                               imm: TrustedImm32(1), | 
| 976 |                                               dest: PlatformAssembler::ScratchRegister); | 
| 977 |         pasm()->setAccumulatorTag(tag: IntegerTag, sourceReg: PlatformAssembler::ScratchRegister); | 
| 978 |         return overflowed; | 
| 979 |     }); | 
| 980 |  | 
| 981 |     // slow path: | 
| 982 |     pasm()->callWithAccumulatorByValueAsFirstArgument(doCall: [this]() { | 
| 983 |         pasm()->callHelper(incHelper); | 
| 984 |         pasm()->saveReturnValueInAccumulator(); | 
| 985 |     }); | 
| 986 |     checkException(); | 
| 987 |  | 
| 988 |     // done. | 
| 989 |     done.link(pasm()); | 
| 990 | } | 
| 991 |  | 
| 992 | static ReturnedValue decHelper(const Value v) | 
| 993 | { | 
| 994 |     double d; | 
| 995 |     if (Q_LIKELY(v.isDouble())) | 
| 996 |         d =  v.doubleValue(); | 
| 997 |     else | 
| 998 |         d = v.toNumberImpl(); | 
| 999 |     return Encode(d - 1.); | 
| 1000 | } | 
| 1001 |  | 
| 1002 | void BaselineAssembler::dec() | 
| 1003 | { | 
| 1004 |     auto done = pasm()->unopIntPath(fastPath: [this](){ | 
| 1005 |         auto overflowed = pasm()->branchSub32(cond: PlatformAssembler::Overflow, | 
| 1006 |                                               src1: PlatformAssembler::AccumulatorRegisterValue, | 
| 1007 |                                               src2: TrustedImm32(1), | 
| 1008 |                                               dest: PlatformAssembler::ScratchRegister); | 
| 1009 |         pasm()->setAccumulatorTag(tag: IntegerTag, sourceReg: PlatformAssembler::ScratchRegister); | 
| 1010 |         return overflowed; | 
| 1011 |     }); | 
| 1012 |  | 
| 1013 |     // slow path: | 
| 1014 |     pasm()->callWithAccumulatorByValueAsFirstArgument(doCall: [this]() { | 
| 1015 |         pasm()->callHelper(decHelper); | 
| 1016 |         pasm()->saveReturnValueInAccumulator(); | 
| 1017 |     }); | 
| 1018 |     checkException(); | 
| 1019 |  | 
| 1020 |     // done. | 
| 1021 |     done.link(pasm()); | 
| 1022 | } | 
| 1023 |  | 
| 1024 | void BaselineAssembler::unot() | 
| 1025 | { | 
| 1026 |     pasm()->toBoolean(continuation: [this](PlatformAssembler::RegisterID resultReg){ | 
| 1027 |         pasm()->compare32(cond: PlatformAssembler::Equal, left: resultReg, | 
| 1028 |                           right: TrustedImm32(0), dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1029 |         pasm()->setAccumulatorTag(tag: QV4::Value::ValueTypeInternal::Boolean); | 
| 1030 |     }); | 
| 1031 | } | 
| 1032 |  | 
| 1033 | void BaselineAssembler::add(int lhs) | 
| 1034 | { | 
| 1035 |     auto done = pasm()->binopBothIntPath(lhsAddr: regAddr(reg: lhs), fastPath: [this](){ | 
| 1036 |         auto overflowed = pasm()->branchAdd32(cond: PlatformAssembler::Overflow, | 
| 1037 |                                               src: PlatformAssembler::AccumulatorRegisterValue, | 
| 1038 |                                               dest: PlatformAssembler::ScratchRegister); | 
| 1039 |         pasm()->setAccumulatorTag(tag: IntegerTag, | 
| 1040 |                                   sourceReg: PlatformAssembler::ScratchRegister); | 
| 1041 |         return overflowed; | 
| 1042 |     }); | 
| 1043 |  | 
| 1044 |     // slow path: | 
| 1045 |     saveAccumulatorInFrame(); | 
| 1046 |     pasm()->prepareCallWithArgCount(argc: 3); | 
| 1047 |     pasm()->passAccumulatorAsArg(arg: 2); | 
| 1048 |     pasm()->passJSSlotAsArg(reg: lhs, arg: 1); | 
| 1049 |     pasm()->passEngineAsArg(arg: 0); | 
| 1050 |     ASM_GENERATE_RUNTIME_CALL(Add, CallResultDestination::InAccumulator); | 
| 1051 |     checkException(); | 
| 1052 |  | 
| 1053 |     // done. | 
| 1054 |     done.link(pasm()); | 
| 1055 | } | 
| 1056 |  | 
| 1057 | void BaselineAssembler::bitAnd(int lhs) | 
| 1058 | { | 
| 1059 |     PlatformAssembler::Address lhsAddr = regAddr(reg: lhs); | 
| 1060 |     pasm()->toInt32LhsAcc(lhs: lhsAddr, lhsTarget: PlatformAssembler::ScratchRegister); | 
| 1061 |     pasm()->and32(src: PlatformAssembler::ScratchRegister, dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1062 |     pasm()->setAccumulatorTag(tag: IntegerTag); | 
| 1063 | } | 
| 1064 |  | 
| 1065 | void BaselineAssembler::bitOr(int lhs) | 
| 1066 | { | 
| 1067 |     PlatformAssembler::Address lhsAddr = regAddr(reg: lhs); | 
| 1068 |     pasm()->toInt32LhsAcc(lhs: lhsAddr, lhsTarget: PlatformAssembler::ScratchRegister); | 
| 1069 |     pasm()->or32(src: PlatformAssembler::ScratchRegister, dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1070 |     pasm()->setAccumulatorTag(tag: IntegerTag); | 
| 1071 | } | 
| 1072 |  | 
| 1073 | void BaselineAssembler::bitXor(int lhs) | 
| 1074 | { | 
| 1075 |     PlatformAssembler::Address lhsAddr = regAddr(reg: lhs); | 
| 1076 |     pasm()->toInt32LhsAcc(lhs: lhsAddr, lhsTarget: PlatformAssembler::ScratchRegister); | 
| 1077 |     pasm()->xor32(src: PlatformAssembler::ScratchRegister, dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1078 |     pasm()->setAccumulatorTag(tag: IntegerTag); | 
| 1079 | } | 
| 1080 |  | 
| 1081 | void BaselineAssembler::ushr(int lhs) | 
| 1082 | { | 
| 1083 |     PlatformAssembler::Address lhsAddr = regAddr(reg: lhs); | 
| 1084 |     pasm()->toInt32LhsAcc(lhs: lhsAddr, lhsTarget: PlatformAssembler::ScratchRegister); | 
| 1085 |     pasm()->and32(imm: TrustedImm32(0x1f), dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1086 |     pasm()->urshift32(shift_amount: PlatformAssembler::AccumulatorRegisterValue, dest: PlatformAssembler::ScratchRegister); | 
| 1087 |     pasm()->move(src: PlatformAssembler::ScratchRegister, dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1088 |     auto doubleEncode = pasm()->branch32(cond: PlatformAssembler::LessThan, | 
| 1089 |                                          left: PlatformAssembler::AccumulatorRegisterValue, | 
| 1090 |                                          right: TrustedImm32(0)); | 
| 1091 |     pasm()->setAccumulatorTag(tag: IntegerTag); | 
| 1092 |     auto done = pasm()->jump(); | 
| 1093 |  | 
| 1094 |     doubleEncode.link(pasm()); | 
| 1095 |     pasm()->convertUInt32ToDouble(src: PlatformAssembler::AccumulatorRegisterValue, | 
| 1096 |                                   dest: PlatformAssembler::FPScratchRegister, | 
| 1097 |                                   PlatformAssembler::ScratchRegister); | 
| 1098 |     pasm()->encodeDoubleIntoAccumulator(src: PlatformAssembler::FPScratchRegister); | 
| 1099 |     done.link(pasm()); | 
| 1100 | } | 
| 1101 |  | 
| 1102 | void BaselineAssembler::shr(int lhs) | 
| 1103 | { | 
| 1104 |     PlatformAssembler::Address lhsAddr = regAddr(reg: lhs); | 
| 1105 |     pasm()->toInt32LhsAcc(lhs: lhsAddr, lhsTarget: PlatformAssembler::ScratchRegister); | 
| 1106 |     pasm()->and32(imm: TrustedImm32(0x1f), dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1107 |     pasm()->rshift32(shift_amount: PlatformAssembler::AccumulatorRegisterValue, dest: PlatformAssembler::ScratchRegister); | 
| 1108 |     pasm()->move(src: PlatformAssembler::ScratchRegister, dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1109 |     pasm()->setAccumulatorTag(tag: IntegerTag); | 
| 1110 | } | 
| 1111 |  | 
| 1112 | void BaselineAssembler::shl(int lhs) | 
| 1113 | { | 
| 1114 |     PlatformAssembler::Address lhsAddr = regAddr(reg: lhs); | 
| 1115 |     pasm()->toInt32LhsAcc(lhs: lhsAddr, lhsTarget: PlatformAssembler::ScratchRegister); | 
| 1116 |     pasm()->and32(imm: TrustedImm32(0x1f), dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1117 |     pasm()->lshift32(shift_amount: PlatformAssembler::AccumulatorRegisterValue, dest: PlatformAssembler::ScratchRegister); | 
| 1118 |     pasm()->move(src: PlatformAssembler::ScratchRegister, dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1119 |     pasm()->setAccumulatorTag(tag: IntegerTag); | 
| 1120 | } | 
| 1121 |  | 
| 1122 | void BaselineAssembler::bitAndConst(int rhs) | 
| 1123 | { | 
| 1124 |     pasm()->toInt32(); | 
| 1125 |     pasm()->and32(imm: TrustedImm32(rhs), dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1126 |     pasm()->setAccumulatorTag(tag: IntegerTag); | 
| 1127 | } | 
| 1128 |  | 
| 1129 | void BaselineAssembler::bitOrConst(int rhs) | 
| 1130 | { | 
| 1131 |     pasm()->toInt32(); | 
| 1132 |     pasm()->or32(imm: TrustedImm32(rhs), dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1133 |     pasm()->setAccumulatorTag(tag: IntegerTag); | 
| 1134 | } | 
| 1135 |  | 
| 1136 | void BaselineAssembler::bitXorConst(int rhs) | 
| 1137 | { | 
| 1138 |     pasm()->toInt32(); | 
| 1139 |     pasm()->xor32(imm: TrustedImm32(rhs), dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1140 |     pasm()->setAccumulatorTag(tag: IntegerTag); | 
| 1141 | } | 
| 1142 |  | 
| 1143 | void BaselineAssembler::ushrConst(int rhs) | 
| 1144 | { | 
| 1145 |     rhs &= 0x1f; | 
| 1146 |     pasm()->toInt32(); | 
| 1147 |     if (rhs) { | 
| 1148 |         // a non zero shift will always give a number encodable as an int | 
| 1149 |         pasm()->urshift32(imm: TrustedImm32(rhs), dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1150 |         pasm()->setAccumulatorTag(tag: IntegerTag); | 
| 1151 |     } else { | 
| 1152 |         // shift with 0 can lead to a negative result | 
| 1153 |         auto doubleEncode = pasm()->branch32(cond: PlatformAssembler::LessThan, | 
| 1154 |                                              left: PlatformAssembler::AccumulatorRegisterValue, | 
| 1155 |                                              right: TrustedImm32(0)); | 
| 1156 |         pasm()->setAccumulatorTag(tag: IntegerTag); | 
| 1157 |         auto done = pasm()->jump(); | 
| 1158 |  | 
| 1159 |         doubleEncode.link(pasm()); | 
| 1160 |         pasm()->convertUInt32ToDouble(src: PlatformAssembler::AccumulatorRegisterValue, | 
| 1161 |                                       dest: PlatformAssembler::FPScratchRegister, | 
| 1162 |                                       PlatformAssembler::ScratchRegister); | 
| 1163 |         pasm()->encodeDoubleIntoAccumulator(src: PlatformAssembler::FPScratchRegister); | 
| 1164 |         done.link(pasm()); | 
| 1165 |     } | 
| 1166 | } | 
| 1167 |  | 
| 1168 | void BaselineAssembler::shrConst(int rhs) | 
| 1169 | { | 
| 1170 |     rhs &= 0x1f; | 
| 1171 |     pasm()->toInt32(); | 
| 1172 |     if (rhs) | 
| 1173 |         pasm()->rshift32(imm: TrustedImm32(rhs), dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1174 |     pasm()->setAccumulatorTag(tag: IntegerTag); | 
| 1175 | } | 
| 1176 |  | 
| 1177 | void BaselineAssembler::shlConst(int rhs) | 
| 1178 | { | 
| 1179 |     rhs &= 0x1f; | 
| 1180 |     pasm()->toInt32(); | 
| 1181 |     if (rhs) | 
| 1182 |         pasm()->lshift32(imm: TrustedImm32(rhs), dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1183 |     pasm()->setAccumulatorTag(tag: IntegerTag); | 
| 1184 | } | 
| 1185 |  | 
| 1186 | void BaselineAssembler::mul(int lhs) | 
| 1187 | { | 
| 1188 |     auto done = pasm()->binopBothIntPath(lhsAddr: regAddr(reg: lhs), fastPath: [this](){ | 
| 1189 |         auto overflowed = pasm()->branchMul32(cond: PlatformAssembler::Overflow, | 
| 1190 |                                               src: PlatformAssembler::AccumulatorRegisterValue, | 
| 1191 |                                               dest: PlatformAssembler::ScratchRegister); | 
| 1192 |         pasm()->setAccumulatorTag(tag: IntegerTag, | 
| 1193 |                                   sourceReg: PlatformAssembler::ScratchRegister); | 
| 1194 |         return overflowed; | 
| 1195 |     }); | 
| 1196 |  | 
| 1197 |     // slow path: | 
| 1198 |     saveAccumulatorInFrame(); | 
| 1199 |     pasm()->prepareCallWithArgCount(argc: 2); | 
| 1200 |     pasm()->passAccumulatorAsArg(arg: 1); | 
| 1201 |     pasm()->passJSSlotAsArg(reg: lhs, arg: 0); | 
| 1202 |     ASM_GENERATE_RUNTIME_CALL(Mul, CallResultDestination::InAccumulator); | 
| 1203 |     checkException(); | 
| 1204 |  | 
| 1205 |     // done. | 
| 1206 |     done.link(pasm()); | 
| 1207 | } | 
| 1208 |  | 
| 1209 | void BaselineAssembler::div(int lhs) | 
| 1210 | { | 
| 1211 |     saveAccumulatorInFrame(); | 
| 1212 |     pasm()->prepareCallWithArgCount(argc: 2); | 
| 1213 |     pasm()->passAccumulatorAsArg(arg: 1); | 
| 1214 |     pasm()->passJSSlotAsArg(reg: lhs, arg: 0); | 
| 1215 |     ASM_GENERATE_RUNTIME_CALL(Div, CallResultDestination::InAccumulator); | 
| 1216 |     checkException(); | 
| 1217 | } | 
| 1218 |  | 
| 1219 | void BaselineAssembler::mod(int lhs) | 
| 1220 | { | 
| 1221 |     saveAccumulatorInFrame(); | 
| 1222 |     pasm()->prepareCallWithArgCount(argc: 2); | 
| 1223 |     pasm()->passAccumulatorAsArg(arg: 1); | 
| 1224 |     pasm()->passJSSlotAsArg(reg: lhs, arg: 0); | 
| 1225 |     ASM_GENERATE_RUNTIME_CALL(Mod, CallResultDestination::InAccumulator); | 
| 1226 |     checkException(); | 
| 1227 | } | 
| 1228 |  | 
| 1229 | void BaselineAssembler::sub(int lhs) | 
| 1230 | { | 
| 1231 |     auto done = pasm()->binopBothIntPath(lhsAddr: regAddr(reg: lhs), fastPath: [this](){ | 
| 1232 |         auto overflowed = pasm()->branchSub32(cond: PlatformAssembler::Overflow, | 
| 1233 |                                               src: PlatformAssembler::AccumulatorRegisterValue, | 
| 1234 |                                               dest: PlatformAssembler::ScratchRegister); | 
| 1235 |         pasm()->setAccumulatorTag(tag: IntegerTag, | 
| 1236 |                                   sourceReg: PlatformAssembler::ScratchRegister); | 
| 1237 |         return overflowed; | 
| 1238 |     }); | 
| 1239 |  | 
| 1240 |     // slow path: | 
| 1241 |     saveAccumulatorInFrame(); | 
| 1242 |     pasm()->prepareCallWithArgCount(argc: 2); | 
| 1243 |     pasm()->passAccumulatorAsArg(arg: 1); | 
| 1244 |     pasm()->passJSSlotAsArg(reg: lhs, arg: 0); | 
| 1245 |     ASM_GENERATE_RUNTIME_CALL(Sub, CallResultDestination::InAccumulator); | 
| 1246 |     checkException(); | 
| 1247 |  | 
| 1248 |     // done. | 
| 1249 |     done.link(pasm()); | 
| 1250 | } | 
| 1251 |  | 
| 1252 | void BaselineAssembler::cmpeqNull() | 
| 1253 | { | 
| 1254 |     pasm()->isNullOrUndefined(); | 
| 1255 |     pasm()->setAccumulatorTag(tag: QV4::Value::ValueTypeInternal::Boolean); | 
| 1256 | } | 
| 1257 |  | 
| 1258 | void BaselineAssembler::cmpneNull() | 
| 1259 | { | 
| 1260 |     pasm()->isNullOrUndefined(); | 
| 1261 |     pasm()->xor32(imm: TrustedImm32(1), dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1262 |     pasm()->setAccumulatorTag(tag: QV4::Value::ValueTypeInternal::Boolean); | 
| 1263 | } | 
| 1264 |  | 
| 1265 | void BaselineAssembler::cmpeqInt(int lhs) | 
| 1266 | { | 
| 1267 |     auto isIntOrBool = pasm()->isIntOrBool(); | 
| 1268 |     saveAccumulatorInFrame(); | 
| 1269 |     pasm()->pushValueAligned(v: Encode(lhs)); | 
| 1270 |     if (PlatformAssembler::ArgInRegCount < 2) | 
| 1271 |         pasm()->push(src: PlatformAssembler::StackPointerRegister); | 
| 1272 |     else | 
| 1273 |         pasm()->move(src: PlatformAssembler::StackPointerRegister, pasm()->registerForArg(arg: 1)); | 
| 1274 |     pasm()->pushAccumulatorAsArg(arg: 0); | 
| 1275 |     pasm()->callRuntimeUnchecked(funcPtr: (void*)Runtime::Equal::call); | 
| 1276 |     pasm()->saveReturnValueInAccumulator(); | 
| 1277 |     if (PlatformAssembler::ArgInRegCount < 2) | 
| 1278 |         pasm()->addPtr(imm: TrustedImm32(2 * PlatformAssembler::PointerSize), srcDest: PlatformAssembler::StackPointerRegister); | 
| 1279 |     pasm()->popValueAligned(); | 
| 1280 |     auto done = pasm()->jump(); | 
| 1281 |     isIntOrBool.link(pasm()); | 
| 1282 |     pasm()->compare32(cond: PlatformAssembler::Equal, left: PlatformAssembler::AccumulatorRegisterValue, | 
| 1283 |                       right: TrustedImm32(lhs), | 
| 1284 |                       dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1285 |     pasm()->setAccumulatorTag(tag: QV4::Value::ValueTypeInternal::Boolean); | 
| 1286 |     done.link(pasm()); | 
| 1287 | } | 
| 1288 |  | 
| 1289 | void BaselineAssembler::cmpneInt(int lhs) | 
| 1290 | { | 
| 1291 |     auto isIntOrBool = pasm()->isIntOrBool(); | 
| 1292 |     saveAccumulatorInFrame(); | 
| 1293 |     pasm()->pushValueAligned(v: Encode(lhs)); | 
| 1294 |     if (PlatformAssembler::ArgInRegCount < 2) | 
| 1295 |         pasm()->push(src: PlatformAssembler::StackPointerRegister); | 
| 1296 |     else | 
| 1297 |         pasm()->move(src: PlatformAssembler::StackPointerRegister, pasm()->registerForArg(arg: 1)); | 
| 1298 |     pasm()->pushAccumulatorAsArg(arg: 0); | 
| 1299 |     pasm()->callRuntimeUnchecked(funcPtr: (void*)Runtime::NotEqual::call); | 
| 1300 |     pasm()->saveReturnValueInAccumulator(); | 
| 1301 |     if (PlatformAssembler::ArgInRegCount < 2) | 
| 1302 |         pasm()->addPtr(imm: TrustedImm32(2 * PlatformAssembler::PointerSize), srcDest: PlatformAssembler::StackPointerRegister); | 
| 1303 |     pasm()->popValueAligned(); | 
| 1304 |     auto done = pasm()->jump(); | 
| 1305 |     isIntOrBool.link(pasm()); | 
| 1306 |     pasm()->compare32(cond: PlatformAssembler::NotEqual, left: PlatformAssembler::AccumulatorRegisterValue, | 
| 1307 |                       right: TrustedImm32(lhs), | 
| 1308 |                       dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1309 |     pasm()->setAccumulatorTag(tag: QV4::Value::ValueTypeInternal::Boolean); | 
| 1310 |     done.link(pasm()); | 
| 1311 | } | 
| 1312 |  | 
| 1313 | void BaselineAssembler::cmp(int cond, CmpFunc function, int lhs) | 
| 1314 | { | 
| 1315 |     auto c = static_cast<PlatformAssembler::RelationalCondition>(cond); | 
| 1316 |     auto done = pasm()->binopBothIntPath(lhsAddr: regAddr(reg: lhs), fastPath: [this, c](){ | 
| 1317 |         pasm()->compare32(cond: c, left: PlatformAssembler::ScratchRegister, | 
| 1318 |                           right: PlatformAssembler::AccumulatorRegisterValue, | 
| 1319 |                           dest: PlatformAssembler::AccumulatorRegisterValue); | 
| 1320 |         return PlatformAssembler::Jump(); | 
| 1321 |     }); | 
| 1322 |  | 
| 1323 |     // slow path: | 
| 1324 |     saveAccumulatorInFrame(); | 
| 1325 |     pasm()->prepareCallWithArgCount(argc: 2); | 
| 1326 |     pasm()->passAccumulatorAsArg(arg: 1); | 
| 1327 |     pasm()->passJSSlotAsArg(reg: lhs, arg: 0); | 
| 1328 |  | 
| 1329 |     callRuntime(funcPtr: reinterpret_cast<void*>(function), dest: CallResultDestination::InAccumulator); | 
| 1330 |     checkException(); | 
| 1331 |  | 
| 1332 |     // done. | 
| 1333 |     done.link(pasm()); | 
| 1334 |     pasm()->setAccumulatorTag(tag: QV4::Value::ValueTypeInternal::Boolean); | 
| 1335 | } | 
| 1336 |  | 
| 1337 | void BaselineAssembler::cmpeq(int lhs) | 
| 1338 | { | 
| 1339 |     cmp(cond: PlatformAssembler::Equal, function: &Runtime::CompareEqual::call, lhs); | 
| 1340 | } | 
| 1341 |  | 
| 1342 | void BaselineAssembler::cmpne(int lhs) | 
| 1343 | { | 
| 1344 |     cmp(cond: PlatformAssembler::NotEqual, function: &Runtime::CompareNotEqual::call, lhs); | 
| 1345 | } | 
| 1346 |  | 
| 1347 | void BaselineAssembler::cmpgt(int lhs) | 
| 1348 | { | 
| 1349 |     cmp(cond: PlatformAssembler::GreaterThan, function: &Runtime::CompareGreaterThan::call, lhs); | 
| 1350 | } | 
| 1351 |  | 
| 1352 | void BaselineAssembler::cmpge(int lhs) | 
| 1353 | { | 
| 1354 |     cmp(cond: PlatformAssembler::GreaterThanOrEqual, function: &Runtime::CompareGreaterEqual::call, lhs); | 
| 1355 | } | 
| 1356 |  | 
| 1357 | void BaselineAssembler::cmplt(int lhs) | 
| 1358 | { | 
| 1359 |     cmp(cond: PlatformAssembler::LessThan, function: &Runtime::CompareLessThan::call, lhs); | 
| 1360 | } | 
| 1361 |  | 
| 1362 | void BaselineAssembler::cmple(int lhs) | 
| 1363 | { | 
| 1364 |     cmp(cond: PlatformAssembler::LessThanOrEqual, function: &Runtime::CompareLessEqual::call, lhs); | 
| 1365 | } | 
| 1366 |  | 
| 1367 | void BaselineAssembler::cmpStrictEqual(int lhs) | 
| 1368 | { | 
| 1369 |     cmp(cond: PlatformAssembler::Equal, function: &Runtime::CompareStrictEqual::call, lhs); | 
| 1370 | } | 
| 1371 |  | 
| 1372 | void BaselineAssembler::cmpStrictNotEqual(int lhs) | 
| 1373 | { | 
| 1374 |     cmp(cond: PlatformAssembler::NotEqual, function: &Runtime::CompareStrictNotEqual::call, lhs); | 
| 1375 | } | 
| 1376 |  | 
| 1377 | int BaselineAssembler::jump(int offset) | 
| 1378 | { | 
| 1379 |     pasm()->addJumpToOffset(pasm()->jump(), offset); | 
| 1380 |     return offset; | 
| 1381 | } | 
| 1382 |  | 
| 1383 | int BaselineAssembler::jumpTrue(int offset) | 
| 1384 | { | 
| 1385 |     pasm()->toBoolean(continuation: [this, offset](PlatformAssembler::RegisterID resultReg) { | 
| 1386 |         auto jump = pasm()->branch32(cond: PlatformAssembler::NotEqual, left: TrustedImm32(0), right: resultReg); | 
| 1387 |         pasm()->addJumpToOffset(jump, offset); | 
| 1388 |     }); | 
| 1389 |     return offset; | 
| 1390 | } | 
| 1391 |  | 
| 1392 | int BaselineAssembler::jumpFalse(int offset) | 
| 1393 | { | 
| 1394 |     pasm()->toBoolean(continuation: [this, offset](PlatformAssembler::RegisterID resultReg) { | 
| 1395 |         auto jump = pasm()->branch32(cond: PlatformAssembler::Equal, left: TrustedImm32(0), right: resultReg); | 
| 1396 |         pasm()->addJumpToOffset(jump, offset); | 
| 1397 |     }); | 
| 1398 |     return offset; | 
| 1399 | } | 
| 1400 |  | 
| 1401 | int BaselineAssembler::jumpNoException(int offset) | 
| 1402 | { | 
| 1403 |     auto jump = pasm()->branch32( | 
| 1404 |         cond: PlatformAssembler::Equal, | 
| 1405 |         left: PlatformAssembler::Address(PlatformAssembler::EngineRegister, | 
| 1406 |                                    offsetof(EngineBase, hasException)), | 
| 1407 |         right: TrustedImm32(0)); | 
| 1408 |     pasm()->addJumpToOffset(jump, offset); | 
| 1409 |     return offset; | 
| 1410 | } | 
| 1411 |  | 
| 1412 | int BaselineAssembler::jumpNotUndefined(int offset) | 
| 1413 | { | 
| 1414 |     pasm()->jumpNotUndefined(offset); | 
| 1415 |     return offset; | 
| 1416 | } | 
| 1417 |  | 
| 1418 | void BaselineAssembler::prepareCallWithArgCount(int argc) | 
| 1419 | { | 
| 1420 |     pasm()->prepareCallWithArgCount(argc); | 
| 1421 | } | 
| 1422 |  | 
| 1423 | void BaselineAssembler::storeInstructionPointer(int instructionOffset) | 
| 1424 | { | 
| 1425 |     pasm()->storeInstructionPointer(instructionOffset); | 
| 1426 | } | 
| 1427 |  | 
| 1428 | void BaselineAssembler::passAccumulatorAsArg(int arg) | 
| 1429 | { | 
| 1430 |     pasm()->passAccumulatorAsArg(arg); | 
| 1431 | } | 
| 1432 |  | 
| 1433 | void BaselineAssembler::passFunctionAsArg(int arg) | 
| 1434 | { | 
| 1435 |     pasm()->passFunctionAsArg(arg); | 
| 1436 | } | 
| 1437 |  | 
| 1438 | void BaselineAssembler::passEngineAsArg(int arg) | 
| 1439 | { | 
| 1440 |     pasm()->passEngineAsArg(arg); | 
| 1441 | } | 
| 1442 |  | 
| 1443 | void BaselineAssembler::passJSSlotAsArg(int reg, int arg) | 
| 1444 | { | 
| 1445 |     pasm()->passJSSlotAsArg(reg, arg); | 
| 1446 | } | 
| 1447 |  | 
| 1448 | void BaselineAssembler::passCppFrameAsArg(int arg) | 
| 1449 | { | 
| 1450 |     pasm()->passCppFrameAsArg(arg); | 
| 1451 | } | 
| 1452 |  | 
| 1453 | void BaselineAssembler::passInt32AsArg(int value, int arg) | 
| 1454 | { | 
| 1455 |     pasm()->passInt32AsArg(value, arg); | 
| 1456 | } | 
| 1457 |  | 
| 1458 | void BaselineAssembler::passPointerAsArg(void *ptr, int arg) | 
| 1459 | { | 
| 1460 |     pasm()->passPointerAsArg(ptr, arg); | 
| 1461 | } | 
| 1462 |  | 
| 1463 | void BaselineAssembler::callRuntime(const void *funcPtr, CallResultDestination dest) | 
| 1464 | { | 
| 1465 |     pasm()->callRuntime(funcPtr, dest); | 
| 1466 | } | 
| 1467 |  | 
| 1468 | void BaselineAssembler::saveAccumulatorInFrame() | 
| 1469 | { | 
| 1470 |     pasm()->storeAccumulator(addr: PlatformAssembler::Address(PlatformAssembler::JSStackFrameRegister, | 
| 1471 |                                                         offsetof(CallData, accumulator))); | 
| 1472 | } | 
| 1473 |  | 
| 1474 | void BaselineAssembler::loadAccumulatorFromFrame() | 
| 1475 | { | 
| 1476 |     pasm()->loadAccumulator(addr: PlatformAssembler::Address(PlatformAssembler::JSStackFrameRegister, | 
| 1477 |                                                        offsetof(CallData, accumulator))); | 
| 1478 | } | 
| 1479 |  | 
| 1480 | static ReturnedValue TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing(CppStackFrame *frame, ExecutionEngine *engine) | 
| 1481 | { | 
| 1482 |     return Runtime::TailCall::call(frame, engine); | 
| 1483 | } | 
| 1484 |  | 
| 1485 | void BaselineAssembler::jsTailCall(int func, int thisObject, int argc, int argv) | 
| 1486 | { | 
| 1487 |     Address tos = pasm()->jsAlloca(slotCount: 4); | 
| 1488 |  | 
| 1489 |     int32_t argcOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_argc; | 
| 1490 |     int32_t argvOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_argv; | 
| 1491 |     int32_t thisOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_thisObject; | 
| 1492 |     int32_t funcOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_function; | 
| 1493 |  | 
| 1494 |     pasm()->storeInt32AsValue(srcInt: argc,      destAddr: Address(tos.base, argcOffset)); | 
| 1495 |     pasm()->storeInt32AsValue(srcInt: argv,      destAddr: Address(tos.base, argvOffset)); | 
| 1496 |     pasm()->moveReg(sourceRegAddress: regAddr(reg: thisObject), destRegAddress: Address(tos.base, thisOffset)); | 
| 1497 |     pasm()->moveReg(sourceRegAddress: regAddr(reg: func),       destRegAddress: Address(tos.base, funcOffset)); | 
| 1498 |     pasm()->tailCallRuntime( | 
| 1499 |             funcPtr: reinterpret_cast<void *>(TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing), | 
| 1500 |             functionName: "TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing" ); | 
| 1501 | } | 
| 1502 |  | 
| 1503 | void BaselineAssembler::checkException() | 
| 1504 | { | 
| 1505 |     pasm()->checkException(); | 
| 1506 | } | 
| 1507 |  | 
| 1508 | void BaselineAssembler::gotoCatchException() | 
| 1509 | { | 
| 1510 |     pasm()->addCatchyJump(pasm()->jump()); | 
| 1511 | } | 
| 1512 |  | 
| 1513 | void BaselineAssembler::getException() | 
| 1514 | { | 
| 1515 |     Q_STATIC_ASSERT(sizeof(QV4::EngineBase::hasException) == 1); | 
| 1516 |  | 
| 1517 |     Address hasExceptionAddr(PlatformAssembler::EngineRegister, | 
| 1518 |                              offsetof(EngineBase, hasException)); | 
| 1519 |     PlatformAssembler::Jump nope = pasm()->branch8(cond: PlatformAssembler::Equal, | 
| 1520 |                                                   left: hasExceptionAddr, | 
| 1521 |                                                   right: TrustedImm32(0)); | 
| 1522 |     pasm()->loadPtr(address: Address(PlatformAssembler::EngineRegister, | 
| 1523 |                             offsetof(EngineBase, exceptionValue)), | 
| 1524 |                     dest: PlatformAssembler::ScratchRegister); | 
| 1525 |     pasm()->loadAccumulator(addr: Address(PlatformAssembler::ScratchRegister)); | 
| 1526 |     pasm()->store8(imm: TrustedImm32(0), address: hasExceptionAddr); | 
| 1527 |     auto done = pasm()->jump(); | 
| 1528 |     nope.link(pasm()); | 
| 1529 |     pasm()->loadValue(value: Value::emptyValue().asReturnedValue()); | 
| 1530 |  | 
| 1531 |     done.link(pasm()); | 
| 1532 | } | 
| 1533 |  | 
| 1534 | void BaselineAssembler::setException() | 
| 1535 | { | 
| 1536 |     auto noException = pasm()->jumpEmpty(); | 
| 1537 |     Address addr(PlatformAssembler::EngineRegister, offsetof(EngineBase, exceptionValue)); | 
| 1538 |     pasm()->loadPtr(address: addr, dest: PlatformAssembler::ScratchRegister); | 
| 1539 |     pasm()->storeAccumulator(addr: Address(PlatformAssembler::ScratchRegister)); | 
| 1540 |     addr.offset = offsetof(EngineBase, hasException); | 
| 1541 |     Q_STATIC_ASSERT(sizeof(QV4::EngineBase::hasException) == 1); | 
| 1542 |     pasm()->store8(imm: TrustedImm32(1), address: addr); | 
| 1543 |     noException.link(pasm()); | 
| 1544 | } | 
| 1545 |  | 
| 1546 | int BaselineAssembler::setUnwindHandler(int offset) | 
| 1547 | { | 
| 1548 |     auto l = pasm()->storePtrWithPatch(initialValue: TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress()); | 
| 1549 |     pasm()->addEHTarget(label: l, offset); | 
| 1550 |     return offset; | 
| 1551 | } | 
| 1552 |  | 
| 1553 |  | 
| 1554 | void BaselineAssembler::clearUnwindHandler() | 
| 1555 | { | 
| 1556 |     pasm()->storePtr(imm: TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress()); | 
| 1557 | } | 
| 1558 |  | 
| 1559 | void JIT::BaselineAssembler::unwindDispatch() | 
| 1560 | { | 
| 1561 |     checkException(); | 
| 1562 |     pasm()->load32(address: Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)), dest: PlatformAssembler::ScratchRegister); | 
| 1563 |     auto noUnwind = pasm()->branch32(cond: PlatformAssembler::Equal, left: PlatformAssembler::ScratchRegister, right: TrustedImm32(0)); | 
| 1564 |     pasm()->sub32(imm: TrustedImm32(1), dest: PlatformAssembler::ScratchRegister); | 
| 1565 |     pasm()->store32(src: PlatformAssembler::ScratchRegister, address: Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel))); | 
| 1566 |     auto jump = pasm()->branch32(cond: PlatformAssembler::Equal, left: PlatformAssembler::ScratchRegister, right: TrustedImm32(0)); | 
| 1567 |     gotoCatchException(); | 
| 1568 |     jump.link(pasm()); | 
| 1569 |  | 
| 1570 |     pasm()->loadPtr(address: Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLabel)), dest: PlatformAssembler::ScratchRegister); | 
| 1571 |     pasm()->jump(target: PlatformAssembler::ScratchRegister); | 
| 1572 |  | 
| 1573 |     noUnwind.link(pasm()); | 
| 1574 | } | 
| 1575 |  | 
| 1576 | int JIT::BaselineAssembler::unwindToLabel(int level, int offset) | 
| 1577 | { | 
| 1578 |     auto l = pasm()->storePtrWithPatch(initialValue: TrustedImmPtr(nullptr), address: Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLabel))); | 
| 1579 |     pasm()->addEHTarget(label: l, offset); | 
| 1580 |     pasm()->store32(imm: TrustedImm32(level), address: Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel))); | 
| 1581 |     gotoCatchException(); | 
| 1582 |     return offset; | 
| 1583 | } | 
| 1584 |  | 
| 1585 | void BaselineAssembler::pushCatchContext(int index, int name) | 
| 1586 | { | 
| 1587 |     pasm()->prepareCallWithArgCount(argc: 3); | 
| 1588 |     pasm()->passInt32AsArg(value: name, arg: 2); | 
| 1589 |     pasm()->passInt32AsArg(value: index, arg: 1); | 
| 1590 |     pasm()->passEngineAsArg(arg: 0); | 
| 1591 |     ASM_GENERATE_RUNTIME_CALL(PushCatchContext, CallResultDestination::Ignore); | 
| 1592 | } | 
| 1593 |  | 
| 1594 | void BaselineAssembler::popContext() | 
| 1595 | { | 
| 1596 |     Heap::CallContext ctx; | 
| 1597 |     Q_UNUSED(ctx) | 
| 1598 |     pasm()->loadPointerFromValue(addr: regAddr(reg: CallData::Context), dest: PlatformAssembler::ScratchRegister); | 
| 1599 |     pasm()->loadPtr(address: Address(PlatformAssembler::ScratchRegister, ctx.outer.offset), dest: PlatformAssembler::ScratchRegister); | 
| 1600 |     pasm()->storeHeapObject(source: PlatformAssembler::ScratchRegister, addr: regAddr(reg: CallData::Context)); | 
| 1601 | } | 
| 1602 |  | 
| 1603 | void BaselineAssembler::deadTemporalZoneCheck(int offsetForSavedIP, int variableName) | 
| 1604 | { | 
| 1605 |     auto valueIsAliveJump = pasm()->jumpNotEmpty(); | 
| 1606 |     storeInstructionPointer(instructionOffset: offsetForSavedIP); | 
| 1607 |     prepareCallWithArgCount(argc: 2); | 
| 1608 |     passInt32AsArg(value: variableName, arg: 1); | 
| 1609 |     passEngineAsArg(arg: 0); | 
| 1610 |     ASM_GENERATE_RUNTIME_CALL(ThrowReferenceError, CallResultDestination::Ignore); | 
| 1611 |     gotoCatchException(); | 
| 1612 |     valueIsAliveJump.link(pasm()); | 
| 1613 | } | 
| 1614 |  | 
| 1615 | void BaselineAssembler::ret() | 
| 1616 | { | 
| 1617 |     pasm()->generateFunctionExit(); | 
| 1618 | } | 
| 1619 |  | 
| 1620 | } // JIT namespace | 
| 1621 | } // QV4 namepsace | 
| 1622 |  | 
| 1623 | QT_END_NAMESPACE | 
| 1624 |  | 
| 1625 | #endif // QT_CONFIG(qml_jit) | 
| 1626 |  |