diff --git a/.ruff.toml b/.ruff.toml index 69c92ac2bae..e6fd850fd43 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -31,6 +31,7 @@ ignore = [ "B011", # https://docs.astral.sh/ruff/rules/assert-false/ "B023", # https://docs.astral.sh/ruff/rules/function-uses-loop-variable/ "E501", # https://docs.astral.sh/ruff/rules/line-too-long/ + "E741", # https://docs.astral.sh/ruff/rules/ambiguous-variable-name/ "PERF401", # https://docs.astral.sh/ruff/rules/manual-list-comprehension/ "PLR0912", # https://docs.astral.sh/ruff/rules/too-many-branches/ "PLR0913", # https://docs.astral.sh/ruff/rules/too-many-arguments/ diff --git a/scripts/test/generate-atomic-spec-test.py b/scripts/test/generate-atomic-spec-test.py new file mode 100644 index 00000000000..f9da782eb71 --- /dev/null +++ b/scripts/test/generate-atomic-spec-test.py @@ -0,0 +1,98 @@ +import enum +from dataclasses import dataclass +from enum import Enum + +# Workaround for python <3.10, escape characters can't appear in f-strings. +# Although we require 3.10 in some places, the formatter complains without this. +newline = "\n" + + +def indent(s): + return "\n".join(f" {line}" if line else "" for line in s.split("\n")) + + +class ValueType(Enum): + i32 = enum.auto() + i64 = enum.auto() + + +class Ordering(Enum): + seqcst = 0 + acqrel = 1 + + +@dataclass +class Template: + str: str + value_type: object + args: int + + +templates = [ + Template(str="(drop (i32.atomic.load %(memarg)s%(args)s))", value_type=ValueType.i32, args=1), + Template(str="(drop (i64.atomic.load %(memarg)s%(args)s))", value_type=ValueType.i64, args=1), + Template(str="(i32.atomic.store %(memarg)s%(args)s)", value_type=ValueType.i32, args=2), + Template(str="(i64.atomic.store %(memarg)s%(args)s)", value_type=ValueType.i64, args=2), +] + + +def statement(template, mem_idx: str | None, ordering: Ordering | None): + """Return a statement exercising the op in `template` e.g. (i32.atomic.store 1 acqrel (i64.const 42) (i32.const 42))""" + memargs = [] + if mem_idx is not None: + memargs.append(mem_idx) + if ordering is not None: + memargs.append(ordering.name) + + memarg_str = " ".join(memargs) + " " if memargs else "" + idx_type = ValueType.i64 if mem_idx == "1" else ValueType.i32 if mem_idx == "0" else ValueType.i32 + + # The first argument (the memory location) must match the memory that we're indexing. Other arguments match the op (e.g. i32 for i32.atomic.load). + args = [f"({idx_type.name}.const 42)"] + [f"({template.value_type.name}.const 42)" for _ in range(template.args - 1)] + return template.str % {"memarg": memarg_str, "args": " ".join(args)} + + +def func(): + """Return a func exercising all ops in `templates` e.g. + (func $test-all-ops + (drop (i32.atomic.load (i32.const 42))) + (drop (i32.atomic.load acqrel (i32.const 42))) + ... + ) + """ + statements = [statement(template, mem_idx, ordering) for template in templates for mem_idx in [None, "0", "1"] for ordering in [None, Ordering.acqrel, Ordering.seqcst]] + return f''';; Memory index must come before memory ordering if present. +;; Both immediates are optional; an ommitted memory ordering will be treated as seqcst. +(func $test-all-ops +{indent(newline.join(statements))} +)''' + + +def text_test(): + """Return a (module ...) that exercises all ops in `templates`.""" + return f'''(module + (memory i32 1 1) + (memory i64 1 1) + +{indent(func())} +)''' + + +def invalid_text_test(): + return '''(assert_invalid (module + (memory 1 1 shared) + + (func $i32load (drop (i32.load acqrel (i32.const 51)))) +) "Can't set memory ordering for non-atomic i32.load")''' + + +def main(): + print(";; Generated by scripts/test/generate-atomic-spec-test.py. Do not edit manually.") + print() + print(text_test()) + print() + print(invalid_text_test()) + + +if __name__ == "__main__": + main() diff --git a/test/spec/relaxed-atomics.wast b/test/spec/relaxed-atomics.wast index 9ad4dcfaf27..c5d7635aa8c 100644 --- a/test/spec/relaxed-atomics.wast +++ b/test/spec/relaxed-atomics.wast @@ -1,3 +1,5 @@ +;; TODO: replace this with the script generated by scripts/test/generate-atomic-spec-test.py + (module (memory 1 1 shared) (memory 1 1 shared) diff --git a/test/spec/relaxed-atomics2.wast b/test/spec/relaxed-atomics2.wast new file mode 100644 index 00000000000..827c518b3b5 --- /dev/null +++ b/test/spec/relaxed-atomics2.wast @@ -0,0 +1,53 @@ +;; Generated by scripts/test/generate-atomic-spec-test.py. Do not edit manually. + +(module + (memory i32 1 1) + (memory i64 1 1) + + ;; Memory index must come before memory ordering if present. + ;; Both immediates are optional; an ommitted memory ordering will be treated as seqcst. + (func $test-all-ops + (drop (i32.atomic.load (i32.const 42))) + (drop (i32.atomic.load acqrel (i32.const 42))) + (drop (i32.atomic.load seqcst (i32.const 42))) + (drop (i32.atomic.load 0 (i32.const 42))) + (drop (i32.atomic.load 0 acqrel (i32.const 42))) + (drop (i32.atomic.load 0 seqcst (i32.const 42))) + (drop (i32.atomic.load 1 (i64.const 42))) + (drop (i32.atomic.load 1 acqrel (i64.const 42))) + (drop (i32.atomic.load 1 seqcst (i64.const 42))) + (drop (i64.atomic.load (i32.const 42))) + (drop (i64.atomic.load acqrel (i32.const 42))) + (drop (i64.atomic.load seqcst (i32.const 42))) + (drop (i64.atomic.load 0 (i32.const 42))) + (drop (i64.atomic.load 0 acqrel (i32.const 42))) + (drop (i64.atomic.load 0 seqcst (i32.const 42))) + (drop (i64.atomic.load 1 (i64.const 42))) + (drop (i64.atomic.load 1 acqrel (i64.const 42))) + (drop (i64.atomic.load 1 seqcst (i64.const 42))) + (i32.atomic.store (i32.const 42) (i32.const 42)) + (i32.atomic.store acqrel (i32.const 42) (i32.const 42)) + (i32.atomic.store seqcst (i32.const 42) (i32.const 42)) + (i32.atomic.store 0 (i32.const 42) (i32.const 42)) + (i32.atomic.store 0 acqrel (i32.const 42) (i32.const 42)) + (i32.atomic.store 0 seqcst (i32.const 42) (i32.const 42)) + (i32.atomic.store 1 (i64.const 42) (i32.const 42)) + (i32.atomic.store 1 acqrel (i64.const 42) (i32.const 42)) + (i32.atomic.store 1 seqcst (i64.const 42) (i32.const 42)) + (i64.atomic.store (i32.const 42) (i64.const 42)) + (i64.atomic.store acqrel (i32.const 42) (i64.const 42)) + (i64.atomic.store seqcst (i32.const 42) (i64.const 42)) + (i64.atomic.store 0 (i32.const 42) (i64.const 42)) + (i64.atomic.store 0 acqrel (i32.const 42) (i64.const 42)) + (i64.atomic.store 0 seqcst (i32.const 42) (i64.const 42)) + (i64.atomic.store 1 (i64.const 42) (i64.const 42)) + (i64.atomic.store 1 acqrel (i64.const 42) (i64.const 42)) + (i64.atomic.store 1 seqcst (i64.const 42) (i64.const 42)) + ) +) + +(assert_invalid (module + (memory 1 1 shared) + + (func $i32load (drop (i32.load acqrel (i32.const 51)))) +) "Can't set memory ordering for non-atomic i32.load")