#!/usr/bin/env python3 import textwrap import enum import os """ Generate the tests in llvm/test/CodeGen/AArch64/Atomics. Run from top level llvm-project. """ TRIPLES = [ "aarch64", "aarch64_be", ] # Type name size class Type(enum.Enum): # Value is the size in bytes i8 = 1 i16 = 2 i32 = 4 i64 = 8 i128 = 16 def align(self, aligned: bool) -> int: return self.value if aligned else 1 def __str__(self) -> str: return self.name # Is this an aligned or unaligned access? class Aligned(enum.Enum): aligned = True unaligned = False def __str__(self) -> str: return self.name def __bool__(self) -> bool: return self.value class AtomicOrder(enum.Enum): notatomic = 0 unordered = 1 monotonic = 2 acquire = 3 release = 4 acq_rel = 5 seq_cst = 6 def __str__(self) -> str: return self.name ATOMICRMW_ORDERS = [ AtomicOrder.monotonic, AtomicOrder.acquire, AtomicOrder.release, AtomicOrder.acq_rel, AtomicOrder.seq_cst, ] ATOMIC_LOAD_ORDERS = [ AtomicOrder.unordered, AtomicOrder.monotonic, AtomicOrder.acquire, AtomicOrder.seq_cst, ] ATOMIC_STORE_ORDERS = [ AtomicOrder.unordered, AtomicOrder.monotonic, AtomicOrder.release, AtomicOrder.seq_cst, ] ATOMIC_FENCE_ORDERS = [ AtomicOrder.acquire, AtomicOrder.release, AtomicOrder.acq_rel, AtomicOrder.seq_cst, ] CMPXCHG_SUCCESS_ORDERS = [ AtomicOrder.monotonic, AtomicOrder.acquire, AtomicOrder.release, AtomicOrder.acq_rel, AtomicOrder.seq_cst, ] CMPXCHG_FAILURE_ORDERS = [ AtomicOrder.monotonic, AtomicOrder.acquire, AtomicOrder.seq_cst, ] FENCE_ORDERS = [ AtomicOrder.acquire, AtomicOrder.release, AtomicOrder.acq_rel, AtomicOrder.seq_cst, ] class Feature(enum.Flag): # Feature names in filenames are determined by the spelling here: v8a = enum.auto() v8_1a = enum.auto() # -mattr=+v8.1a, mandatory FEAT_LOR, FEAT_LSE rcpc = enum.auto() # FEAT_LRCPC lse2 = enum.auto() # FEAT_LSE2 outline_atomics = enum.auto() # -moutline-atomics rcpc3 = enum.auto() # FEAT_LSE2 + FEAT_LRCPC3 lse2_lse128 = enum.auto() # FEAT_LSE2 + FEAT_LSE128 @property def mattr(self): if self == Feature.outline_atomics: return "+outline-atomics" if self == Feature.v8_1a: return "+v8.1a" if self == Feature.rcpc3: return "+lse2,+rcpc3" if self == Feature.lse2_lse128: return "+lse2,+lse128" return "+" + self.name ATOMICRMW_OPS = [ "xchg", "add", "sub", "and", "nand", "or", "xor", "max", "min", "umax", "umin", ] def all_atomicrmw(f): for op in ATOMICRMW_OPS: for aligned in Aligned: for ty in Type: for ordering in ATOMICRMW_ORDERS: name = f"atomicrmw_{op}_{ty}_{aligned}_{ordering}" instr = "atomicrmw" f.write( textwrap.dedent( f""" define dso_local {ty} @{name}(ptr %ptr, {ty} %value) {{ %r = {instr} {op} ptr %ptr, {ty} %value {ordering}, align {ty.align(aligned)} ret {ty} %r }} """ ) ) def all_load(f): for aligned in Aligned: for ty in Type: for ordering in ATOMIC_LOAD_ORDERS: for const in [False, True]: name = f"load_atomic_{ty}_{aligned}_{ordering}" instr = "load atomic" if const: name += "_const" arg = "ptr readonly %ptr" if const else "ptr %ptr" f.write( textwrap.dedent( f""" define dso_local {ty} @{name}({arg}) {{ %r = {instr} {ty}, ptr %ptr {ordering}, align {ty.align(aligned)} ret {ty} %r }} """ ) ) def all_store(f): for aligned in Aligned: for ty in Type: for ordering in ATOMIC_STORE_ORDERS: # FIXME stores name = f"store_atomic_{ty}_{aligned}_{ordering}" instr = "store atomic" f.write( textwrap.dedent( f""" define dso_local void @{name}({ty} %value, ptr %ptr) {{ {instr} {ty} %value, ptr %ptr {ordering}, align {ty.align(aligned)} ret void }} """ ) ) def all_cmpxchg(f): for aligned in Aligned: for ty in Type: for success_ordering in CMPXCHG_SUCCESS_ORDERS: for failure_ordering in CMPXCHG_FAILURE_ORDERS: for weak in [False, True]: name = f"cmpxchg_{ty}_{aligned}_{success_ordering}_{failure_ordering}" instr = "cmpxchg" if weak: name += "_weak" instr += " weak" f.write( textwrap.dedent( f""" define dso_local {ty} @{name}({ty} %expected, {ty} %new, ptr %ptr) {{ %pair = {instr} ptr %ptr, {ty} %expected, {ty} %new {success_ordering} {failure_ordering}, align {ty.align(aligned)} %r = extractvalue {{ {ty}, i1 }} %pair, 0 ret {ty} %r }} """ ) ) def all_fence(f): for ordering in FENCE_ORDERS: name = f"fence_{ordering}" f.write( textwrap.dedent( f""" define dso_local void @{name}() {{ fence {ordering} ret void }} """ ) ) def header(f, triple, features, filter_args: str): f.write( "; NOTE: Assertions have been autogenerated by " "utils/update_llc_test_checks.py UTC_ARGS: " ) f.write(filter_args) f.write("\n") f.write(f"; The base test file was generated by {__file__}\n") for feat in features: for OptFlag in ["-O0", "-O1"]: f.write( " ".join( [ ";", "RUN:", "llc", "%s", "-o", "-", "-verify-machineinstrs", f"-mtriple={triple}", f"-mattr={feat.mattr}", OptFlag, "|", "FileCheck", "%s", f"--check-prefixes=CHECK,{OptFlag}\n", ] ) ) def write_lit_tests(): os.chdir("llvm/test/CodeGen/AArch64/Atomics/") for triple in TRIPLES: # Feature has no effect on fence, so keep it to one file. with open(f"{triple}-fence.ll", "w") as f: filter_args = r'--filter "^\s*(dmb)"' header(f, triple, Feature, filter_args) all_fence(f) for feat in Feature: with open(f"{triple}-atomicrmw-{feat.name}.ll", "w") as f: 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)"' header(f, triple, [feat], filter_args) all_atomicrmw(f) with open(f"{triple}-cmpxchg-{feat.name}.ll", "w") as f: 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)"' header(f, triple, [feat], filter_args) all_cmpxchg(f) with open(f"{triple}-atomic-load-{feat.name}.ll", "w") as f: 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)"' header(f, triple, [feat], filter_args) all_load(f) with open(f"{triple}-atomic-store-{feat.name}.ll", "w") as f: 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)"' header(f, triple, [feat], filter_args) all_store(f) if __name__ == "__main__": write_lit_tests() print( textwrap.dedent( """ Testcases written. To update checks run: $ ./llvm/utils/update_llc_test_checks.py -u llvm/test/CodeGen/AArch64/Atomics/*.ll Or in parallel: $ parallel ./llvm/utils/update_llc_test_checks.py -u ::: llvm/test/CodeGen/AArch64/Atomics/*.ll """ ) )