|
2 | 2 | # |
3 | 3 | # SPDX-License-Identifier: Apache-2.0 |
4 | 4 |
|
5 | | -from cuda.lang._enums import MemoryOrder, MemoryScope |
| 5 | +from cuda.lang._enums import FenceProxy, MemoryOrder, MemoryScope |
6 | 6 | from cuda.lang._exception import TypeCheckingError |
7 | | -from cuda.lang._ir.op_defs import InlinePTX |
| 7 | +from cuda.lang._ir.op_defs import Fence, RawNVVMIntrinsic |
| 8 | +from cuda.lang._ir.type_checking_helpers import is_none, require_pointer_type |
8 | 9 | from cuda.lang._stub import fence as fence_stub |
9 | 10 | from cuda.tile._ir.ir import add_operation_variadic |
10 | | -from cuda.tile._ir.op_impl import ImplRegistry, require_constant_enum |
| 11 | +from cuda.tile._ir.op_impl import ( |
| 12 | + ImplRegistry, |
| 13 | + require_constant_enum, |
| 14 | + require_constant_int, |
| 15 | +) |
| 16 | +from cuda.tile._ir.type import DataclassTy, EnumTy |
11 | 17 |
|
12 | 18 |
|
13 | 19 | _registry = ImplRegistry() |
14 | 20 | impl = _registry.impl |
15 | 21 |
|
16 | 22 |
|
| 23 | +_SCOPE_SUFFIX = { |
| 24 | + MemoryScope.BLOCK: "cta", |
| 25 | + MemoryScope.CLUSTER: "cluster", |
| 26 | + MemoryScope.DEVICE: "gpu", |
| 27 | + MemoryScope.SYS: "sys", |
| 28 | +} |
| 29 | + |
| 30 | +_ORDER_SUFFIX = { |
| 31 | + MemoryOrder.ACQUIRE: "acquire", |
| 32 | + MemoryOrder.RELEASE: "release", |
| 33 | + MemoryOrder.ACQ_REL: "acq_rel", |
| 34 | + MemoryOrder.SEQ_CST: "sc", |
| 35 | +} |
| 36 | + |
| 37 | +# The restrictions are spelled differently on bidirectional intrinsics, so we |
| 38 | +# can't alwyas map the shared cta enum to the same string. For example: |
| 39 | +# @llvm.nvvm.fence.proxy.async.shared_cluster |
| 40 | +# @llvm.nvvm.fence.proxy.async_generic.release.sync_restrict.space.cta.scope.cluster |
| 41 | +_RESTRICTION_SPACE_SUFFIX = { |
| 42 | + fence_stub._FenceRestrictionKind.SHARED_BLOCK: "cta", |
| 43 | + fence_stub._FenceRestrictionKind.SHARED_CLUSTER: "cluster", |
| 44 | + fence_stub._FenceRestrictionKind.GLOBAL: "global", |
| 45 | +} |
| 46 | + |
| 47 | +_BIDIRECTIONAL_RESTRICTION_SUFFIX = { |
| 48 | + fence_stub._FenceRestrictionKind.MBARRIER_INIT: "mbarrier_init", |
| 49 | + fence_stub._FenceRestrictionKind.SHARED_BLOCK: "shared_cta", |
| 50 | + fence_stub._FenceRestrictionKind.SHARED_CLUSTER: "shared_cluster", |
| 51 | + fence_stub._FenceRestrictionKind.GLOBAL: "global", |
| 52 | +} |
| 53 | + |
| 54 | + |
17 | 55 | def fence_impl_registry() -> ImplRegistry: |
18 | 56 | return _registry |
19 | 57 |
|
20 | 58 |
|
21 | | -@impl(fence_stub.fence) |
22 | | -def fence_impl(order, scope) -> None: |
23 | | - order = require_constant_enum(order, MemoryOrder) |
24 | | - scope = require_constant_enum(scope, MemoryScope) |
| 59 | +__all__ = ("fence_impl_registry",) |
25 | 60 |
|
26 | | - valid_orders = ( |
| 61 | + |
| 62 | +def require_fence_order(order): |
| 63 | + order = require_constant_enum(order, MemoryOrder) |
| 64 | + valid = ( |
27 | 65 | MemoryOrder.ACQUIRE, |
28 | 66 | MemoryOrder.RELEASE, |
29 | 67 | MemoryOrder.ACQ_REL, |
30 | 68 | MemoryOrder.SEQ_CST, |
31 | 69 | ) |
32 | | - if order not in valid_orders: |
33 | | - formatted = ", ".join(str(value) for value in valid_orders) |
| 70 | + if order not in valid: |
| 71 | + formatted = ", ".join(str(value) for value in valid) |
34 | 72 | raise TypeCheckingError( |
35 | 73 | f"Invalid fence memory order {order}, expected one of {formatted}" |
36 | 74 | ) |
| 75 | + return order |
| 76 | + |
37 | 77 |
|
38 | | - scope_suffixes = { |
39 | | - MemoryScope.BLOCK: "cta", |
40 | | - MemoryScope.CLUSTER: "cluster", |
41 | | - MemoryScope.DEVICE: "gpu", |
42 | | - MemoryScope.SYS: "sys", |
43 | | - } |
44 | | - if scope not in scope_suffixes: |
45 | | - formatted = ", ".join(str(value) for value in scope_suffixes) |
| 78 | +def require_fence_scope(scope): |
| 79 | + scope = require_constant_enum(scope, MemoryScope) |
| 80 | + valid = ( |
| 81 | + MemoryScope.BLOCK, |
| 82 | + MemoryScope.CLUSTER, |
| 83 | + MemoryScope.DEVICE, |
| 84 | + MemoryScope.SYS, |
| 85 | + ) |
| 86 | + if scope not in valid: |
| 87 | + formatted = ", ".join(str(value) for value in valid) |
46 | 88 | raise TypeCheckingError( |
47 | 89 | f"Invalid fence memory scope {scope}, expected one of {formatted}" |
48 | 90 | ) |
| 91 | + return scope |
| 92 | + |
| 93 | + |
| 94 | +def require_fence_restriction(restriction): |
| 95 | + if is_none(restriction): |
| 96 | + return None |
| 97 | + |
| 98 | + restriction_ty = restriction.get_type() |
| 99 | + if isinstance(restriction_ty, EnumTy): |
| 100 | + return require_constant_enum(restriction, fence_stub._FenceRestrictionKind) |
| 101 | + |
| 102 | + if ( |
| 103 | + isinstance(restriction_ty, DataclassTy) |
| 104 | + and restriction_ty.cls is fence_stub._FenceAddressRestriction |
| 105 | + ): |
| 106 | + restriction_value = restriction.get_aggregate() |
| 107 | + address = restriction_value.get_field("address") |
| 108 | + size = restriction_value.get_field("size") |
| 109 | + require_pointer_type(address) |
| 110 | + size_value = require_constant_int(size) |
| 111 | + if size_value != 128: |
| 112 | + raise TypeCheckingError( |
| 113 | + f"An address restriction must have size 128, got {size_value}" |
| 114 | + ) |
| 115 | + return restriction_value |
| 116 | + |
| 117 | + raise TypeCheckingError(f"Expected FenceRestriction or None, got {restriction_ty}") |
| 118 | + |
| 119 | + |
| 120 | +def lower_non_proxy_fence(order, scope, restriction): |
| 121 | + if restriction is None: |
| 122 | + add_operation_variadic(Fence, (), memory_order=order, memory_scope=scope) |
| 123 | + return |
| 124 | + |
| 125 | + order_suffix = _ORDER_SUFFIX[order] |
| 126 | + scope_suffix = _SCOPE_SUFFIX[scope] |
| 127 | + operands = () |
| 128 | + if restriction is fence_stub._FenceRestrictionKind.MBARRIER_INIT: |
| 129 | + intrinsic = f"llvm.nvvm.fence.mbarrier_init.{order_suffix}.{scope_suffix}" |
| 130 | + elif isinstance(restriction, fence_stub._FenceRestrictionKind): |
| 131 | + space_suffix = _RESTRICTION_SPACE_SUFFIX[restriction] |
| 132 | + intrinsic = ( |
| 133 | + f"llvm.nvvm.fence.{order_suffix}.sync_restrict." |
| 134 | + f"space.{space_suffix}.scope.{scope_suffix}" |
| 135 | + ) |
| 136 | + else: |
| 137 | + intrinsic = f"llvm.nvvm.fence.{order_suffix}.address.scope.{scope_suffix}" |
| 138 | + operands = ( |
| 139 | + restriction.get_field("address"), |
| 140 | + restriction.get_field("size"), |
| 141 | + ) |
49 | 142 |
|
50 | | - order_suffix = "sc" if order is MemoryOrder.SEQ_CST else order.value |
51 | 143 | add_operation_variadic( |
52 | | - InlinePTX, |
| 144 | + RawNVVMIntrinsic, |
53 | 145 | (), |
54 | | - ptx_code=f"fence.{order_suffix}.{scope_suffixes[scope]};", |
55 | | - read_only_operands=(), |
56 | | - write_only_operands=(), |
57 | | - read_write_operands=(), |
| 146 | + intrinsic=intrinsic, |
| 147 | + operands_=operands, |
58 | 148 | ) |
59 | 149 |
|
60 | 150 |
|
61 | | -__all__ = ("fence_impl_registry",) |
| 151 | +def lower_proxy_fence(order, scope, from_proxy, to_proxy, restriction): |
| 152 | + order_suffix = _ORDER_SUFFIX[order] |
| 153 | + scope_suffix = _SCOPE_SUFFIX[scope] |
| 154 | + proxy_suffix = f"{to_proxy.value}_{from_proxy.value}" |
| 155 | + operands = () |
| 156 | + if restriction is None: |
| 157 | + intrinsic = ( |
| 158 | + f"llvm.nvvm.fence.proxy.{proxy_suffix}.{order_suffix}.{scope_suffix}" |
| 159 | + ) |
| 160 | + elif not isinstance(restriction, fence_stub._FenceRestrictionKind): |
| 161 | + intrinsic = ( |
| 162 | + f"llvm.nvvm.fence.proxy.{proxy_suffix}.{order_suffix}.{scope_suffix}" |
| 163 | + ) |
| 164 | + operands = ( |
| 165 | + restriction.get_field("address"), |
| 166 | + restriction.get_field("size"), |
| 167 | + ) |
| 168 | + elif restriction is fence_stub._FenceRestrictionKind.MBARRIER_INIT: |
| 169 | + intrinsic = ( |
| 170 | + f"llvm.nvvm.fence.proxy.{proxy_suffix}.{order_suffix}." |
| 171 | + f"op_restrict.mbarrier_init.scope.{scope_suffix}" |
| 172 | + ) |
| 173 | + else: |
| 174 | + space_suffix = _RESTRICTION_SPACE_SUFFIX[restriction] |
| 175 | + intrinsic = ( |
| 176 | + f"llvm.nvvm.fence.proxy.{proxy_suffix}.{order_suffix}." |
| 177 | + f"sync_restrict.space.{space_suffix}.scope.{scope_suffix}" |
| 178 | + ) |
| 179 | + |
| 180 | + add_operation_variadic( |
| 181 | + RawNVVMIntrinsic, |
| 182 | + (), |
| 183 | + intrinsic=intrinsic, |
| 184 | + operands_=operands, |
| 185 | + ) |
| 186 | + |
| 187 | + |
| 188 | +@impl(fence_stub.fence) |
| 189 | +def fence_impl(order, scope, from_proxy, to_proxy, restriction) -> None: |
| 190 | + order = require_fence_order(order) |
| 191 | + scope = require_fence_scope(scope) |
| 192 | + from_proxy = require_constant_enum(from_proxy, FenceProxy) |
| 193 | + to_proxy = require_constant_enum(to_proxy, FenceProxy) |
| 194 | + restriction = require_fence_restriction(restriction) |
| 195 | + |
| 196 | + if from_proxy is FenceProxy.GENERIC and to_proxy is FenceProxy.GENERIC: |
| 197 | + lower_non_proxy_fence(order, scope, restriction) |
| 198 | + else: |
| 199 | + lower_proxy_fence(order, scope, from_proxy, to_proxy, restriction) |
| 200 | + |
| 201 | + |
| 202 | +@impl(fence_stub.fence_proxy_bidirectional) |
| 203 | +def fence_proxy_bidirectional_impl(proxy, restriction) -> None: |
| 204 | + proxy = require_constant_enum(proxy, FenceProxy) |
| 205 | + restriction = require_fence_restriction(restriction) |
| 206 | + |
| 207 | + operands = () |
| 208 | + if restriction is None: |
| 209 | + proxy_suffix = proxy.value |
| 210 | + elif isinstance(restriction, fence_stub._FenceRestrictionKind): |
| 211 | + restriction_suffix = _BIDIRECTIONAL_RESTRICTION_SUFFIX[restriction] |
| 212 | + proxy_suffix = f"{proxy.value}.{restriction_suffix}" |
| 213 | + else: |
| 214 | + proxy_suffix = f"{proxy.value}.address" |
| 215 | + operands = ( |
| 216 | + restriction.get_field("address"), |
| 217 | + restriction.get_field("size"), |
| 218 | + ) |
| 219 | + |
| 220 | + add_operation_variadic( |
| 221 | + RawNVVMIntrinsic, |
| 222 | + (), |
| 223 | + intrinsic=f"llvm.nvvm.fence.proxy.{proxy_suffix}", |
| 224 | + operands_=operands, |
| 225 | + ) |
0 commit comments