1#!/usr/bin/env python3 2import textwrap 3import enum 4import os 5 6""" 7Generate the tests in llvm/test/CodeGen/AArch64/Atomics. Run from top level llvm-project. 8""" 9 10TRIPLES = [ 11 "aarch64", 12 "aarch64_be", 13] 14 15 16# Type name size 17class Type(enum.Enum): 18 # Value is the size in bytes 19 i8 = 1 20 i16 = 2 21 i32 = 4 22 i64 = 8 23 i128 = 16 24 25 def align(self, aligned: bool) -> int: 26 return self.value if aligned else 1 27 28 def __str__(self) -> str: 29 return self.name 30 31 32# Is this an aligned or unaligned access? 33class Aligned(enum.Enum): 34 aligned = True 35 unaligned = False 36 37 def __str__(self) -> str: 38 return self.name 39 40 def __bool__(self) -> bool: 41 return self.value 42 43 44class AtomicOrder(enum.Enum): 45 notatomic = 0 46 unordered = 1 47 monotonic = 2 48 acquire = 3 49 release = 4 50 acq_rel = 5 51 seq_cst = 6 52 53 def __str__(self) -> str: 54 return self.name 55 56 57ATOMICRMW_ORDERS = [ 58 AtomicOrder.monotonic, 59 AtomicOrder.acquire, 60 AtomicOrder.release, 61 AtomicOrder.acq_rel, 62 AtomicOrder.seq_cst, 63] 64 65ATOMIC_LOAD_ORDERS = [ 66 AtomicOrder.unordered, 67 AtomicOrder.monotonic, 68 AtomicOrder.acquire, 69 AtomicOrder.seq_cst, 70] 71 72ATOMIC_STORE_ORDERS = [ 73 AtomicOrder.unordered, 74 AtomicOrder.monotonic, 75 AtomicOrder.release, 76 AtomicOrder.seq_cst, 77] 78 79ATOMIC_FENCE_ORDERS = [ 80 AtomicOrder.acquire, 81 AtomicOrder.release, 82 AtomicOrder.acq_rel, 83 AtomicOrder.seq_cst, 84] 85 86CMPXCHG_SUCCESS_ORDERS = [ 87 AtomicOrder.monotonic, 88 AtomicOrder.acquire, 89 AtomicOrder.release, 90 AtomicOrder.acq_rel, 91 AtomicOrder.seq_cst, 92] 93 94CMPXCHG_FAILURE_ORDERS = [ 95 AtomicOrder.monotonic, 96 AtomicOrder.acquire, 97 AtomicOrder.seq_cst, 98] 99 100FENCE_ORDERS = [ 101 AtomicOrder.acquire, 102 AtomicOrder.release, 103 AtomicOrder.acq_rel, 104 AtomicOrder.seq_cst, 105] 106 107 108class Feature(enum.Flag): 109 # Feature names in filenames are determined by the spelling here: 110 v8a = enum.auto() 111 v8_1a = enum.auto() # -mattr=+v8.1a, mandatory FEAT_LOR, FEAT_LSE 112 rcpc = enum.auto() # FEAT_LRCPC 113 lse2 = enum.auto() # FEAT_LSE2 114 outline_atomics = enum.auto() # -moutline-atomics 115 rcpc3 = enum.auto() # FEAT_LSE2 + FEAT_LRCPC3 116 lse2_lse128 = enum.auto() # FEAT_LSE2 + FEAT_LSE128 117 118 @property 119 def mattr(self): 120 if self == Feature.outline_atomics: 121 return "+outline-atomics" 122 if self == Feature.v8_1a: 123 return "+v8.1a" 124 if self == Feature.rcpc3: 125 return "+lse2,+rcpc3" 126 if self == Feature.lse2_lse128: 127 return "+lse2,+lse128" 128 return "+" + self.name 129 130 131ATOMICRMW_OPS = [ 132 "xchg", 133 "add", 134 "sub", 135 "and", 136 "nand", 137 "or", 138 "xor", 139 "max", 140 "min", 141 "umax", 142 "umin", 143] 144 145 146def all_atomicrmw(f): 147 for op in ATOMICRMW_OPS: 148 for aligned in Aligned: 149 for ty in Type: 150 for ordering in ATOMICRMW_ORDERS: 151 name = f"atomicrmw_{op}_{ty}_{aligned}_{ordering}" 152 instr = "atomicrmw" 153 f.write( 154 textwrap.dedent( 155 f""" 156 define dso_local {ty} @{name}(ptr %ptr, {ty} %value) {{ 157 %r = {instr} {op} ptr %ptr, {ty} %value {ordering}, align {ty.align(aligned)} 158 ret {ty} %r 159 }} 160 """ 161 ) 162 ) 163 164 165def all_load(f): 166 for aligned in Aligned: 167 for ty in Type: 168 for ordering in ATOMIC_LOAD_ORDERS: 169 for const in [False, True]: 170 name = f"load_atomic_{ty}_{aligned}_{ordering}" 171 instr = "load atomic" 172 if const: 173 name += "_const" 174 arg = "ptr readonly %ptr" if const else "ptr %ptr" 175 f.write( 176 textwrap.dedent( 177 f""" 178 define dso_local {ty} @{name}({arg}) {{ 179 %r = {instr} {ty}, ptr %ptr {ordering}, align {ty.align(aligned)} 180 ret {ty} %r 181 }} 182 """ 183 ) 184 ) 185 186 187def all_store(f): 188 for aligned in Aligned: 189 for ty in Type: 190 for ordering in ATOMIC_STORE_ORDERS: # FIXME stores 191 name = f"store_atomic_{ty}_{aligned}_{ordering}" 192 instr = "store atomic" 193 f.write( 194 textwrap.dedent( 195 f""" 196 define dso_local void @{name}({ty} %value, ptr %ptr) {{ 197 {instr} {ty} %value, ptr %ptr {ordering}, align {ty.align(aligned)} 198 ret void 199 }} 200 """ 201 ) 202 ) 203 204 205def all_cmpxchg(f): 206 for aligned in Aligned: 207 for ty in Type: 208 for success_ordering in CMPXCHG_SUCCESS_ORDERS: 209 for failure_ordering in CMPXCHG_FAILURE_ORDERS: 210 for weak in [False, True]: 211 name = f"cmpxchg_{ty}_{aligned}_{success_ordering}_{failure_ordering}" 212 instr = "cmpxchg" 213 if weak: 214 name += "_weak" 215 instr += " weak" 216 f.write( 217 textwrap.dedent( 218 f""" 219 define dso_local {ty} @{name}({ty} %expected, {ty} %new, ptr %ptr) {{ 220 %pair = {instr} ptr %ptr, {ty} %expected, {ty} %new {success_ordering} {failure_ordering}, align {ty.align(aligned)} 221 %r = extractvalue {{ {ty}, i1 }} %pair, 0 222 ret {ty} %r 223 }} 224 """ 225 ) 226 ) 227 228 229def all_fence(f): 230 for ordering in FENCE_ORDERS: 231 name = f"fence_{ordering}" 232 f.write( 233 textwrap.dedent( 234 f""" 235 define dso_local void @{name}() {{ 236 fence {ordering} 237 ret void 238 }} 239 """ 240 ) 241 ) 242 243 244def header(f, triple, features, filter_args: str): 245 f.write( 246 "; NOTE: Assertions have been autogenerated by " 247 "utils/update_llc_test_checks.py UTC_ARGS: " 248 ) 249 f.write(filter_args) 250 f.write("\n") 251 f.write(f"; The base test file was generated by {__file__}\n") 252 for feat in features: 253 for OptFlag in ["-O0", "-O1"]: 254 f.write( 255 " ".join( 256 [ 257 ";", 258 "RUN:", 259 "llc", 260 "%s", 261 "-o", 262 "-", 263 "-verify-machineinstrs", 264 f"-mtriple={triple}", 265 f"-mattr={feat.mattr}", 266 OptFlag, 267 "|", 268 "FileCheck", 269 "%s", 270 f"--check-prefixes=CHECK,{OptFlag}\n", 271 ] 272 ) 273 ) 274 275 276def write_lit_tests(): 277 os.chdir("llvm/test/CodeGen/AArch64/Atomics/") 278 for triple in TRIPLES: 279 # Feature has no effect on fence, so keep it to one file. 280 with open(f"{triple}-fence.ll", "w") as f: 281 filter_args = r'--filter "^\s*(dmb)"' 282 header(f, triple, Feature, filter_args) 283 all_fence(f) 284 285 for feat in Feature: 286 with open(f"{triple}-atomicrmw-{feat.name}.ll", "w") as f: 287 filter_args = r'--filter-out "\b(sp)\b" --filter "^\s*(ld[^r]|st[^r]|swp|cas|bl|add|and|eor|orn|orr|sub|mvn|sxt|cmp|ccmp|csel|dmb)"' 288 header(f, triple, [feat], filter_args) 289 all_atomicrmw(f) 290 291 with open(f"{triple}-cmpxchg-{feat.name}.ll", "w") as f: 292 filter_args = r'--filter-out "\b(sp)\b" --filter "^\s*(ld[^r]|st[^r]|swp|cas|bl|add|and|eor|orn|orr|sub|mvn|sxt|cmp|ccmp|csel|dmb)"' 293 header(f, triple, [feat], filter_args) 294 all_cmpxchg(f) 295 296 with open(f"{triple}-atomic-load-{feat.name}.ll", "w") as f: 297 filter_args = r'--filter-out "\b(sp)\b" --filter "^\s*(ld|st[^r]|swp|cas|bl|add|and|eor|orn|orr|sub|mvn|sxt|cmp|ccmp|csel|dmb)"' 298 header(f, triple, [feat], filter_args) 299 all_load(f) 300 301 with open(f"{triple}-atomic-store-{feat.name}.ll", "w") as f: 302 filter_args = r'--filter-out "\b(sp)\b" --filter "^\s*(ld[^r]|st|swp|cas|bl|add|and|eor|orn|orr|sub|mvn|sxt|cmp|ccmp|csel|dmb)"' 303 header(f, triple, [feat], filter_args) 304 all_store(f) 305 306 307if __name__ == "__main__": 308 write_lit_tests() 309 310 print( 311 textwrap.dedent( 312 """ 313 Testcases written. To update checks run: 314 $ ./llvm/utils/update_llc_test_checks.py -u llvm/test/CodeGen/AArch64/Atomics/*.ll 315 316 Or in parallel: 317 $ parallel ./llvm/utils/update_llc_test_checks.py -u ::: llvm/test/CodeGen/AArch64/Atomics/*.ll 318 """ 319 ) 320 ) 321