| #!/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 |
| """ |
| ) |
| ) |