llvm.org GIT mirror llvm / 0ac5780
[WebAssembly] Support for atomic.wait / atomic.wake instructions Summary: This adds support for atomic.wait / atomic.wake instructions in the wasm thread proposal. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Differential Revision: https://reviews.llvm.org/D49395 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@338770 91177308-0d34-0410-b5e6-96231b3b80d8 Heejin Ahn 1 year, 8 months ago
7 changed file(s) with 467 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
6363
6464 // Returns LSDA address of the current function.
6565 def int_wasm_lsda : Intrinsic<[llvm_ptr_ty], [], [IntrNoMem]>;
66
67 //===----------------------------------------------------------------------===//
68 // Atomic intrinsics
69 //===----------------------------------------------------------------------===//
70
71 // wait / notify
72 def int_wasm_atomic_wait_i32 :
73 Intrinsic<[llvm_i32_ty],
74 [LLVMPointerType, llvm_i32_ty, llvm_i64_ty],
75 [IntrInaccessibleMemOrArgMemOnly, ReadOnly<0>, NoCapture<0>,
76 IntrHasSideEffects],
77 "", [SDNPMemOperand]>;
78 def int_wasm_atomic_wait_i64 :
79 Intrinsic<[llvm_i32_ty],
80 [LLVMPointerType, llvm_i64_ty, llvm_i64_ty],
81 [IntrInaccessibleMemOrArgMemOnly, ReadOnly<0>, NoCapture<0>,
82 IntrHasSideEffects],
83 "", [SDNPMemOperand]>;
84 def int_wasm_atomic_notify:
85 Intrinsic<[llvm_i64_ty], [LLVMPointerType, llvm_i64_ty],
86 [IntrInaccessibleMemOnly, NoCapture<0>, IntrHasSideEffects], "",
87 [SDNPMemOperand]>;
88
6689 }
252252 case WebAssembly::ATOMIC_RMW_CMPXCHG_I32_S:
253253 case WebAssembly::ATOMIC_RMW32_U_CMPXCHG_I64:
254254 case WebAssembly::ATOMIC_RMW32_U_CMPXCHG_I64_S:
255 case WebAssembly::ATOMIC_NOTIFY:
256 case WebAssembly::ATOMIC_NOTIFY_S:
257 case WebAssembly::ATOMIC_WAIT_I32:
258 case WebAssembly::ATOMIC_WAIT_I32_S:
255259 return 2;
256260 case WebAssembly::LOAD_I64:
257261 case WebAssembly::LOAD_I64_S:
279283 case WebAssembly::ATOMIC_RMW_XCHG_I64_S:
280284 case WebAssembly::ATOMIC_RMW_CMPXCHG_I64:
281285 case WebAssembly::ATOMIC_RMW_CMPXCHG_I64_S:
286 case WebAssembly::ATOMIC_WAIT_I64:
287 case WebAssembly::ATOMIC_WAIT_I64_S:
282288 return 3;
283289 default:
284290 llvm_unreachable("Only loads and stores have p2align values");
435435 return VT.changeVectorElementTypeToInteger();
436436
437437 return TargetLowering::getSetCCResultType(DL, C, VT);
438 }
439
440 bool WebAssemblyTargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info,
441 const CallInst &I,
442 MachineFunction &MF,
443 unsigned Intrinsic) const {
444 switch (Intrinsic) {
445 case Intrinsic::wasm_atomic_notify:
446 Info.opc = ISD::INTRINSIC_W_CHAIN;
447 Info.memVT = MVT::i32;
448 Info.ptrVal = I.getArgOperand(0);
449 Info.offset = 0;
450 Info.align = 4;
451 // atomic.notify instruction does not really load the memory specified with
452 // this argument, but MachineMemOperand should either be load or store, so
453 // we set this to a load.
454 // FIXME Volatile isn't really correct, but currently all LLVM atomic
455 // instructions are treated as volatiles in the backend, so we should be
456 // consistent. The same applies for wasm_atomic_wait intrinsics too.
457 Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad;
458 return true;
459 case Intrinsic::wasm_atomic_wait_i32:
460 Info.opc = ISD::INTRINSIC_W_CHAIN;
461 Info.memVT = MVT::i32;
462 Info.ptrVal = I.getArgOperand(0);
463 Info.offset = 0;
464 Info.align = 4;
465 Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad;
466 return true;
467 case Intrinsic::wasm_atomic_wait_i64:
468 Info.opc = ISD::INTRINSIC_W_CHAIN;
469 Info.memVT = MVT::i64;
470 Info.ptrVal = I.getArgOperand(0);
471 Info.offset = 0;
472 Info.align = 8;
473 Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad;
474 return true;
475 default:
476 return false;
477 }
438478 }
439479
440480 //===----------------------------------------------------------------------===//
6565
6666 EVT getSetCCResultType(const DataLayout &DL, LLVMContext &Context,
6767 EVT VT) const override;
68 bool getTgtMemIntrinsic(IntrinsicInfo &Info, const CallInst &I,
69 MachineFunction &MF,
70 unsigned Intrinsic) const override;
6871
6972 SDValue LowerCall(CallLoweringInfo &CLI,
7073 SmallVectorImpl &InVals) const override;
896896 ATOMIC_RMW8_U_CMPXCHG_I32, ATOMIC_RMW16_U_CMPXCHG_I32,
897897 ATOMIC_RMW8_U_CMPXCHG_I64, ATOMIC_RMW16_U_CMPXCHG_I64,
898898 ATOMIC_RMW32_U_CMPXCHG_I64>;
899 }
900
901 //===----------------------------------------------------------------------===//
902 // Atomic wait / notify
903 //===----------------------------------------------------------------------===//
904
905 let Defs = [ARGUMENTS] in {
906 let hasSideEffects = 1 in {
907 defm ATOMIC_NOTIFY :
908 I<(outs I64:$dst),
909 (ins P2Align:$p2align, offset32_op:$off, I32:$addr, I64:$count),
910 (outs), (ins P2Align:$p2align, offset32_op:$off), [],
911 "atomic.notify \t$dst, ${off}(${addr})${p2align}, $count",
912 "atomic.notify \t${off}, ${p2align}", 0xfe00>;
913 let mayLoad = 1 in {
914 defm ATOMIC_WAIT_I32 :
915 I<(outs I32:$dst),
916 (ins P2Align:$p2align, offset32_op:$off, I32:$addr, I32:$exp, I64:$timeout),
917 (outs), (ins P2Align:$p2align, offset32_op:$off), [],
918 "i32.atomic.wait \t$dst, ${off}(${addr})${p2align}, $exp, $timeout",
919 "i32.atomic.wait \t${off}, ${p2align}", 0xfe01>;
920 defm ATOMIC_WAIT_I64 :
921 I<(outs I32:$dst),
922 (ins P2Align:$p2align, offset32_op:$off, I32:$addr, I64:$exp, I64:$timeout),
923 (outs), (ins P2Align:$p2align, offset32_op:$off), [],
924 "i64.atomic.wait \t$dst, ${off}(${addr})${p2align}, $exp, $timeout",
925 "i64.atomic.wait \t${off}, ${p2align}", 0xfe02>;
926 } // mayLoad = 1
927 } // hasSideEffects = 1
928 } // Defs = [ARGUMENTS]
929
930 let Predicates = [HasAtomics] in {
931 // Select notifys with no constant offset.
932 class NotifyPatNoOffset :
933 Pat<(i64 (kind I32:$addr, I64:$count)),
934 (ATOMIC_NOTIFY 0, 0, I32:$addr, I64:$count)>;
935 def : NotifyPatNoOffset;
936
937 // Select notifys with a constant offset.
938
939 // Pattern with address + immediate offset
940 class NotifyPatImmOff :
941 Pat<(i64 (kind (operand I32:$addr, imm:$off), I64:$count)),
942 (ATOMIC_NOTIFY 0, imm:$off, I32:$addr, I64:$count)>;
943 def : NotifyPatImmOff;
944 def : NotifyPatImmOff;
945
946 class NotifyPatGlobalAddr :
947 Pat<(i64 (kind (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off)),
948 I64:$count)),
949 (ATOMIC_NOTIFY 0, tglobaladdr:$off, I32:$addr, I64:$count)>;
950 def : NotifyPatGlobalAddr;
951
952 class NotifyPatExternalSym :
953 Pat<(i64 (kind (add I32:$addr, (WebAssemblywrapper texternalsym:$off)),
954 I64:$count)),
955 (ATOMIC_NOTIFY 0, texternalsym:$off, I32:$addr, I64:$count)>;
956 def : NotifyPatExternalSym;
957
958 // Select notifys with just a constant offset.
959 class NotifyPatOffsetOnly :
960 Pat<(i64 (kind imm:$off, I64:$count)),
961 (ATOMIC_NOTIFY 0, imm:$off, (CONST_I32 0), I64:$count)>;
962 def : NotifyPatOffsetOnly;
963
964 class NotifyPatGlobalAddrOffOnly :
965 Pat<(i64 (kind (WebAssemblywrapper tglobaladdr:$off), I64:$count)),
966 (ATOMIC_NOTIFY 0, tglobaladdr:$off, (CONST_I32 0), I64:$count)>;
967 def : NotifyPatGlobalAddrOffOnly;
968
969 class NotifyPatExternSymOffOnly :
970 Pat<(i64 (kind (WebAssemblywrapper texternalsym:$off), I64:$count)),
971 (ATOMIC_NOTIFY 0, texternalsym:$off, (CONST_I32 0), I64:$count)>;
972 def : NotifyPatExternSymOffOnly;
973
974 // Select waits with no constant offset.
975 class WaitPatNoOffset :
976 Pat<(i32 (kind I32:$addr, ty:$exp, I64:$timeout)),
977 (inst 0, 0, I32:$addr, ty:$exp, I64:$timeout)>;
978 def : WaitPatNoOffset;
979 def : WaitPatNoOffset;
980
981 // Select waits with a constant offset.
982
983 // Pattern with address + immediate offset
984 class WaitPatImmOff :
985 Pat<(i32 (kind (operand I32:$addr, imm:$off), ty:$exp, I64:$timeout)),
986 (inst 0, imm:$off, I32:$addr, ty:$exp, I64:$timeout)>;
987 def : WaitPatImmOff;
988 def : WaitPatImmOff;
989 def : WaitPatImmOff;
990 def : WaitPatImmOff;
991
992 class WaitPatGlobalAddr :
993 Pat<(i32 (kind (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off)),
994 ty:$exp, I64:$timeout)),
995 (inst 0, tglobaladdr:$off, I32:$addr, ty:$exp, I64:$timeout)>;
996 def : WaitPatGlobalAddr;
997 def : WaitPatGlobalAddr;
998
999 class WaitPatExternalSym :
1000 Pat<(i32 (kind (add I32:$addr, (WebAssemblywrapper texternalsym:$off)),
1001 ty:$exp, I64:$timeout)),
1002 (inst 0, texternalsym:$off, I32:$addr, ty:$exp, I64:$timeout)>;
1003 def : WaitPatExternalSym;
1004 def : WaitPatExternalSym;
1005
1006 // Select wait_i32, ATOMIC_WAIT_I32s with just a constant offset.
1007 class WaitPatOffsetOnly :
1008 Pat<(i32 (kind imm:$off, ty:$exp, I64:$timeout)),
1009 (inst 0, imm:$off, (CONST_I32 0), ty:$exp, I64:$timeout)>;
1010 def : WaitPatOffsetOnly;
1011 def : WaitPatOffsetOnly;
1012
1013 class WaitPatGlobalAddrOffOnly :
1014 Pat<(i32 (kind (WebAssemblywrapper tglobaladdr:$off), ty:$exp, I64:$timeout)),
1015 (inst 0, tglobaladdr:$off, (CONST_I32 0), ty:$exp, I64:$timeout)>;
1016 def : WaitPatGlobalAddrOffOnly;
1017 def : WaitPatGlobalAddrOffOnly;
1018
1019 class WaitPatExternSymOffOnly :
1020 Pat<(i32 (kind (WebAssemblywrapper texternalsym:$off), ty:$exp,
1021 I64:$timeout)),
1022 (inst 0, texternalsym:$off, (CONST_I32 0), ty:$exp, I64:$timeout)>;
1023 def : WaitPatExternSymOffOnly;
1024 def : WaitPatExternSymOffOnly;
8991025 } // Predicates = [HasAtomics]
155155 case WebAssembly::ATOMIC_RMW_XOR_I64:
156156 case WebAssembly::ATOMIC_RMW_XCHG_I64:
157157 case WebAssembly::ATOMIC_RMW_CMPXCHG_I64:
158 case WebAssembly::ATOMIC_NOTIFY:
159 case WebAssembly::ATOMIC_WAIT_I32:
160 case WebAssembly::ATOMIC_WAIT_I64:
158161 RewriteP2Align(MI, WebAssembly::LoadP2AlignOperandNo);
159162 break;
160163 case WebAssembly::STORE_I32:
15241524 %u = zext i8 %old to i32
15251525 ret i32 %u
15261526 }
1527
1528 ;===----------------------------------------------------------------------------
1529 ; Waits: 32-bit
1530 ;===----------------------------------------------------------------------------
1531
1532 declare i32 @llvm.wasm.atomic.wait.i32(i32*, i32, i64)
1533
1534 ; Basic wait.
1535
1536 ; CHECK-LABEL: wait_i32_no_offset:
1537 ; CHECK: i32.atomic.wait $push0=, 0($0), $1, $2{{$}}
1538 ; CHECK-NEXT: return $pop0{{$}}
1539 define i32 @wait_i32_no_offset(i32* %p, i32 %exp, i64 %timeout) {
1540 %v = call i32 @llvm.wasm.atomic.wait.i32(i32* %p, i32 %exp, i64 %timeout)
1541 ret i32 %v
1542 }
1543
1544 ; With an nuw add, we can fold an offset.
1545
1546 ; CHECK-LABEL: wait_i32_with_folded_offset:
1547 ; CHECK: i32.atomic.wait $push0=, 24($0), $1, $2{{$}}
1548 define i32 @wait_i32_with_folded_offset(i32* %p, i32 %exp, i64 %timeout) {
1549 %q = ptrtoint i32* %p to i32
1550 %r = add nuw i32 %q, 24
1551 %s = inttoptr i32 %r to i32*
1552 %t = call i32 @llvm.wasm.atomic.wait.i32(i32* %s, i32 %exp, i64 %timeout)
1553 ret i32 %t
1554 }
1555
1556 ; With an inbounds gep, we can fold an offset.
1557
1558 ; CHECK-LABEL: wait_i32_with_folded_gep_offset:
1559 ; CHECK: i32.atomic.wait $push0=, 24($0), $1, $2{{$}}
1560 define i32 @wait_i32_with_folded_gep_offset(i32* %p, i32 %exp, i64 %timeout) {
1561 %s = getelementptr inbounds i32, i32* %p, i32 6
1562 %t = call i32 @llvm.wasm.atomic.wait.i32(i32* %s, i32 %exp, i64 %timeout)
1563 ret i32 %t
1564 }
1565
1566 ; We can't fold a negative offset though, even with an inbounds gep.
1567
1568 ; CHECK-LABEL: wait_i32_with_unfolded_gep_negative_offset:
1569 ; CHECK: i32.const $push0=, -24{{$}}
1570 ; CHECK: i32.add $push1=, $0, $pop0{{$}}
1571 ; CHECK: i32.atomic.wait $push2=, 0($pop1), $1, $2{{$}}
1572 define i32 @wait_i32_with_unfolded_gep_negative_offset(i32* %p, i32 %exp, i64 %timeout) {
1573 %s = getelementptr inbounds i32, i32* %p, i32 -6
1574 %t = call i32 @llvm.wasm.atomic.wait.i32(i32* %s, i32 %exp, i64 %timeout)
1575 ret i32 %t
1576 }
1577
1578 ; Without nuw, and even with nsw, we can't fold an offset.
1579
1580 ; CHECK-LABEL: wait_i32_with_unfolded_offset:
1581 ; CHECK: i32.const $push0=, 24{{$}}
1582 ; CHECK: i32.add $push1=, $0, $pop0{{$}}
1583 ; CHECK: i32.atomic.wait $push2=, 0($pop1), $1, $2{{$}}
1584 define i32 @wait_i32_with_unfolded_offset(i32* %p, i32 %exp, i64 %timeout) {
1585 %q = ptrtoint i32* %p to i32
1586 %r = add nsw i32 %q, 24
1587 %s = inttoptr i32 %r to i32*
1588 %t = call i32 @llvm.wasm.atomic.wait.i32(i32* %s, i32 %exp, i64 %timeout)
1589 ret i32 %t
1590 }
1591
1592 ; Without inbounds, we can't fold a gep offset.
1593
1594 ; CHECK-LABEL: wait_i32_with_unfolded_gep_offset:
1595 ; CHECK: i32.const $push0=, 24{{$}}
1596 ; CHECK: i32.add $push1=, $0, $pop0{{$}}
1597 ; CHECK: i32.atomic.wait $push2=, 0($pop1), $1, $2{{$}}
1598 define i32 @wait_i32_with_unfolded_gep_offset(i32* %p, i32 %exp, i64 %timeout) {
1599 %s = getelementptr i32, i32* %p, i32 6
1600 %t = call i32 @llvm.wasm.atomic.wait.i32(i32* %s, i32 %exp, i64 %timeout)
1601 ret i32 %t
1602 }
1603
1604 ; When waiting from a fixed address, materialize a zero.
1605
1606 ; CHECK-LABEL: wait_i32_from_numeric_address
1607 ; CHECK: i32.const $push0=, 0{{$}}
1608 ; CHECK: i32.atomic.wait $push1=, 42($pop0), $0, $1{{$}}
1609 define i32 @wait_i32_from_numeric_address(i32 %exp, i64 %timeout) {
1610 %s = inttoptr i32 42 to i32*
1611 %t = call i32 @llvm.wasm.atomic.wait.i32(i32* %s, i32 %exp, i64 %timeout)
1612 ret i32 %t
1613 }
1614
1615 ; CHECK-LABEL: wait_i32_from_global_address
1616 ; CHECK: i32.const $push0=, 0{{$}}
1617 ; CHECK: i32.atomic.wait $push1=, gv($pop0), $0, $1{{$}}
1618 define i32 @wait_i32_from_global_address(i32 %exp, i64 %timeout) {
1619 %t = call i32 @llvm.wasm.atomic.wait.i32(i32* @gv, i32 %exp, i64 %timeout)
1620 ret i32 %t
1621 }
1622
1623 ;===----------------------------------------------------------------------------
1624 ; Waits: 64-bit
1625 ;===----------------------------------------------------------------------------
1626
1627 declare i32 @llvm.wasm.atomic.wait.i64(i64*, i64, i64)
1628
1629 ; Basic wait.
1630
1631 ; CHECK-LABEL: wait_i64_no_offset:
1632 ; CHECK: i64.atomic.wait $push0=, 0($0), $1, $2{{$}}
1633 ; CHECK-NEXT: return $pop0{{$}}
1634 define i32 @wait_i64_no_offset(i64* %p, i64 %exp, i64 %timeout) {
1635 %v = call i32 @llvm.wasm.atomic.wait.i64(i64* %p, i64 %exp, i64 %timeout)
1636 ret i32 %v
1637 }
1638
1639 ; With an nuw add, we can fold an offset.
1640
1641 ; CHECK-LABEL: wait_i64_with_folded_offset:
1642 ; CHECK: i64.atomic.wait $push0=, 24($0), $1, $2{{$}}
1643 define i32 @wait_i64_with_folded_offset(i64* %p, i64 %exp, i64 %timeout) {
1644 %q = ptrtoint i64* %p to i32
1645 %r = add nuw i32 %q, 24
1646 %s = inttoptr i32 %r to i64*
1647 %t = call i32 @llvm.wasm.atomic.wait.i64(i64* %s, i64 %exp, i64 %timeout)
1648 ret i32 %t
1649 }
1650
1651 ; With an inbounds gep, we can fold an offset.
1652
1653 ; CHECK-LABEL: wait_i64_with_folded_gep_offset:
1654 ; CHECK: i64.atomic.wait $push0=, 24($0), $1, $2{{$}}
1655 define i32 @wait_i64_with_folded_gep_offset(i64* %p, i64 %exp, i64 %timeout) {
1656 %s = getelementptr inbounds i64, i64* %p, i32 3
1657 %t = call i32 @llvm.wasm.atomic.wait.i64(i64* %s, i64 %exp, i64 %timeout)
1658 ret i32 %t
1659 }
1660
1661 ; We can't fold a negative offset though, even with an inbounds gep.
1662
1663 ; CHECK-LABEL: wait_i64_with_unfolded_gep_negative_offset:
1664 ; CHECK: i32.const $push0=, -24{{$}}
1665 ; CHECK: i32.add $push1=, $0, $pop0{{$}}
1666 ; CHECK: i64.atomic.wait $push2=, 0($pop1), $1, $2{{$}}
1667 define i32 @wait_i64_with_unfolded_gep_negative_offset(i64* %p, i64 %exp, i64 %timeout) {
1668 %s = getelementptr inbounds i64, i64* %p, i32 -3
1669 %t = call i32 @llvm.wasm.atomic.wait.i64(i64* %s, i64 %exp, i64 %timeout)
1670 ret i32 %t
1671 }
1672
1673 ; Without nuw, and even with nsw, we can't fold an offset.
1674
1675 ; CHECK-LABEL: wait_i64_with_unfolded_offset:
1676 ; CHECK: i32.const $push0=, 24{{$}}
1677 ; CHECK: i32.add $push1=, $0, $pop0{{$}}
1678 ; CHECK: i64.atomic.wait $push2=, 0($pop1), $1, $2{{$}}
1679 define i32 @wait_i64_with_unfolded_offset(i64* %p, i64 %exp, i64 %timeout) {
1680 %q = ptrtoint i64* %p to i32
1681 %r = add nsw i32 %q, 24
1682 %s = inttoptr i32 %r to i64*
1683 %t = call i32 @llvm.wasm.atomic.wait.i64(i64* %s, i64 %exp, i64 %timeout)
1684 ret i32 %t
1685 }
1686
1687 ; Without inbounds, we can't fold a gep offset.
1688
1689 ; CHECK-LABEL: wait_i64_with_unfolded_gep_offset:
1690 ; CHECK: i32.const $push0=, 24{{$}}
1691 ; CHECK: i32.add $push1=, $0, $pop0{{$}}
1692 ; CHECK: i64.atomic.wait $push2=, 0($pop1), $1, $2{{$}}
1693 define i32 @wait_i64_with_unfolded_gep_offset(i64* %p, i64 %exp, i64 %timeout) {
1694 %s = getelementptr i64, i64* %p, i32 3
1695 %t = call i32 @llvm.wasm.atomic.wait.i64(i64* %s, i64 %exp, i64 %timeout)
1696 ret i32 %t
1697 }
1698
1699 ;===----------------------------------------------------------------------------
1700 ; Notifies
1701 ;===----------------------------------------------------------------------------
1702
1703 declare i64 @llvm.wasm.atomic.notify(i32*, i64)
1704
1705 ; Basic notify.
1706
1707 ; CHECK-LABEL: notify_no_offset:
1708 ; CHECK: atomic.notify $push0=, 0($0), $1{{$}}
1709 ; CHECK-NEXT: return $pop0{{$}}
1710 define i64 @notify_no_offset(i32* %p, i64 %notify_count) {
1711 %v = call i64 @llvm.wasm.atomic.notify(i32* %p, i64 %notify_count)
1712 ret i64 %v
1713 }
1714
1715 ; With an nuw add, we can fold an offset.
1716
1717 ; CHECK-LABEL: notify_with_folded_offset:
1718 ; CHECK: atomic.notify $push0=, 24($0), $1{{$}}
1719 define i64 @notify_with_folded_offset(i32* %p, i64 %notify_count) {
1720 %q = ptrtoint i32* %p to i32
1721 %r = add nuw i32 %q, 24
1722 %s = inttoptr i32 %r to i32*
1723 %t = call i64 @llvm.wasm.atomic.notify(i32* %s, i64 %notify_count)
1724 ret i64 %t
1725 }
1726
1727 ; With an inbounds gep, we can fold an offset.
1728
1729 ; CHECK-LABEL: notify_with_folded_gep_offset:
1730 ; CHECK: atomic.notify $push0=, 24($0), $1{{$}}
1731 define i64 @notify_with_folded_gep_offset(i32* %p, i64 %notify_count) {
1732 %s = getelementptr inbounds i32, i32* %p, i32 6
1733 %t = call i64 @llvm.wasm.atomic.notify(i32* %s, i64 %notify_count)
1734 ret i64 %t
1735 }
1736
1737 ; We can't fold a negative offset though, even with an inbounds gep.
1738
1739 ; CHECK-LABEL: notify_with_unfolded_gep_negative_offset:
1740 ; CHECK: i32.const $push0=, -24{{$}}
1741 ; CHECK: i32.add $push1=, $0, $pop0{{$}}
1742 ; CHECK: atomic.notify $push2=, 0($pop1), $1{{$}}
1743 define i64 @notify_with_unfolded_gep_negative_offset(i32* %p, i64 %notify_count) {
1744 %s = getelementptr inbounds i32, i32* %p, i32 -6
1745 %t = call i64 @llvm.wasm.atomic.notify(i32* %s, i64 %notify_count)
1746 ret i64 %t
1747 }
1748
1749 ; Without nuw, and even with nsw, we can't fold an offset.
1750
1751 ; CHECK-LABEL: notify_with_unfolded_offset:
1752 ; CHECK: i32.const $push0=, 24{{$}}
1753 ; CHECK: i32.add $push1=, $0, $pop0{{$}}
1754 ; CHECK: atomic.notify $push2=, 0($pop1), $1{{$}}
1755 define i64 @notify_with_unfolded_offset(i32* %p, i64 %notify_count) {
1756 %q = ptrtoint i32* %p to i32
1757 %r = add nsw i32 %q, 24
1758 %s = inttoptr i32 %r to i32*
1759 %t = call i64 @llvm.wasm.atomic.notify(i32* %s, i64 %notify_count)
1760 ret i64 %t
1761 }
1762
1763 ; Without inbounds, we can't fold a gep offset.
1764
1765 ; CHECK-LABEL: notify_with_unfolded_gep_offset:
1766 ; CHECK: i32.const $push0=, 24{{$}}
1767 ; CHECK: i32.add $push1=, $0, $pop0{{$}}
1768 ; CHECK: atomic.notify $push2=, 0($pop1), $1{{$}}
1769 define i64 @notify_with_unfolded_gep_offset(i32* %p, i64 %notify_count) {
1770 %s = getelementptr i32, i32* %p, i32 6
1771 %t = call i64 @llvm.wasm.atomic.notify(i32* %s, i64 %notify_count)
1772 ret i64 %t
1773 }
1774
1775 ; When notifying from a fixed address, materialize a zero.
1776
1777 ; CHECK-LABEL: notify_from_numeric_address
1778 ; CHECK: i32.const $push0=, 0{{$}}
1779 ; CHECK: atomic.notify $push1=, 42($pop0), $0{{$}}
1780 define i64 @notify_from_numeric_address(i64 %notify_count) {
1781 %s = inttoptr i32 42 to i32*
1782 %t = call i64 @llvm.wasm.atomic.notify(i32* %s, i64 %notify_count)
1783 ret i64 %t
1784 }
1785
1786 ; CHECK-LABEL: notify_from_global_address
1787 ; CHECK: i32.const $push0=, 0{{$}}
1788 ; CHECK: atomic.notify $push1=, gv($pop0), $0{{$}}
1789 define i64 @notify_from_global_address(i64 %notify_count) {
1790 %t = call i64 @llvm.wasm.atomic.notify(i32* @gv, i64 %notify_count)
1791 ret i64 %t
1792 }