Skip to content

Commit f6871f4

Browse files
Add initial script to generate relaxed atomics spec test (WebAssembly#8220)
Part of WebAssembly#8165. test/spec/relaxed-atomics2.wast was generated by the script. Binary tests will be added in the next PR. Will add missing instructions e.g. load8, i64 instructions, etc once the approach looks good. The ruff warning for E741 probably isn't triggering anymore, but I chose to disable it because it disallows variables named `l` because it could be confused with `I` which I think isn't relevant in text editors.
1 parent 31c4bdf commit f6871f4

File tree

4 files changed

+154
-0
lines changed

4 files changed

+154
-0
lines changed

.ruff.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ ignore = [
3131
"B011", # https://docs.astral.sh/ruff/rules/assert-false/
3232
"B023", # https://docs.astral.sh/ruff/rules/function-uses-loop-variable/
3333
"E501", # https://docs.astral.sh/ruff/rules/line-too-long/
34+
"E741", # https://docs.astral.sh/ruff/rules/ambiguous-variable-name/
3435
"PERF401", # https://docs.astral.sh/ruff/rules/manual-list-comprehension/
3536
"PLR0912", # https://docs.astral.sh/ruff/rules/too-many-branches/
3637
"PLR0913", # https://docs.astral.sh/ruff/rules/too-many-arguments/
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import enum
2+
from dataclasses import dataclass
3+
from enum import Enum
4+
5+
# Workaround for python <3.10, escape characters can't appear in f-strings.
6+
# Although we require 3.10 in some places, the formatter complains without this.
7+
newline = "\n"
8+
9+
10+
def indent(s):
11+
return "\n".join(f" {line}" if line else "" for line in s.split("\n"))
12+
13+
14+
class ValueType(Enum):
15+
i32 = enum.auto()
16+
i64 = enum.auto()
17+
18+
19+
class Ordering(Enum):
20+
seqcst = 0
21+
acqrel = 1
22+
23+
24+
@dataclass
25+
class Template:
26+
str: str
27+
value_type: object
28+
args: int
29+
30+
31+
templates = [
32+
Template(str="(drop (i32.atomic.load %(memarg)s%(args)s))", value_type=ValueType.i32, args=1),
33+
Template(str="(drop (i64.atomic.load %(memarg)s%(args)s))", value_type=ValueType.i64, args=1),
34+
Template(str="(i32.atomic.store %(memarg)s%(args)s)", value_type=ValueType.i32, args=2),
35+
Template(str="(i64.atomic.store %(memarg)s%(args)s)", value_type=ValueType.i64, args=2),
36+
]
37+
38+
39+
def statement(template, mem_idx: str | None, ordering: Ordering | None):
40+
"""Return a statement exercising the op in `template` e.g. (i32.atomic.store 1 acqrel (i64.const 42) (i32.const 42))"""
41+
memargs = []
42+
if mem_idx is not None:
43+
memargs.append(mem_idx)
44+
if ordering is not None:
45+
memargs.append(ordering.name)
46+
47+
memarg_str = " ".join(memargs) + " " if memargs else ""
48+
idx_type = ValueType.i64 if mem_idx == "1" else ValueType.i32 if mem_idx == "0" else ValueType.i32
49+
50+
# 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).
51+
args = [f"({idx_type.name}.const 42)"] + [f"({template.value_type.name}.const 42)" for _ in range(template.args - 1)]
52+
return template.str % {"memarg": memarg_str, "args": " ".join(args)}
53+
54+
55+
def func():
56+
"""Return a func exercising all ops in `templates` e.g.
57+
(func $test-all-ops
58+
(drop (i32.atomic.load (i32.const 42)))
59+
(drop (i32.atomic.load acqrel (i32.const 42)))
60+
...
61+
)
62+
"""
63+
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]]
64+
return f''';; Memory index must come before memory ordering if present.
65+
;; Both immediates are optional; an ommitted memory ordering will be treated as seqcst.
66+
(func $test-all-ops
67+
{indent(newline.join(statements))}
68+
)'''
69+
70+
71+
def text_test():
72+
"""Return a (module ...) that exercises all ops in `templates`."""
73+
return f'''(module
74+
(memory i32 1 1)
75+
(memory i64 1 1)
76+
77+
{indent(func())}
78+
)'''
79+
80+
81+
def invalid_text_test():
82+
return '''(assert_invalid (module
83+
(memory 1 1 shared)
84+
85+
(func $i32load (drop (i32.load acqrel (i32.const 51))))
86+
) "Can't set memory ordering for non-atomic i32.load")'''
87+
88+
89+
def main():
90+
print(";; Generated by scripts/test/generate-atomic-spec-test.py. Do not edit manually.")
91+
print()
92+
print(text_test())
93+
print()
94+
print(invalid_text_test())
95+
96+
97+
if __name__ == "__main__":
98+
main()

test/spec/relaxed-atomics.wast

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
;; TODO: replace this with the script generated by scripts/test/generate-atomic-spec-test.py
2+
13
(module
24
(memory 1 1 shared)
35
(memory 1 1 shared)

test/spec/relaxed-atomics2.wast

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
;; Generated by scripts/test/generate-atomic-spec-test.py. Do not edit manually.
2+
3+
(module
4+
(memory i32 1 1)
5+
(memory i64 1 1)
6+
7+
;; Memory index must come before memory ordering if present.
8+
;; Both immediates are optional; an ommitted memory ordering will be treated as seqcst.
9+
(func $test-all-ops
10+
(drop (i32.atomic.load (i32.const 42)))
11+
(drop (i32.atomic.load acqrel (i32.const 42)))
12+
(drop (i32.atomic.load seqcst (i32.const 42)))
13+
(drop (i32.atomic.load 0 (i32.const 42)))
14+
(drop (i32.atomic.load 0 acqrel (i32.const 42)))
15+
(drop (i32.atomic.load 0 seqcst (i32.const 42)))
16+
(drop (i32.atomic.load 1 (i64.const 42)))
17+
(drop (i32.atomic.load 1 acqrel (i64.const 42)))
18+
(drop (i32.atomic.load 1 seqcst (i64.const 42)))
19+
(drop (i64.atomic.load (i32.const 42)))
20+
(drop (i64.atomic.load acqrel (i32.const 42)))
21+
(drop (i64.atomic.load seqcst (i32.const 42)))
22+
(drop (i64.atomic.load 0 (i32.const 42)))
23+
(drop (i64.atomic.load 0 acqrel (i32.const 42)))
24+
(drop (i64.atomic.load 0 seqcst (i32.const 42)))
25+
(drop (i64.atomic.load 1 (i64.const 42)))
26+
(drop (i64.atomic.load 1 acqrel (i64.const 42)))
27+
(drop (i64.atomic.load 1 seqcst (i64.const 42)))
28+
(i32.atomic.store (i32.const 42) (i32.const 42))
29+
(i32.atomic.store acqrel (i32.const 42) (i32.const 42))
30+
(i32.atomic.store seqcst (i32.const 42) (i32.const 42))
31+
(i32.atomic.store 0 (i32.const 42) (i32.const 42))
32+
(i32.atomic.store 0 acqrel (i32.const 42) (i32.const 42))
33+
(i32.atomic.store 0 seqcst (i32.const 42) (i32.const 42))
34+
(i32.atomic.store 1 (i64.const 42) (i32.const 42))
35+
(i32.atomic.store 1 acqrel (i64.const 42) (i32.const 42))
36+
(i32.atomic.store 1 seqcst (i64.const 42) (i32.const 42))
37+
(i64.atomic.store (i32.const 42) (i64.const 42))
38+
(i64.atomic.store acqrel (i32.const 42) (i64.const 42))
39+
(i64.atomic.store seqcst (i32.const 42) (i64.const 42))
40+
(i64.atomic.store 0 (i32.const 42) (i64.const 42))
41+
(i64.atomic.store 0 acqrel (i32.const 42) (i64.const 42))
42+
(i64.atomic.store 0 seqcst (i32.const 42) (i64.const 42))
43+
(i64.atomic.store 1 (i64.const 42) (i64.const 42))
44+
(i64.atomic.store 1 acqrel (i64.const 42) (i64.const 42))
45+
(i64.atomic.store 1 seqcst (i64.const 42) (i64.const 42))
46+
)
47+
)
48+
49+
(assert_invalid (module
50+
(memory 1 1 shared)
51+
52+
(func $i32load (drop (i32.load acqrel (i32.const 51))))
53+
) "Can't set memory ordering for non-atomic i32.load")

0 commit comments

Comments
 (0)