320 lines
9.1 KiB
Python
Executable file
320 lines
9.1 KiB
Python
Executable file
#!/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
|
|
"""
|
|
)
|
|
)
|