Ignore:
Timestamp:
Mar 10, 2017, 9:49:42 AM (8 years ago)
Author:
[email protected]
Message:

B3 should have comprehensive support for atomic operations
https://bugs.webkit.org/show_bug.cgi?id=162349

Reviewed by Keith Miller.

Source/JavaScriptCore:

This adds the following capabilities to B3:

  • Atomic weak/strong unfenced/fenced compare-and-swap
  • Atomic add/sub/or/and/xor/xchg
  • Acquire/release fencing on loads/stores
  • Fenceless load-load dependencies


This adds lowering to the following instructions on x86:

  • lock cmpxchg
  • lock xadd
  • lock add/sub/or/and/xor/xchg


This adds lowering to the following instructions on ARM64:

  • ldar and friends
  • stlr and friends
  • ldxr and friends (unfenced LL)
  • stxr and friends (unfended SC)
  • ldaxr and friends (fenced LL)
  • stlxr and friends (fenced SC)
  • eor as a fenceless load-load dependency


This does instruction selection pattern matching to ensure that weak/strong CAS and all of the
variants of fences and atomic math ops get lowered to the best possible instruction sequence.
For example, we support the Equal(AtomicStrongCAS(expected, ...), expected) pattern and a bunch
of its friends. You can say Branch(Equal(AtomicStrongCAS(expected, ...), expected)) and it will
generate the best possible branch sequence on x86 and ARM64.

B3 now knows how to model all of the kinds of fencing. It knows that acq loads are ordered with
respect to each other and with respect to rel stores, creating sequential consistency that
transcends just the acq/rel fences themselves (see Effects::fence). It knows that the phantom
fence effects may only target some abstract heaps but not others, so that load elimination and
store sinking can still operate across fences if you just tell B3 that the fence does not alias
those accesses. This makes it super easy to teach B3 that some of your heap is thread-local.
Even better, it lets you express fine-grained dependencies where the atomics that affect one
property in shared memory do not clobber non-atomics that ffect some other property in shared
memory.

One of my favorite features is Depend, which allows you to express load-load dependencies. On
x86 it lowers to nothing, while on ARM64 it lowers to eor.

This also exposes a common atomicWeakCAS API to the x86_64/ARM64 MacroAssemblers. Same for
acq/rel. JSC's 64-bit JITs are now a happy concurrency playground.

This doesn't yet expose the functionality to JS or wasm. SAB still uses the non-intrinsic
implementations of the Atomics object, for now.

  • CMakeLists.txt:
  • JavaScriptCore.xcodeproj/project.pbxproj:
  • assembler/ARM64Assembler.h:

(JSC::ARM64Assembler::ldar):
(JSC::ARM64Assembler::ldxr):
(JSC::ARM64Assembler::ldaxr):
(JSC::ARM64Assembler::stxr):
(JSC::ARM64Assembler::stlr):
(JSC::ARM64Assembler::stlxr):
(JSC::ARM64Assembler::excepnGenerationImmMask):
(JSC::ARM64Assembler::exoticLoad):
(JSC::ARM64Assembler::storeRelease):
(JSC::ARM64Assembler::exoticStore):

  • assembler/AbstractMacroAssembler.cpp: Added.

(WTF::printInternal):

  • assembler/AbstractMacroAssembler.h:

(JSC::AbstractMacroAssemblerBase::invert):

  • assembler/MacroAssembler.h:
  • assembler/MacroAssemblerARM64.h:

(JSC::MacroAssemblerARM64::loadAcq8SignedExtendTo32):
(JSC::MacroAssemblerARM64::loadAcq8):
(JSC::MacroAssemblerARM64::storeRel8):
(JSC::MacroAssemblerARM64::loadAcq16SignedExtendTo32):
(JSC::MacroAssemblerARM64::loadAcq16):
(JSC::MacroAssemblerARM64::storeRel16):
(JSC::MacroAssemblerARM64::loadAcq32):
(JSC::MacroAssemblerARM64::loadAcq64):
(JSC::MacroAssemblerARM64::storeRel32):
(JSC::MacroAssemblerARM64::storeRel64):
(JSC::MacroAssemblerARM64::loadLink8):
(JSC::MacroAssemblerARM64::loadLinkAcq8):
(JSC::MacroAssemblerARM64::storeCond8):
(JSC::MacroAssemblerARM64::storeCondRel8):
(JSC::MacroAssemblerARM64::loadLink16):
(JSC::MacroAssemblerARM64::loadLinkAcq16):
(JSC::MacroAssemblerARM64::storeCond16):
(JSC::MacroAssemblerARM64::storeCondRel16):
(JSC::MacroAssemblerARM64::loadLink32):
(JSC::MacroAssemblerARM64::loadLinkAcq32):
(JSC::MacroAssemblerARM64::storeCond32):
(JSC::MacroAssemblerARM64::storeCondRel32):
(JSC::MacroAssemblerARM64::loadLink64):
(JSC::MacroAssemblerARM64::loadLinkAcq64):
(JSC::MacroAssemblerARM64::storeCond64):
(JSC::MacroAssemblerARM64::storeCondRel64):
(JSC::MacroAssemblerARM64::atomicStrongCAS8):
(JSC::MacroAssemblerARM64::atomicStrongCAS16):
(JSC::MacroAssemblerARM64::atomicStrongCAS32):
(JSC::MacroAssemblerARM64::atomicStrongCAS64):
(JSC::MacroAssemblerARM64::atomicRelaxedStrongCAS8):
(JSC::MacroAssemblerARM64::atomicRelaxedStrongCAS16):
(JSC::MacroAssemblerARM64::atomicRelaxedStrongCAS32):
(JSC::MacroAssemblerARM64::atomicRelaxedStrongCAS64):
(JSC::MacroAssemblerARM64::branchAtomicWeakCAS8):
(JSC::MacroAssemblerARM64::branchAtomicWeakCAS16):
(JSC::MacroAssemblerARM64::branchAtomicWeakCAS32):
(JSC::MacroAssemblerARM64::branchAtomicWeakCAS64):
(JSC::MacroAssemblerARM64::branchAtomicRelaxedWeakCAS8):
(JSC::MacroAssemblerARM64::branchAtomicRelaxedWeakCAS16):
(JSC::MacroAssemblerARM64::branchAtomicRelaxedWeakCAS32):
(JSC::MacroAssemblerARM64::branchAtomicRelaxedWeakCAS64):
(JSC::MacroAssemblerARM64::depend32):
(JSC::MacroAssemblerARM64::depend64):
(JSC::MacroAssemblerARM64::loadLink):
(JSC::MacroAssemblerARM64::loadLinkAcq):
(JSC::MacroAssemblerARM64::storeCond):
(JSC::MacroAssemblerARM64::storeCondRel):
(JSC::MacroAssemblerARM64::signExtend):
(JSC::MacroAssemblerARM64::branch):
(JSC::MacroAssemblerARM64::atomicStrongCAS):
(JSC::MacroAssemblerARM64::atomicRelaxedStrongCAS):
(JSC::MacroAssemblerARM64::branchAtomicWeakCAS):
(JSC::MacroAssemblerARM64::branchAtomicRelaxedWeakCAS):
(JSC::MacroAssemblerARM64::extractSimpleAddress):
(JSC::MacroAssemblerARM64::signExtend<8>):
(JSC::MacroAssemblerARM64::signExtend<16>):
(JSC::MacroAssemblerARM64::branch<64>):

  • assembler/MacroAssemblerX86Common.h:

(JSC::MacroAssemblerX86Common::add32):
(JSC::MacroAssemblerX86Common::and32):
(JSC::MacroAssemblerX86Common::and16):
(JSC::MacroAssemblerX86Common::and8):
(JSC::MacroAssemblerX86Common::neg32):
(JSC::MacroAssemblerX86Common::neg16):
(JSC::MacroAssemblerX86Common::neg8):
(JSC::MacroAssemblerX86Common::or32):
(JSC::MacroAssemblerX86Common::or16):
(JSC::MacroAssemblerX86Common::or8):
(JSC::MacroAssemblerX86Common::sub16):
(JSC::MacroAssemblerX86Common::sub8):
(JSC::MacroAssemblerX86Common::sub32):
(JSC::MacroAssemblerX86Common::xor32):
(JSC::MacroAssemblerX86Common::xor16):
(JSC::MacroAssemblerX86Common::xor8):
(JSC::MacroAssemblerX86Common::not32):
(JSC::MacroAssemblerX86Common::not16):
(JSC::MacroAssemblerX86Common::not8):
(JSC::MacroAssemblerX86Common::store16):
(JSC::MacroAssemblerX86Common::atomicStrongCAS8):
(JSC::MacroAssemblerX86Common::atomicStrongCAS16):
(JSC::MacroAssemblerX86Common::atomicStrongCAS32):
(JSC::MacroAssemblerX86Common::branchAtomicStrongCAS8):
(JSC::MacroAssemblerX86Common::branchAtomicStrongCAS16):
(JSC::MacroAssemblerX86Common::branchAtomicStrongCAS32):
(JSC::MacroAssemblerX86Common::atomicWeakCAS8):
(JSC::MacroAssemblerX86Common::atomicWeakCAS16):
(JSC::MacroAssemblerX86Common::atomicWeakCAS32):
(JSC::MacroAssemblerX86Common::branchAtomicWeakCAS8):
(JSC::MacroAssemblerX86Common::branchAtomicWeakCAS16):
(JSC::MacroAssemblerX86Common::branchAtomicWeakCAS32):
(JSC::MacroAssemblerX86Common::atomicRelaxedWeakCAS8):
(JSC::MacroAssemblerX86Common::atomicRelaxedWeakCAS16):
(JSC::MacroAssemblerX86Common::atomicRelaxedWeakCAS32):
(JSC::MacroAssemblerX86Common::branchAtomicRelaxedWeakCAS8):
(JSC::MacroAssemblerX86Common::branchAtomicRelaxedWeakCAS16):
(JSC::MacroAssemblerX86Common::branchAtomicRelaxedWeakCAS32):
(JSC::MacroAssemblerX86Common::atomicAdd8):
(JSC::MacroAssemblerX86Common::atomicAdd16):
(JSC::MacroAssemblerX86Common::atomicAdd32):
(JSC::MacroAssemblerX86Common::atomicSub8):
(JSC::MacroAssemblerX86Common::atomicSub16):
(JSC::MacroAssemblerX86Common::atomicSub32):
(JSC::MacroAssemblerX86Common::atomicAnd8):
(JSC::MacroAssemblerX86Common::atomicAnd16):
(JSC::MacroAssemblerX86Common::atomicAnd32):
(JSC::MacroAssemblerX86Common::atomicOr8):
(JSC::MacroAssemblerX86Common::atomicOr16):
(JSC::MacroAssemblerX86Common::atomicOr32):
(JSC::MacroAssemblerX86Common::atomicXor8):
(JSC::MacroAssemblerX86Common::atomicXor16):
(JSC::MacroAssemblerX86Common::atomicXor32):
(JSC::MacroAssemblerX86Common::atomicNeg8):
(JSC::MacroAssemblerX86Common::atomicNeg16):
(JSC::MacroAssemblerX86Common::atomicNeg32):
(JSC::MacroAssemblerX86Common::atomicNot8):
(JSC::MacroAssemblerX86Common::atomicNot16):
(JSC::MacroAssemblerX86Common::atomicNot32):
(JSC::MacroAssemblerX86Common::atomicXchgAdd8):
(JSC::MacroAssemblerX86Common::atomicXchgAdd16):
(JSC::MacroAssemblerX86Common::atomicXchgAdd32):
(JSC::MacroAssemblerX86Common::atomicXchg8):
(JSC::MacroAssemblerX86Common::atomicXchg16):
(JSC::MacroAssemblerX86Common::atomicXchg32):
(JSC::MacroAssemblerX86Common::loadAcq8):
(JSC::MacroAssemblerX86Common::loadAcq8SignedExtendTo32):
(JSC::MacroAssemblerX86Common::loadAcq16):
(JSC::MacroAssemblerX86Common::loadAcq16SignedExtendTo32):
(JSC::MacroAssemblerX86Common::loadAcq32):
(JSC::MacroAssemblerX86Common::storeRel8):
(JSC::MacroAssemblerX86Common::storeRel16):
(JSC::MacroAssemblerX86Common::storeRel32):
(JSC::MacroAssemblerX86Common::storeFence):
(JSC::MacroAssemblerX86Common::loadFence):
(JSC::MacroAssemblerX86Common::replaceWithJump):
(JSC::MacroAssemblerX86Common::maxJumpReplacementSize):
(JSC::MacroAssemblerX86Common::patchableJumpSize):
(JSC::MacroAssemblerX86Common::supportsFloatingPointRounding):
(JSC::MacroAssemblerX86Common::supportsAVX):
(JSC::MacroAssemblerX86Common::updateEax1EcxFlags):
(JSC::MacroAssemblerX86Common::x86Condition):
(JSC::MacroAssemblerX86Common::atomicStrongCAS):
(JSC::MacroAssemblerX86Common::branchAtomicStrongCAS):

  • assembler/MacroAssemblerX86_64.h:

(JSC::MacroAssemblerX86_64::add64):
(JSC::MacroAssemblerX86_64::and64):
(JSC::MacroAssemblerX86_64::neg64):
(JSC::MacroAssemblerX86_64::or64):
(JSC::MacroAssemblerX86_64::sub64):
(JSC::MacroAssemblerX86_64::xor64):
(JSC::MacroAssemblerX86_64::not64):
(JSC::MacroAssemblerX86_64::store64):
(JSC::MacroAssemblerX86_64::atomicStrongCAS64):
(JSC::MacroAssemblerX86_64::branchAtomicStrongCAS64):
(JSC::MacroAssemblerX86_64::atomicWeakCAS64):
(JSC::MacroAssemblerX86_64::branchAtomicWeakCAS64):
(JSC::MacroAssemblerX86_64::atomicRelaxedWeakCAS64):
(JSC::MacroAssemblerX86_64::branchAtomicRelaxedWeakCAS64):
(JSC::MacroAssemblerX86_64::atomicAdd64):
(JSC::MacroAssemblerX86_64::atomicSub64):
(JSC::MacroAssemblerX86_64::atomicAnd64):
(JSC::MacroAssemblerX86_64::atomicOr64):
(JSC::MacroAssemblerX86_64::atomicXor64):
(JSC::MacroAssemblerX86_64::atomicNeg64):
(JSC::MacroAssemblerX86_64::atomicNot64):
(JSC::MacroAssemblerX86_64::atomicXchgAdd64):
(JSC::MacroAssemblerX86_64::atomicXchg64):
(JSC::MacroAssemblerX86_64::loadAcq64):
(JSC::MacroAssemblerX86_64::storeRel64):

  • assembler/X86Assembler.h:

(JSC::X86Assembler::addl_mr):
(JSC::X86Assembler::addq_mr):
(JSC::X86Assembler::addq_rm):
(JSC::X86Assembler::addq_im):
(JSC::X86Assembler::andl_mr):
(JSC::X86Assembler::andl_rm):
(JSC::X86Assembler::andw_rm):
(JSC::X86Assembler::andb_rm):
(JSC::X86Assembler::andl_im):
(JSC::X86Assembler::andw_im):
(JSC::X86Assembler::andb_im):
(JSC::X86Assembler::andq_mr):
(JSC::X86Assembler::andq_rm):
(JSC::X86Assembler::andq_im):
(JSC::X86Assembler::incq_m):
(JSC::X86Assembler::negq_m):
(JSC::X86Assembler::negl_m):
(JSC::X86Assembler::negw_m):
(JSC::X86Assembler::negb_m):
(JSC::X86Assembler::notl_m):
(JSC::X86Assembler::notw_m):
(JSC::X86Assembler::notb_m):
(JSC::X86Assembler::notq_m):
(JSC::X86Assembler::orl_mr):
(JSC::X86Assembler::orl_rm):
(JSC::X86Assembler::orw_rm):
(JSC::X86Assembler::orb_rm):
(JSC::X86Assembler::orl_im):
(JSC::X86Assembler::orw_im):
(JSC::X86Assembler::orb_im):
(JSC::X86Assembler::orq_mr):
(JSC::X86Assembler::orq_rm):
(JSC::X86Assembler::orq_im):
(JSC::X86Assembler::subl_mr):
(JSC::X86Assembler::subl_rm):
(JSC::X86Assembler::subw_rm):
(JSC::X86Assembler::subb_rm):
(JSC::X86Assembler::subl_im):
(JSC::X86Assembler::subw_im):
(JSC::X86Assembler::subb_im):
(JSC::X86Assembler::subq_mr):
(JSC::X86Assembler::subq_rm):
(JSC::X86Assembler::subq_im):
(JSC::X86Assembler::xorl_mr):
(JSC::X86Assembler::xorl_rm):
(JSC::X86Assembler::xorl_im):
(JSC::X86Assembler::xorw_rm):
(JSC::X86Assembler::xorw_im):
(JSC::X86Assembler::xorb_rm):
(JSC::X86Assembler::xorb_im):
(JSC::X86Assembler::xorq_im):
(JSC::X86Assembler::xorq_rm):
(JSC::X86Assembler::xorq_mr):
(JSC::X86Assembler::xchgb_rm):
(JSC::X86Assembler::xchgw_rm):
(JSC::X86Assembler::xchgl_rm):
(JSC::X86Assembler::xchgq_rm):
(JSC::X86Assembler::movw_im):
(JSC::X86Assembler::movq_i32m):
(JSC::X86Assembler::cmpxchgb_rm):
(JSC::X86Assembler::cmpxchgw_rm):
(JSC::X86Assembler::cmpxchgl_rm):
(JSC::X86Assembler::cmpxchgq_rm):
(JSC::X86Assembler::xaddb_rm):
(JSC::X86Assembler::xaddw_rm):
(JSC::X86Assembler::xaddl_rm):
(JSC::X86Assembler::xaddq_rm):
(JSC::X86Assembler::X86InstructionFormatter::SingleInstructionBufferWriter::memoryModRM):

  • b3/B3AtomicValue.cpp: Added.

(JSC::B3::AtomicValue::~AtomicValue):
(JSC::B3::AtomicValue::dumpMeta):
(JSC::B3::AtomicValue::cloneImpl):
(JSC::B3::AtomicValue::AtomicValue):

  • b3/B3AtomicValue.h: Added.
  • b3/B3BasicBlock.h:
  • b3/B3BlockInsertionSet.cpp:

(JSC::B3::BlockInsertionSet::BlockInsertionSet):
(JSC::B3::BlockInsertionSet::insert): Deleted.
(JSC::B3::BlockInsertionSet::insertBefore): Deleted.
(JSC::B3::BlockInsertionSet::insertAfter): Deleted.
(JSC::B3::BlockInsertionSet::execute): Deleted.

  • b3/B3BlockInsertionSet.h:
  • b3/B3Effects.cpp:

(JSC::B3::Effects::interferes):
(JSC::B3::Effects::operator==):
(JSC::B3::Effects::dump):

  • b3/B3Effects.h:

(JSC::B3::Effects::forCall):
(JSC::B3::Effects::mustExecute):

  • b3/B3EliminateCommonSubexpressions.cpp:
  • b3/B3Generate.cpp:

(JSC::B3::generateToAir):

  • b3/B3GenericBlockInsertionSet.h: Added.

(JSC::B3::GenericBlockInsertionSet::GenericBlockInsertionSet):
(JSC::B3::GenericBlockInsertionSet::insert):
(JSC::B3::GenericBlockInsertionSet::insertBefore):
(JSC::B3::GenericBlockInsertionSet::insertAfter):
(JSC::B3::GenericBlockInsertionSet::execute):

  • b3/B3HeapRange.h:

(JSC::B3::HeapRange::operator|):

  • b3/B3InsertionSet.cpp:

(JSC::B3::InsertionSet::insertClone):

  • b3/B3InsertionSet.h:
  • b3/B3LegalizeMemoryOffsets.cpp:
  • b3/B3LowerMacros.cpp:

(JSC::B3::lowerMacros):

  • b3/B3LowerMacrosAfterOptimizations.cpp:
  • b3/B3LowerToAir.cpp:

(JSC::B3::Air::LowerToAir::LowerToAir):
(JSC::B3::Air::LowerToAir::run):
(JSC::B3::Air::LowerToAir::effectiveAddr):
(JSC::B3::Air::LowerToAir::addr):
(JSC::B3::Air::LowerToAir::loadPromiseAnyOpcode):
(JSC::B3::Air::LowerToAir::appendShift):
(JSC::B3::Air::LowerToAir::tryAppendStoreBinOp):
(JSC::B3::Air::LowerToAir::storeOpcode):
(JSC::B3::Air::LowerToAir::createStore):
(JSC::B3::Air::LowerToAir::finishAppendingInstructions):
(JSC::B3::Air::LowerToAir::newBlock):
(JSC::B3::Air::LowerToAir::splitBlock):
(JSC::B3::Air::LowerToAir::fillStackmap):
(JSC::B3::Air::LowerToAir::appendX86Div):
(JSC::B3::Air::LowerToAir::appendX86UDiv):
(JSC::B3::Air::LowerToAir::loadLinkOpcode):
(JSC::B3::Air::LowerToAir::storeCondOpcode):
(JSC::B3::Air::LowerToAir::appendCAS):
(JSC::B3::Air::LowerToAir::appendVoidAtomic):
(JSC::B3::Air::LowerToAir::appendGeneralAtomic):
(JSC::B3::Air::LowerToAir::lower):
(JSC::B3::Air::LowerToAir::lowerX86Div): Deleted.
(JSC::B3::Air::LowerToAir::lowerX86UDiv): Deleted.

  • b3/B3LowerToAir.h:
  • b3/B3MemoryValue.cpp:

(JSC::B3::MemoryValue::isLegalOffset):
(JSC::B3::MemoryValue::accessType):
(JSC::B3::MemoryValue::accessBank):
(JSC::B3::MemoryValue::accessByteSize):
(JSC::B3::MemoryValue::dumpMeta):
(JSC::B3::MemoryValue::MemoryValue):
(JSC::B3::MemoryValue::accessWidth): Deleted.

  • b3/B3MemoryValue.h:
  • b3/B3MemoryValueInlines.h: Added.

(JSC::B3::MemoryValue::isLegalOffset):
(JSC::B3::MemoryValue::requiresSimpleAddr):
(JSC::B3::MemoryValue::accessWidth):

  • b3/B3MoveConstants.cpp:
  • b3/B3NativeTraits.h: Added.
  • b3/B3Opcode.cpp:

(JSC::B3::storeOpcode):
(WTF::printInternal):

  • b3/B3Opcode.h:

(JSC::B3::isLoad):
(JSC::B3::isStore):
(JSC::B3::isLoadStore):
(JSC::B3::isAtomic):
(JSC::B3::isAtomicCAS):
(JSC::B3::isAtomicXchg):
(JSC::B3::isMemoryAccess):
(JSC::B3::signExtendOpcode):

  • b3/B3Procedure.cpp:

(JSC::B3::Procedure::dump):

  • b3/B3Procedure.h:

(JSC::B3::Procedure::hasQuirks):
(JSC::B3::Procedure::setHasQuirks):

  • b3/B3PureCSE.cpp:

(JSC::B3::pureCSE):

  • b3/B3PureCSE.h:
  • b3/B3ReduceStrength.cpp:
  • b3/B3Validate.cpp:
  • b3/B3Value.cpp:

(JSC::B3::Value::returnsBool):
(JSC::B3::Value::effects):
(JSC::B3::Value::key):
(JSC::B3::Value::performSubstitution):
(JSC::B3::Value::typeFor):

  • b3/B3Value.h:
  • b3/B3Width.cpp:

(JSC::B3::bestType):

  • b3/B3Width.h:

(JSC::B3::canonicalWidth):
(JSC::B3::isCanonicalWidth):
(JSC::B3::mask):

  • b3/air/AirArg.cpp:

(JSC::B3::Air::Arg::jsHash):
(JSC::B3::Air::Arg::dump):
(WTF::printInternal):

  • b3/air/AirArg.h:

(JSC::B3::Air::Arg::isAnyUse):
(JSC::B3::Air::Arg::isColdUse):
(JSC::B3::Air::Arg::cooled):
(JSC::B3::Air::Arg::isEarlyUse):
(JSC::B3::Air::Arg::isLateUse):
(JSC::B3::Air::Arg::isAnyDef):
(JSC::B3::Air::Arg::isEarlyDef):
(JSC::B3::Air::Arg::isLateDef):
(JSC::B3::Air::Arg::isZDef):
(JSC::B3::Air::Arg::simpleAddr):
(JSC::B3::Air::Arg::statusCond):
(JSC::B3::Air::Arg::isSimpleAddr):
(JSC::B3::Air::Arg::isMemory):
(JSC::B3::Air::Arg::isStatusCond):
(JSC::B3::Air::Arg::isCondition):
(JSC::B3::Air::Arg::ptr):
(JSC::B3::Air::Arg::base):
(JSC::B3::Air::Arg::isGP):
(JSC::B3::Air::Arg::isFP):
(JSC::B3::Air::Arg::isValidForm):
(JSC::B3::Air::Arg::forEachTmpFast):
(JSC::B3::Air::Arg::forEachTmp):
(JSC::B3::Air::Arg::asAddress):
(JSC::B3::Air::Arg::asStatusCondition):
(JSC::B3::Air::Arg::isInvertible):
(JSC::B3::Air::Arg::inverted):

  • b3/air/AirBasicBlock.cpp:

(JSC::B3::Air::BasicBlock::setSuccessors):

  • b3/air/AirBasicBlock.h:
  • b3/air/AirBlockInsertionSet.cpp: Added.

(JSC::B3::Air::BlockInsertionSet::BlockInsertionSet):
(JSC::B3::Air::BlockInsertionSet::~BlockInsertionSet):

  • b3/air/AirBlockInsertionSet.h: Added.
  • b3/air/AirDumpAsJS.cpp: Removed.
  • b3/air/AirDumpAsJS.h: Removed.
  • b3/air/AirEliminateDeadCode.cpp:

(JSC::B3::Air::eliminateDeadCode):

  • b3/air/AirGenerate.cpp:

(JSC::B3::Air::prepareForGeneration):

  • b3/air/AirInstInlines.h:

(JSC::B3::Air::isAtomicStrongCASValid):
(JSC::B3::Air::isBranchAtomicStrongCASValid):
(JSC::B3::Air::isAtomicStrongCAS8Valid):
(JSC::B3::Air::isAtomicStrongCAS16Valid):
(JSC::B3::Air::isAtomicStrongCAS32Valid):
(JSC::B3::Air::isAtomicStrongCAS64Valid):
(JSC::B3::Air::isBranchAtomicStrongCAS8Valid):
(JSC::B3::Air::isBranchAtomicStrongCAS16Valid):
(JSC::B3::Air::isBranchAtomicStrongCAS32Valid):
(JSC::B3::Air::isBranchAtomicStrongCAS64Valid):

  • b3/air/AirOpcode.opcodes:
  • b3/air/AirOptimizeBlockOrder.cpp:

(JSC::B3::Air::optimizeBlockOrder):

  • b3/air/AirPadInterference.cpp:

(JSC::B3::Air::padInterference):

  • b3/air/AirSpillEverything.cpp:

(JSC::B3::Air::spillEverything):

  • b3/air/opcode_generator.rb:
  • b3/testb3.cpp:

(JSC::B3::testLoadAcq42):
(JSC::B3::testStoreRelAddLoadAcq32):
(JSC::B3::testStoreRelAddLoadAcq8):
(JSC::B3::testStoreRelAddFenceLoadAcq8):
(JSC::B3::testStoreRelAddLoadAcq16):
(JSC::B3::testStoreRelAddLoadAcq64):
(JSC::B3::testTrappingStoreElimination):
(JSC::B3::testX86LeaAddAdd):
(JSC::B3::testX86LeaAddShlLeftScale1):
(JSC::B3::testAtomicWeakCAS):
(JSC::B3::testAtomicStrongCAS):
(JSC::B3::testAtomicXchg):
(JSC::B3::testDepend32):
(JSC::B3::testDepend64):
(JSC::B3::run):

  • runtime/Options.h:

Websites/webkit.org:

Document the new opcodes!

  • docs/b3/intermediate-representation.html:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/b3/testb3.cpp

    r211896 r213714  
    3131#include "AllowMacroScratchRegisterUsage.h"
    3232#include "B3ArgumentRegValue.h"
     33#include "B3AtomicValue.h"
    3334#include "B3BasicBlockInlines.h"
    3435#include "B3CCallValue.h"
     
    4546#include "B3MemoryValue.h"
    4647#include "B3MoveConstants.h"
     48#include "B3NativeTraits.h"
    4749#include "B3Procedure.h"
    4850#include "B3ReduceStrength.h"
     
    298300}
    299301
     302void testLoadAcq42()
     303{
     304    Procedure proc;
     305    BasicBlock* root = proc.addBlock();
     306    int x = 42;
     307    root->appendNewControlValue(
     308        proc, Return, Origin(),
     309        root->appendNew<MemoryValue>(
     310            proc, Load, Int32, Origin(),
     311            root->appendNew<ConstPtrValue>(proc, Origin(), &x),
     312            0, HeapRange(42), HeapRange(42)));
     313
     314    auto code = compile(proc);
     315    if (isARM64())
     316        checkUsesInstruction(*code, "lda");
     317    CHECK(invoke<int>(*code) == 42);
     318}
     319
    300320void testLoadWithOffsetImpl(int32_t offset64, int32_t offset32)
    301321{
     
    57495769}
    57505770
     5771void testStoreRelAddLoadAcq32(int amount)
     5772{
     5773    Procedure proc;
     5774    BasicBlock* root = proc.addBlock();
     5775    int slot = 37;
     5776    ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot);
     5777    root->appendNew<MemoryValue>(
     5778        proc, Store, Origin(),
     5779        root->appendNew<Value>(
     5780            proc, Add, Origin(),
     5781            root->appendNew<MemoryValue>(
     5782                proc, Load, Int32, Origin(), slotPtr, 0, HeapRange(42), HeapRange(42)),
     5783            root->appendNew<Value>(
     5784                proc, Trunc, Origin(),
     5785                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
     5786        slotPtr, 0, HeapRange(42), HeapRange(42));
     5787    root->appendNewControlValue(
     5788        proc, Return, Origin(),
     5789        root->appendNew<Const32Value>(proc, Origin(), 0));
     5790
     5791    auto code = compile(proc);
     5792    if (isARM64()) {
     5793        checkUsesInstruction(*code, "lda");
     5794        checkUsesInstruction(*code, "stl");
     5795    }
     5796    CHECK(!invoke<int>(*code, amount));
     5797    CHECK(slot == 37 + amount);
     5798}
     5799
    57515800void testStoreAddLoadImm32(int amount)
    57525801{
     
    57935842}
    57945843
     5844void testStoreRelAddLoadAcq8(int amount, B3::Opcode loadOpcode)
     5845{
     5846    Procedure proc;
     5847    BasicBlock* root = proc.addBlock();
     5848    int8_t slot = 37;
     5849    ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot);
     5850    root->appendNew<MemoryValue>(
     5851        proc, Store8, Origin(),
     5852        root->appendNew<Value>(
     5853            proc, Add, Origin(),
     5854            root->appendNew<MemoryValue>(
     5855                proc, loadOpcode, Origin(), slotPtr, 0, HeapRange(42), HeapRange(42)),
     5856            root->appendNew<Value>(
     5857                proc, Trunc, Origin(),
     5858                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
     5859        slotPtr, 0, HeapRange(42), HeapRange(42));
     5860    root->appendNewControlValue(
     5861        proc, Return, Origin(),
     5862        root->appendNew<Const32Value>(proc, Origin(), 0));
     5863
     5864    auto code = compile(proc);
     5865    if (isARM64()) {
     5866        checkUsesInstruction(*code, "lda");
     5867        checkUsesInstruction(*code, "stl");
     5868    }
     5869    CHECK(!invoke<int>(*code, amount));
     5870    CHECK(slot == 37 + amount);
     5871}
     5872
     5873void testStoreRelAddFenceLoadAcq8(int amount, B3::Opcode loadOpcode)
     5874{
     5875    Procedure proc;
     5876    BasicBlock* root = proc.addBlock();
     5877    int8_t slot = 37;
     5878    ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot);
     5879    Value* loadedValue = root->appendNew<MemoryValue>(
     5880        proc, loadOpcode, Origin(), slotPtr, 0, HeapRange(42), HeapRange(42));
     5881    PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Void, Origin());
     5882    patchpoint->clobber(RegisterSet::macroScratchRegisters());
     5883    patchpoint->setGenerator(
     5884        [&] (CCallHelpers& jit, const StackmapGenerationParams&) {
     5885            AllowMacroScratchRegisterUsage allowScratch(jit);
     5886            jit.store8(CCallHelpers::TrustedImm32(0xbeef), &slot);
     5887        });
     5888    patchpoint->effects = Effects::none();
     5889    patchpoint->effects.fence = true;
     5890    root->appendNew<MemoryValue>(
     5891        proc, Store8, Origin(),
     5892        root->appendNew<Value>(
     5893            proc, Add, Origin(),
     5894            loadedValue,
     5895            root->appendNew<Value>(
     5896                proc, Trunc, Origin(),
     5897                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
     5898        slotPtr, 0, HeapRange(42), HeapRange(42));
     5899    root->appendNewControlValue(
     5900        proc, Return, Origin(),
     5901        root->appendNew<Const32Value>(proc, Origin(), 0));
     5902
     5903    auto code = compile(proc);
     5904    if (isARM64()) {
     5905        checkUsesInstruction(*code, "lda");
     5906        checkUsesInstruction(*code, "stl");
     5907    }
     5908    CHECK(!invoke<int>(*code, amount));
     5909    CHECK(slot == 37 + amount);
     5910}
     5911
    57955912void testStoreAddLoadImm8(int amount, B3::Opcode loadOpcode)
    57965913{
     
    58375954}
    58385955
    5839 void testStoreAddLoadImm16(int amount, B3::Opcode loadOpcode)
     5956void testStoreRelAddLoadAcq16(int amount, B3::Opcode loadOpcode)
    58405957{
    58415958    Procedure proc;
     
    58475964        root->appendNew<Value>(
    58485965            proc, Add, Origin(),
     5966            root->appendNew<MemoryValue>(
     5967                proc, loadOpcode, Origin(), slotPtr, 0, HeapRange(42), HeapRange(42)),
     5968            root->appendNew<Value>(
     5969                proc, Trunc, Origin(),
     5970                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
     5971        slotPtr, 0, HeapRange(42), HeapRange(42));
     5972    root->appendNewControlValue(
     5973        proc, Return, Origin(),
     5974        root->appendNew<Const32Value>(proc, Origin(), 0));
     5975
     5976    auto code = compile(proc);
     5977    if (isARM64()) {
     5978        checkUsesInstruction(*code, "lda");
     5979        checkUsesInstruction(*code, "stl");
     5980    }
     5981    CHECK(!invoke<int>(*code, amount));
     5982    CHECK(slot == 37 + amount);
     5983}
     5984
     5985void testStoreAddLoadImm16(int amount, B3::Opcode loadOpcode)
     5986{
     5987    Procedure proc;
     5988    BasicBlock* root = proc.addBlock();
     5989    int16_t slot = 37;
     5990    ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot);
     5991    root->appendNew<MemoryValue>(
     5992        proc, Store16, Origin(),
     5993        root->appendNew<Value>(
     5994            proc, Add, Origin(),
    58495995            root->appendNew<MemoryValue>(proc, loadOpcode, Origin(), slotPtr),
    58505996            root->appendNew<Const32Value>(proc, Origin(), amount)),
     
    58766022
    58776023    CHECK(!compileAndRun<int>(proc, amount));
     6024    CHECK(slot == 37000000000ll + amount);
     6025}
     6026
     6027void testStoreRelAddLoadAcq64(int amount)
     6028{
     6029    Procedure proc;
     6030    BasicBlock* root = proc.addBlock();
     6031    int64_t slot = 37000000000ll;
     6032    ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot);
     6033    root->appendNew<MemoryValue>(
     6034        proc, Store, Origin(),
     6035        root->appendNew<Value>(
     6036            proc, Add, Origin(),
     6037            root->appendNew<MemoryValue>(
     6038                proc, Load, Int64, Origin(), slotPtr, 0, HeapRange(42), HeapRange(42)),
     6039            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
     6040        slotPtr, 0, HeapRange(42), HeapRange(42));
     6041    root->appendNewControlValue(
     6042        proc, Return, Origin(),
     6043        root->appendNew<Const32Value>(proc, Origin(), 0));
     6044
     6045    auto code = compile(proc);
     6046    if (isARM64()) {
     6047        checkUsesInstruction(*code, "lda");
     6048        checkUsesInstruction(*code, "stl");
     6049    }
     6050    CHECK(!invoke<int>(*code, amount));
    58786051    CHECK(slot == 37000000000ll + amount);
    58796052}
     
    1379213965    unsigned storeCount = 0;
    1379313966    for (Value* value : proc.values()) {
    13794         if (MemoryValue::isStore(value->opcode()))
     13967        if (isStore(value->opcode()))
    1379513968            storeCount++;
    1379613969    }
     
    1399814171   
    1399914172    auto code = compile(proc);
     14173    CHECK_EQ(invoke<intptr_t>(*code, 1, 2), (1 + 2) + 100);
    1400014174    checkDisassembly(
    1400114175        *code,
     
    1400514179        },
    1400614180        "Expected to find something like lea 0x64(%rdi,%rsi), %rax but didn't!");
    14007     CHECK_EQ(invoke<intptr_t>(*code, 1, 2), (1 + 2) + 100);
    1400814181}
    1400914182
     
    1404214215   
    1404314216    auto code = compile(proc);
     14217    CHECK_EQ(invoke<intptr_t>(*code, 1, 2), 1 + 2);
    1404414218    checkDisassembly(
    1404514219        *code,
     
    1404914223        },
    1405014224        "Expected to find something like lea (%rdi,%rsi), %rax but didn't!");
    14051     CHECK_EQ(invoke<intptr_t>(*code, 1, 2), 1 + 2);
    1405214225}
    1405314226
     
    1427814451}
    1427914452
     14453template<typename T>
     14454void testAtomicWeakCAS()
     14455{
     14456    Type type = NativeTraits<T>::type;
     14457    Width width = NativeTraits<T>::width;
     14458   
     14459    auto checkMyDisassembly = [&] (Compilation& compilation, bool fenced) {
     14460        if (isX86()) {
     14461            checkUsesInstruction(compilation, "lock");
     14462            checkUsesInstruction(compilation, "cmpxchg");
     14463        } else {
     14464            if (fenced) {
     14465                checkUsesInstruction(compilation, "ldax");
     14466                checkUsesInstruction(compilation, "stlx");
     14467            } else {
     14468                checkUsesInstruction(compilation, "ldx");
     14469                checkUsesInstruction(compilation, "stx");
     14470            }
     14471        }
     14472    };
     14473   
     14474    {
     14475        Procedure proc;
     14476        BasicBlock* root = proc.addBlock();
     14477        BasicBlock* reloop = proc.addBlock();
     14478        BasicBlock* done = proc.addBlock();
     14479       
     14480        Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     14481        root->appendNew<Value>(proc, Jump, Origin());
     14482        root->setSuccessors(reloop);
     14483       
     14484        reloop->appendNew<Value>(
     14485            proc, Branch, Origin(),
     14486            reloop->appendNew<AtomicValue>(
     14487                proc, AtomicWeakCAS, Origin(), width,
     14488                reloop->appendIntConstant(proc, Origin(), type, 42),
     14489                reloop->appendIntConstant(proc, Origin(), type, 0xbeef),
     14490                ptr));
     14491        reloop->setSuccessors(done, reloop);
     14492       
     14493        done->appendNew<Value>(proc, Return, Origin());
     14494       
     14495        auto code = compile(proc);
     14496        T value[2];
     14497        value[0] = 42;
     14498        value[1] = 13;
     14499        invoke<void>(*code, value);
     14500        CHECK_EQ(value[0], static_cast<T>(0xbeef));
     14501        CHECK_EQ(value[1], 13);
     14502        checkMyDisassembly(*code, true);
     14503    }
     14504   
     14505    {
     14506        Procedure proc;
     14507        BasicBlock* root = proc.addBlock();
     14508        BasicBlock* reloop = proc.addBlock();
     14509        BasicBlock* done = proc.addBlock();
     14510       
     14511        Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     14512        root->appendNew<Value>(proc, Jump, Origin());
     14513        root->setSuccessors(reloop);
     14514       
     14515        reloop->appendNew<Value>(
     14516            proc, Branch, Origin(),
     14517            reloop->appendNew<AtomicValue>(
     14518                proc, AtomicWeakCAS, Origin(), width,
     14519                reloop->appendIntConstant(proc, Origin(), type, 42),
     14520                reloop->appendIntConstant(proc, Origin(), type, 0xbeef),
     14521                ptr, 0, HeapRange(42), HeapRange()));
     14522        reloop->setSuccessors(done, reloop);
     14523       
     14524        done->appendNew<Value>(proc, Return, Origin());
     14525       
     14526        auto code = compile(proc);
     14527        T value[2];
     14528        value[0] = 42;
     14529        value[1] = 13;
     14530        invoke<void>(*code, value);
     14531        CHECK_EQ(value[0], static_cast<T>(0xbeef));
     14532        CHECK_EQ(value[1], 13);
     14533        checkMyDisassembly(*code, false);
     14534    }
     14535   
     14536    {
     14537        Procedure proc;
     14538        BasicBlock* root = proc.addBlock();
     14539        BasicBlock* succ = proc.addBlock();
     14540        BasicBlock* fail = proc.addBlock();
     14541       
     14542        Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     14543        root->appendNew<Value>(
     14544            proc, Branch, Origin(),
     14545            root->appendNew<AtomicValue>(
     14546                proc, AtomicWeakCAS, Origin(), width,
     14547                root->appendIntConstant(proc, Origin(), type, 42),
     14548                root->appendIntConstant(proc, Origin(), type, 0xbeef),
     14549                ptr));
     14550        root->setSuccessors(succ, fail);
     14551       
     14552        succ->appendNew<MemoryValue>(
     14553            proc, storeOpcode(GP, width), Origin(),
     14554            succ->appendIntConstant(proc, Origin(), type, 100),
     14555            ptr);
     14556        succ->appendNew<Value>(proc, Return, Origin());
     14557       
     14558        fail->appendNew<Value>(proc, Return, Origin());
     14559       
     14560        auto code = compile(proc);
     14561        T value[2];
     14562        value[0] = 42;
     14563        value[1] = 13;
     14564        while (value[0] == 42)
     14565            invoke<void>(*code, value);
     14566        CHECK_EQ(value[0], static_cast<T>(100));
     14567        CHECK_EQ(value[1], 13);
     14568        value[0] = static_cast<T>(300);
     14569        invoke<void>(*code, value);
     14570        CHECK_EQ(value[0], static_cast<T>(300));
     14571        CHECK_EQ(value[1], 13);
     14572        checkMyDisassembly(*code, true);
     14573    }
     14574   
     14575    {
     14576        Procedure proc;
     14577        BasicBlock* root = proc.addBlock();
     14578        BasicBlock* succ = proc.addBlock();
     14579        BasicBlock* fail = proc.addBlock();
     14580       
     14581        Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     14582        root->appendNew<Value>(
     14583            proc, Branch, Origin(),
     14584            root->appendNew<Value>(
     14585                proc, Equal, Origin(),
     14586                root->appendNew<AtomicValue>(
     14587                    proc, AtomicWeakCAS, Origin(), width,
     14588                    root->appendIntConstant(proc, Origin(), type, 42),
     14589                    root->appendIntConstant(proc, Origin(), type, 0xbeef),
     14590                    ptr),
     14591                root->appendIntConstant(proc, Origin(), Int32, 0)));
     14592        root->setSuccessors(fail, succ);
     14593       
     14594        succ->appendNew<MemoryValue>(
     14595            proc, storeOpcode(GP, width), Origin(),
     14596            succ->appendIntConstant(proc, Origin(), type, 100),
     14597            ptr);
     14598        succ->appendNew<Value>(proc, Return, Origin());
     14599       
     14600        fail->appendNew<Value>(proc, Return, Origin());
     14601       
     14602        auto code = compile(proc);
     14603        T value[2];
     14604        value[0] = 42;
     14605        value[1] = 13;
     14606        while (value[0] == 42)
     14607            invoke<void>(*code, value);
     14608        CHECK_EQ(value[0], static_cast<T>(100));
     14609        CHECK_EQ(value[1], 13);
     14610        value[0] = static_cast<T>(300);
     14611        invoke<void>(*code, value);
     14612        CHECK_EQ(value[0], static_cast<T>(300));
     14613        CHECK_EQ(value[1], 13);
     14614        checkMyDisassembly(*code, true);
     14615    }
     14616   
     14617    {
     14618        Procedure proc;
     14619        BasicBlock* root = proc.addBlock();
     14620        root->appendNew<Value>(
     14621            proc, Return, Origin(),
     14622            root->appendNew<AtomicValue>(
     14623                proc, AtomicWeakCAS, Origin(), width,
     14624                root->appendIntConstant(proc, Origin(), type, 42),
     14625                root->appendIntConstant(proc, Origin(), type, 0xbeef),
     14626                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
     14627       
     14628        auto code = compile(proc);
     14629        T value[2];
     14630        value[0] = 42;
     14631        value[1] = 13;
     14632        while (!invoke<bool>(*code, value)) { }
     14633        CHECK_EQ(value[0], static_cast<T>(0xbeef));
     14634        CHECK_EQ(value[1], 13);
     14635       
     14636        value[0] = static_cast<T>(300);
     14637        CHECK(!invoke<bool>(*code, value));
     14638        CHECK_EQ(value[0], static_cast<T>(300));
     14639        CHECK_EQ(value[1], 13);
     14640        checkMyDisassembly(*code, true);
     14641    }
     14642   
     14643    {
     14644        Procedure proc;
     14645        BasicBlock* root = proc.addBlock();
     14646        root->appendNew<Value>(
     14647            proc, Return, Origin(),
     14648            root->appendNew<Value>(
     14649                proc, Equal, Origin(),
     14650                root->appendNew<AtomicValue>(
     14651                    proc, AtomicWeakCAS, Origin(), width,
     14652                    root->appendIntConstant(proc, Origin(), type, 42),
     14653                    root->appendIntConstant(proc, Origin(), type, 0xbeef),
     14654                    root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
     14655                root->appendNew<Const32Value>(proc, Origin(), 0)));
     14656       
     14657        auto code = compile(proc);
     14658        T value[2];
     14659        value[0] = 42;
     14660        value[1] = 13;
     14661        while (invoke<bool>(*code, value)) { }
     14662        CHECK_EQ(value[0], static_cast<T>(0xbeef));
     14663        CHECK_EQ(value[1], 13);
     14664       
     14665        value[0] = static_cast<T>(300);
     14666        CHECK(invoke<bool>(*code, value));
     14667        CHECK_EQ(value[0], static_cast<T>(300));
     14668        CHECK_EQ(value[1], 13);
     14669        checkMyDisassembly(*code, true);
     14670    }
     14671   
     14672    {
     14673        Procedure proc;
     14674        BasicBlock* root = proc.addBlock();
     14675        root->appendNew<Value>(
     14676            proc, Return, Origin(),
     14677            root->appendNew<AtomicValue>(
     14678                proc, AtomicWeakCAS, Origin(), width,
     14679                root->appendIntConstant(proc, Origin(), type, 42),
     14680                root->appendIntConstant(proc, Origin(), type, 0xbeef),
     14681                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
     14682                42));
     14683       
     14684        auto code = compile(proc);
     14685        T value[2];
     14686        value[0] = 42;
     14687        value[1] = 13;
     14688        while (!invoke<bool>(*code, bitwise_cast<intptr_t>(value) - 42)) { }
     14689        CHECK_EQ(value[0], static_cast<T>(0xbeef));
     14690        CHECK_EQ(value[1], 13);
     14691       
     14692        value[0] = static_cast<T>(300);
     14693        CHECK(!invoke<bool>(*code, bitwise_cast<intptr_t>(value) - 42));
     14694        CHECK_EQ(value[0], static_cast<T>(300));
     14695        CHECK_EQ(value[1], 13);
     14696        checkMyDisassembly(*code, true);
     14697    }
     14698}
     14699
     14700template<typename T>
     14701void testAtomicStrongCAS()
     14702{
     14703    Type type = NativeTraits<T>::type;
     14704    Width width = NativeTraits<T>::width;
     14705   
     14706    auto checkMyDisassembly = [&] (Compilation& compilation, bool fenced) {
     14707        if (isX86()) {
     14708            checkUsesInstruction(compilation, "lock");
     14709            checkUsesInstruction(compilation, "cmpxchg");
     14710        } else {
     14711            if (fenced) {
     14712                checkUsesInstruction(compilation, "ldax");
     14713                checkUsesInstruction(compilation, "stlx");
     14714            } else {
     14715                checkUsesInstruction(compilation, "ldx");
     14716                checkUsesInstruction(compilation, "stx");
     14717            }
     14718        }
     14719    };
     14720   
     14721    {
     14722        Procedure proc;
     14723        BasicBlock* root = proc.addBlock();
     14724        BasicBlock* succ = proc.addBlock();
     14725        BasicBlock* fail = proc.addBlock();
     14726       
     14727        Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     14728        root->appendNew<Value>(
     14729            proc, Branch, Origin(),
     14730            root->appendNew<Value>(
     14731                proc, Equal, Origin(),
     14732                root->appendNew<AtomicValue>(
     14733                    proc, AtomicStrongCAS, Origin(), width,
     14734                    root->appendIntConstant(proc, Origin(), type, 42),
     14735                    root->appendIntConstant(proc, Origin(), type, 0xbeef),
     14736                    ptr),
     14737                root->appendIntConstant(proc, Origin(), type, 42)));
     14738        root->setSuccessors(succ, fail);
     14739       
     14740        succ->appendNew<MemoryValue>(
     14741            proc, storeOpcode(GP, width), Origin(),
     14742            succ->appendIntConstant(proc, Origin(), type, 100),
     14743            ptr);
     14744        succ->appendNew<Value>(proc, Return, Origin());
     14745       
     14746        fail->appendNew<Value>(proc, Return, Origin());
     14747       
     14748        auto code = compile(proc);
     14749        T value[2];
     14750        value[0] = 42;
     14751        value[1] = 13;
     14752        invoke<void>(*code, value);
     14753        CHECK_EQ(value[0], static_cast<T>(100));
     14754        CHECK_EQ(value[1], 13);
     14755        value[0] = static_cast<T>(300);
     14756        invoke<void>(*code, value);
     14757        CHECK_EQ(value[0], static_cast<T>(300));
     14758        CHECK_EQ(value[1], 13);
     14759        checkMyDisassembly(*code, true);
     14760    }
     14761   
     14762    {
     14763        Procedure proc;
     14764        BasicBlock* root = proc.addBlock();
     14765        BasicBlock* succ = proc.addBlock();
     14766        BasicBlock* fail = proc.addBlock();
     14767       
     14768        Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     14769        root->appendNew<Value>(
     14770            proc, Branch, Origin(),
     14771            root->appendNew<Value>(
     14772                proc, Equal, Origin(),
     14773                root->appendNew<AtomicValue>(
     14774                    proc, AtomicStrongCAS, Origin(), width,
     14775                    root->appendIntConstant(proc, Origin(), type, 42),
     14776                    root->appendIntConstant(proc, Origin(), type, 0xbeef),
     14777                    ptr, 0, HeapRange(42), HeapRange()),
     14778                root->appendIntConstant(proc, Origin(), type, 42)));
     14779        root->setSuccessors(succ, fail);
     14780       
     14781        succ->appendNew<MemoryValue>(
     14782            proc, storeOpcode(GP, width), Origin(),
     14783            succ->appendIntConstant(proc, Origin(), type, 100),
     14784            ptr);
     14785        succ->appendNew<Value>(proc, Return, Origin());
     14786       
     14787        fail->appendNew<Value>(proc, Return, Origin());
     14788       
     14789        auto code = compile(proc);
     14790        T value[2];
     14791        value[0] = 42;
     14792        value[1] = 13;
     14793        invoke<void>(*code, value);
     14794        CHECK_EQ(value[0], static_cast<T>(100));
     14795        CHECK_EQ(value[1], 13);
     14796        value[0] = static_cast<T>(300);
     14797        invoke<void>(*code, value);
     14798        CHECK_EQ(value[0], static_cast<T>(300));
     14799        CHECK_EQ(value[1], 13);
     14800        checkMyDisassembly(*code, false);
     14801    }
     14802   
     14803    {
     14804        Procedure proc;
     14805        BasicBlock* root = proc.addBlock();
     14806        BasicBlock* succ = proc.addBlock();
     14807        BasicBlock* fail = proc.addBlock();
     14808       
     14809        Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     14810        root->appendNew<Value>(
     14811            proc, Branch, Origin(),
     14812            root->appendNew<Value>(
     14813                proc, NotEqual, Origin(),
     14814                root->appendNew<AtomicValue>(
     14815                    proc, AtomicStrongCAS, Origin(), width,
     14816                    root->appendIntConstant(proc, Origin(), type, 42),
     14817                    root->appendIntConstant(proc, Origin(), type, 0xbeef),
     14818                    ptr),
     14819                root->appendIntConstant(proc, Origin(), type, 42)));
     14820        root->setSuccessors(fail, succ);
     14821       
     14822        succ->appendNew<MemoryValue>(
     14823            proc, storeOpcode(GP, width), Origin(),
     14824            succ->appendIntConstant(proc, Origin(), type, 100),
     14825            ptr);
     14826        succ->appendNew<Value>(proc, Return, Origin());
     14827       
     14828        fail->appendNew<Value>(proc, Return, Origin());
     14829       
     14830        auto code = compile(proc);
     14831        T value[2];
     14832        value[0] = 42;
     14833        value[1] = 13;
     14834        invoke<void>(*code, value);
     14835        CHECK_EQ(value[0], static_cast<T>(100));
     14836        CHECK_EQ(value[1], 13);
     14837        value[0] = static_cast<T>(300);
     14838        invoke<void>(*code, value);
     14839        CHECK_EQ(value[0], static_cast<T>(300));
     14840        CHECK_EQ(value[1], 13);
     14841        checkMyDisassembly(*code, true);
     14842    }
     14843   
     14844    {
     14845        Procedure proc;
     14846        BasicBlock* root = proc.addBlock();
     14847        root->appendNew<Value>(
     14848            proc, Return, Origin(),
     14849            root->appendNew<AtomicValue>(
     14850                proc, AtomicStrongCAS, Origin(), width,
     14851                root->appendIntConstant(proc, Origin(), type, 42),
     14852                root->appendIntConstant(proc, Origin(), type, 0xbeef),
     14853                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
     14854       
     14855        auto code = compile(proc);
     14856        T value[2];
     14857        value[0] = 42;
     14858        value[1] = 13;
     14859        CHECK_EQ(invoke<typename NativeTraits<T>::CanonicalType>(*code, value), 42);
     14860        CHECK_EQ(value[0], static_cast<T>(0xbeef));
     14861        CHECK_EQ(value[1], 13);
     14862        value[0] = static_cast<T>(300);
     14863        CHECK_EQ(invoke<typename NativeTraits<T>::CanonicalType>(*code, value), static_cast<typename NativeTraits<T>::CanonicalType>(static_cast<T>(300)));
     14864        CHECK_EQ(value[0], static_cast<T>(300));
     14865        CHECK_EQ(value[1], 13);
     14866        value[0] = static_cast<T>(-1);
     14867        CHECK_EQ(invoke<typename NativeTraits<T>::CanonicalType>(*code, value), static_cast<typename NativeTraits<T>::CanonicalType>(static_cast<T>(-1)));
     14868        CHECK_EQ(value[0], static_cast<T>(-1));
     14869        CHECK_EQ(value[1], 13);
     14870        checkMyDisassembly(*code, true);
     14871    }
     14872   
     14873    {
     14874        Procedure proc;
     14875        BasicBlock* root = proc.addBlock();
     14876        root->appendNew<Value>(
     14877            proc, Return, Origin(),
     14878            root->appendNew<Value>(
     14879                proc, Equal, Origin(),
     14880                root->appendNew<AtomicValue>(
     14881                    proc, AtomicStrongCAS, Origin(), width,
     14882                    root->appendIntConstant(proc, Origin(), type, 42),
     14883                    root->appendIntConstant(proc, Origin(), type, 0xbeef),
     14884                    root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
     14885                root->appendIntConstant(proc, Origin(), type, 42)));
     14886       
     14887        auto code = compile(proc);
     14888        T value[2];
     14889        value[0] = 42;
     14890        value[1] = 13;
     14891        CHECK(invoke<bool>(*code, value));
     14892        CHECK_EQ(value[0], static_cast<T>(0xbeef));
     14893        CHECK_EQ(value[1], 13);
     14894        value[0] = static_cast<T>(300);
     14895        CHECK(!invoke<bool>(*code, value));
     14896        CHECK_EQ(value[0], static_cast<T>(300));
     14897        CHECK_EQ(value[1], 13);
     14898        checkMyDisassembly(*code, true);
     14899    }
     14900   
     14901    {
     14902        Procedure proc;
     14903        BasicBlock* root = proc.addBlock();
     14904        root->appendNew<Value>(
     14905            proc, Return, Origin(),
     14906            root->appendNew<Value>(
     14907                proc, Equal, Origin(),
     14908                root->appendNew<Value>(
     14909                    proc, NotEqual, Origin(),
     14910                    root->appendNew<AtomicValue>(
     14911                        proc, AtomicStrongCAS, Origin(), width,
     14912                        root->appendIntConstant(proc, Origin(), type, 42),
     14913                        root->appendIntConstant(proc, Origin(), type, 0xbeef),
     14914                        root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
     14915                    root->appendIntConstant(proc, Origin(), type, 42)),
     14916                root->appendNew<Const32Value>(proc, Origin(), 0)));
     14917           
     14918        auto code = compile(proc);
     14919        T value[2];
     14920        value[0] = 42;
     14921        value[1] = 13;
     14922        CHECK(invoke<bool>(*code, value));
     14923        CHECK_EQ(value[0], static_cast<T>(0xbeef));
     14924        CHECK_EQ(value[1], 13);
     14925        value[0] = static_cast<T>(300);
     14926        CHECK(!invoke<bool>(*code, &value));
     14927        CHECK_EQ(value[0], static_cast<T>(300));
     14928        CHECK_EQ(value[1], 13);
     14929        checkMyDisassembly(*code, true);
     14930    }
     14931}
     14932
     14933template<typename T>
     14934void testAtomicXchg(B3::Opcode opcode)
     14935{
     14936    Type type = NativeTraits<T>::type;
     14937    Width width = NativeTraits<T>::width;
     14938   
     14939    auto doTheMath = [&] (T& memory, T operand) -> T {
     14940        T oldValue = memory;
     14941        switch (opcode) {
     14942        case AtomicXchgAdd:
     14943            memory += operand;
     14944            break;
     14945        case AtomicXchgAnd:
     14946            memory &= operand;
     14947            break;
     14948        case AtomicXchgOr:
     14949            memory |= operand;
     14950            break;
     14951        case AtomicXchgSub:
     14952            memory -= operand;
     14953            break;
     14954        case AtomicXchgXor:
     14955            memory ^= operand;
     14956            break;
     14957        case AtomicXchg:
     14958            memory = operand;
     14959            break;
     14960        default:
     14961            RELEASE_ASSERT_NOT_REACHED();
     14962        }
     14963        return oldValue;
     14964    };
     14965   
     14966    auto oldValue = [&] (T memory, T operand) -> T {
     14967        return doTheMath(memory, operand);
     14968    };
     14969   
     14970    auto newValue = [&] (T memory, T operand) -> T {
     14971        doTheMath(memory, operand);
     14972        return memory;
     14973    };
     14974   
     14975    auto checkMyDisassembly = [&] (Compilation& compilation, bool fenced) {
     14976        if (isX86())
     14977            checkUsesInstruction(compilation, "lock");
     14978        else {
     14979            if (fenced) {
     14980                checkUsesInstruction(compilation, "ldax");
     14981                checkUsesInstruction(compilation, "stlx");
     14982            } else {
     14983                checkUsesInstruction(compilation, "ldx");
     14984                checkUsesInstruction(compilation, "stx");
     14985            }
     14986        }
     14987    };
     14988   
     14989    {
     14990        Procedure proc;
     14991        BasicBlock* root = proc.addBlock();
     14992        root->appendNew<Value>(
     14993            proc, Return, Origin(),
     14994            root->appendNew<AtomicValue>(
     14995                proc, opcode, Origin(), width,
     14996                root->appendIntConstant(proc, Origin(), type, 1),
     14997                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
     14998   
     14999        auto code = compile(proc);
     15000        T value[2];
     15001        value[0] = 5;
     15002        value[1] = 100;
     15003        CHECK_EQ(invoke<T>(*code, value), oldValue(5, 1));
     15004        CHECK_EQ(value[0], newValue(5, 1));
     15005        CHECK_EQ(value[1], 100);
     15006        checkMyDisassembly(*code, true);
     15007    }
     15008   
     15009    {
     15010        Procedure proc;
     15011        BasicBlock* root = proc.addBlock();
     15012        root->appendNew<Value>(
     15013            proc, Return, Origin(),
     15014            root->appendNew<AtomicValue>(
     15015                proc, opcode, Origin(), width,
     15016                root->appendIntConstant(proc, Origin(), type, 42),
     15017                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
     15018   
     15019        auto code = compile(proc);
     15020        T value[2];
     15021        value[0] = 5;
     15022        value[1] = 100;
     15023        CHECK_EQ(invoke<T>(*code, value), oldValue(5, 42));
     15024        CHECK_EQ(value[0], newValue(5, 42));
     15025        CHECK_EQ(value[1], 100);
     15026        checkMyDisassembly(*code, true);
     15027    }
     15028   
     15029    {
     15030        Procedure proc;
     15031        BasicBlock* root = proc.addBlock();
     15032        root->appendNew<AtomicValue>(
     15033            proc, opcode, Origin(), width,
     15034            root->appendIntConstant(proc, Origin(), type, 42),
     15035            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
     15036        root->appendNew<Value>(proc, Return, Origin());
     15037   
     15038        auto code = compile(proc);
     15039        T value[2];
     15040        value[0] = 5;
     15041        value[1] = 100;
     15042        invoke<T>(*code, value);
     15043        CHECK_EQ(value[0], newValue(5, 42));
     15044        CHECK_EQ(value[1], 100);
     15045        checkMyDisassembly(*code, true);
     15046    }
     15047   
     15048    {
     15049        Procedure proc;
     15050        BasicBlock* root = proc.addBlock();
     15051        root->appendNew<AtomicValue>(
     15052            proc, opcode, Origin(), width,
     15053            root->appendIntConstant(proc, Origin(), type, 42),
     15054            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
     15055            0, HeapRange(42), HeapRange());
     15056        root->appendNew<Value>(proc, Return, Origin());
     15057   
     15058        auto code = compile(proc);
     15059        T value[2];
     15060        value[0] = 5;
     15061        value[1] = 100;
     15062        invoke<T>(*code, value);
     15063        CHECK_EQ(value[0], newValue(5, 42));
     15064        CHECK_EQ(value[1], 100);
     15065        checkMyDisassembly(*code, false);
     15066    }
     15067}
     15068
     15069void testDepend32()
     15070{
     15071    Procedure proc;
     15072    BasicBlock* root = proc.addBlock();
     15073    Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     15074    Value* first = root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), ptr, 0);
     15075    Value* second = root->appendNew<MemoryValue>(
     15076        proc, Load, Int32, Origin(),
     15077        root->appendNew<Value>(
     15078            proc, Add, Origin(), ptr,
     15079            root->appendNew<Value>(
     15080                proc, ZExt32, Origin(),
     15081                root->appendNew<Value>(proc, Depend, Origin(), first))),
     15082        4);
     15083    root->appendNew<Value>(
     15084        proc, Return, Origin(),
     15085        root->appendNew<Value>(proc, Add, Origin(), first, second));
     15086   
     15087    int32_t values[2];
     15088    values[0] = 42;
     15089    values[1] = 0xbeef;
     15090   
     15091    auto code = compile(proc);
     15092    if (isARM64())
     15093        checkUsesInstruction(*code, "eor");
     15094    else if (isX86()) {
     15095        checkDoesNotUseInstruction(*code, "mfence");
     15096        checkDoesNotUseInstruction(*code, "lock");
     15097    }
     15098    CHECK_EQ(invoke<int32_t>(*code, values), 42 + 0xbeef);
     15099}
     15100
     15101void testDepend64()
     15102{
     15103    Procedure proc;
     15104    BasicBlock* root = proc.addBlock();
     15105    Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     15106    Value* first = root->appendNew<MemoryValue>(proc, Load, Int64, Origin(), ptr, 0);
     15107    Value* second = root->appendNew<MemoryValue>(
     15108        proc, Load, Int64, Origin(),
     15109        root->appendNew<Value>(
     15110            proc, Add, Origin(), ptr,
     15111            root->appendNew<Value>(proc, Depend, Origin(), first)),
     15112        8);
     15113    root->appendNew<Value>(
     15114        proc, Return, Origin(),
     15115        root->appendNew<Value>(proc, Add, Origin(), first, second));
     15116   
     15117    int64_t values[2];
     15118    values[0] = 42;
     15119    values[1] = 0xbeef;
     15120   
     15121    auto code = compile(proc);
     15122    if (isARM64())
     15123        checkUsesInstruction(*code, "eor");
     15124    else if (isX86()) {
     15125        checkDoesNotUseInstruction(*code, "mfence");
     15126        checkDoesNotUseInstruction(*code, "lock");
     15127    }
     15128    CHECK_EQ(invoke<int64_t>(*code, values), 42 + 0xbeef);
     15129}
     15130
    1428015131void testWasmBoundsCheck(unsigned offset)
    1428115132{
     
    1437115222}
    1437215223
     15224#define RUN_NOW(test) do {                      \
     15225        if (!shouldRun(#test))                  \
     15226            break;                              \
     15227        dataLog(#test "...\n");                 \
     15228        test;                                   \
     15229        dataLog(#test ": OK!\n");               \
     15230    } while (false)
    1437315231#define RUN(test) do {                          \
    1437415232        if (!shouldRun(#test))                  \
     
    1442215280    };
    1442315281
    14424     // We run this test first because it fiddles with some
    14425     // JSC options.
    14426     testTerminalPatchpointThatNeedsToBeSpilled2();
     15282    RUN_NOW(testTerminalPatchpointThatNeedsToBeSpilled2());
    1442715283
    1442815284    RUN(test42());
    1442915285    RUN(testLoad42());
     15286    RUN(testLoadAcq42());
    1443015287    RUN(testLoadOffsetImm9Max());
    1443115288    RUN(testLoadOffsetImm9MaxPlusOne());
     
    1510015957    RUN(testNegPtr(53));
    1510115958    RUN(testStoreAddLoad32(46));
     15959    RUN(testStoreRelAddLoadAcq32(46));
    1510215960    RUN(testStoreAddLoadImm32(46));
    1510315961    RUN(testStoreAddLoad64(4600));
     15962    RUN(testStoreRelAddLoadAcq64(4600));
    1510415963    RUN(testStoreAddLoadImm64(4600));
    1510515964    RUN(testStoreAddLoad8(4, Load8Z));
     15965    RUN(testStoreRelAddLoadAcq8(4, Load8Z));
     15966    RUN(testStoreRelAddFenceLoadAcq8(4, Load8Z));
    1510615967    RUN(testStoreAddLoadImm8(4, Load8Z));
    1510715968    RUN(testStoreAddLoad8(4, Load8S));
     15969    RUN(testStoreRelAddLoadAcq8(4, Load8S));
    1510815970    RUN(testStoreAddLoadImm8(4, Load8S));
    1510915971    RUN(testStoreAddLoad16(6, Load16Z));
     15972    RUN(testStoreRelAddLoadAcq16(6, Load16Z));
    1511015973    RUN(testStoreAddLoadImm16(6, Load16Z));
    1511115974    RUN(testStoreAddLoad16(6, Load16S));
     15975    RUN(testStoreRelAddLoadAcq16(6, Load16S));
    1511215976    RUN(testStoreAddLoadImm16(6, Load16S));
    1511315977    RUN(testStoreAddLoad32Index(46));
     
    1583216696    RUN(testLoadBaseIndexShift32());
    1583316697    RUN(testOptimizeMaterialization());
     16698   
     16699    RUN(testAtomicWeakCAS<int8_t>());
     16700    RUN(testAtomicWeakCAS<int16_t>());
     16701    RUN(testAtomicWeakCAS<int32_t>());
     16702    RUN(testAtomicWeakCAS<int64_t>());
     16703    RUN(testAtomicStrongCAS<int8_t>());
     16704    RUN(testAtomicStrongCAS<int16_t>());
     16705    RUN(testAtomicStrongCAS<int32_t>());
     16706    RUN(testAtomicStrongCAS<int64_t>());
     16707    RUN(testAtomicXchg<int8_t>(AtomicXchgAdd));
     16708    RUN(testAtomicXchg<int16_t>(AtomicXchgAdd));
     16709    RUN(testAtomicXchg<int32_t>(AtomicXchgAdd));
     16710    RUN(testAtomicXchg<int64_t>(AtomicXchgAdd));
     16711    RUN(testAtomicXchg<int8_t>(AtomicXchgAnd));
     16712    RUN(testAtomicXchg<int16_t>(AtomicXchgAnd));
     16713    RUN(testAtomicXchg<int32_t>(AtomicXchgAnd));
     16714    RUN(testAtomicXchg<int64_t>(AtomicXchgAnd));
     16715    RUN(testAtomicXchg<int8_t>(AtomicXchgOr));
     16716    RUN(testAtomicXchg<int16_t>(AtomicXchgOr));
     16717    RUN(testAtomicXchg<int32_t>(AtomicXchgOr));
     16718    RUN(testAtomicXchg<int64_t>(AtomicXchgOr));
     16719    RUN(testAtomicXchg<int8_t>(AtomicXchgSub));
     16720    RUN(testAtomicXchg<int16_t>(AtomicXchgSub));
     16721    RUN(testAtomicXchg<int32_t>(AtomicXchgSub));
     16722    RUN(testAtomicXchg<int64_t>(AtomicXchgSub));
     16723    RUN(testAtomicXchg<int8_t>(AtomicXchgXor));
     16724    RUN(testAtomicXchg<int16_t>(AtomicXchgXor));
     16725    RUN(testAtomicXchg<int32_t>(AtomicXchgXor));
     16726    RUN(testAtomicXchg<int64_t>(AtomicXchgXor));
     16727    RUN(testAtomicXchg<int8_t>(AtomicXchg));
     16728    RUN(testAtomicXchg<int16_t>(AtomicXchg));
     16729    RUN(testAtomicXchg<int32_t>(AtomicXchg));
     16730    RUN(testAtomicXchg<int64_t>(AtomicXchg));
     16731    RUN(testDepend32());
     16732    RUN(testDepend64());
    1583416733
    1583516734    RUN(testWasmBoundsCheck(0));
Note: See TracChangeset for help on using the changeset viewer.