OR-1 dataflow CPU sketch
1"""Tests for macro IR types (MacroParam, ParamRef, MacroDef, IRMacroCall).
2
3Tests verify:
4- MacroParam can be constructed with a name
5- ParamRef can be constructed with param, prefix, and suffix
6- MacroDef can be constructed with name, params, and body IRGraph
7- IRMacroCall can be constructed with name, positional and named args
8- IRNode.const field accepts ParamRef values
9- IRGraph.macro_defs and IRGraph.macro_calls fields can be populated
10- RegionKind.MACRO enum value exists and has correct value
11"""
12
13import pytest
14
15from asm.ir import (
16 IRGraph,
17 IRNode,
18 IREdge,
19 IRDataDef,
20 MacroParam,
21 ParamRef,
22 MacroDef,
23 IRMacroCall,
24 RegionKind,
25 SourceLoc,
26)
27from cm_inst import ArithOp, Port
28
29
30class TestMacroParam:
31 """Tests for MacroParam dataclass."""
32
33 def test_macro_param_construction(self):
34 """MacroParam can be constructed with a name."""
35 param = MacroParam(name="x")
36 assert param.name == "x"
37
38 def test_macro_param_frozen(self):
39 """MacroParam is frozen (immutable)."""
40 param = MacroParam(name="x")
41 with pytest.raises(AttributeError):
42 param.name = "y"
43
44
45class TestParamRef:
46 """Tests for ParamRef dataclass."""
47
48 def test_param_ref_basic_construction(self):
49 """ParamRef can be constructed with param name."""
50 ref = ParamRef(param="x")
51 assert ref.param == "x"
52 assert ref.prefix == ""
53 assert ref.suffix == ""
54
55 def test_param_ref_with_prefix(self):
56 """ParamRef can include an optional prefix."""
57 ref = ParamRef(param="x", prefix="pre_")
58 assert ref.param == "x"
59 assert ref.prefix == "pre_"
60 assert ref.suffix == ""
61
62 def test_param_ref_with_suffix(self):
63 """ParamRef can include an optional suffix."""
64 ref = ParamRef(param="x", suffix="_suf")
65 assert ref.param == "x"
66 assert ref.prefix == ""
67 assert ref.suffix == "_suf"
68
69 def test_param_ref_with_prefix_and_suffix(self):
70 """ParamRef can include both prefix and suffix for token pasting."""
71 ref = ParamRef(param="label", prefix="&", suffix="_out")
72 assert ref.param == "label"
73 assert ref.prefix == "&"
74 assert ref.suffix == "_out"
75
76 def test_param_ref_frozen(self):
77 """ParamRef is frozen (immutable)."""
78 ref = ParamRef(param="x")
79 with pytest.raises(AttributeError):
80 ref.param = "y"
81
82
83class TestMacroDef:
84 """Tests for MacroDef dataclass."""
85
86 def test_macro_def_basic_construction(self):
87 """MacroDef can be constructed with name, params, and body."""
88 body = IRGraph()
89 macro = MacroDef(name="loop", params=(), body=body)
90 assert macro.name == "loop"
91 assert macro.params == ()
92 assert macro.body is body
93 assert macro.loc == SourceLoc(0, 0)
94
95 def test_macro_def_with_params(self):
96 """MacroDef can include formal parameters."""
97 params = (MacroParam(name="init"), MacroParam(name="limit"))
98 body = IRGraph()
99 macro = MacroDef(name="loop_counted", params=params, body=body)
100 assert macro.name == "loop_counted"
101 assert len(macro.params) == 2
102 assert macro.params[0].name == "init"
103 assert macro.params[1].name == "limit"
104
105 def test_macro_def_with_location(self):
106 """MacroDef can include a source location."""
107 loc = SourceLoc(line=5, column=10)
108 body = IRGraph()
109 macro = MacroDef(name="test", params=(), body=body, loc=loc)
110 assert macro.loc == loc
111
112 def test_macro_def_body_with_nodes(self):
113 """MacroDef body IRGraph can contain nodes."""
114 node = IRNode(name="&add", opcode=ArithOp.ADD)
115 body = IRGraph(nodes={"&add": node})
116 macro = MacroDef(name="simple", params=(), body=body)
117 assert "&add" in macro.body.nodes
118 assert macro.body.nodes["&add"].opcode == ArithOp.ADD
119
120 def test_macro_def_frozen(self):
121 """MacroDef is frozen (immutable)."""
122 macro = MacroDef(name="test", params=(), body=IRGraph())
123 with pytest.raises(AttributeError):
124 macro.name = "other"
125
126
127class TestIRMacroCall:
128 """Tests for IRMacroCall dataclass."""
129
130 def test_macro_call_basic_construction(self):
131 """IRMacroCall can be constructed with name."""
132 call = IRMacroCall(name="loop")
133 assert call.name == "loop"
134 assert call.positional_args == ()
135 assert call.named_args == ()
136
137 def test_macro_call_with_positional_args(self):
138 """IRMacroCall can include positional arguments."""
139 args = ("&init", "&limit")
140 call = IRMacroCall(name="loop_counted", positional_args=args)
141 assert call.name == "loop_counted"
142 assert call.positional_args == args
143 assert len(call.positional_args) == 2
144
145 def test_macro_call_with_named_args(self):
146 """IRMacroCall can include named arguments."""
147 named = (("gate", "&my_gate"), ("trigger", "&my_trigger"))
148 call = IRMacroCall(name="inject", named_args=named)
149 assert call.name == "inject"
150 assert call.named_args == named
151 assert len(call.named_args) == 2
152
153 def test_macro_call_with_location(self):
154 """IRMacroCall can include a source location."""
155 loc = SourceLoc(line=12, column=5)
156 call = IRMacroCall(name="loop", loc=loc)
157 assert call.loc == loc
158
159 def test_macro_call_frozen(self):
160 """IRMacroCall is frozen (immutable)."""
161 call = IRMacroCall(name="test")
162 with pytest.raises(AttributeError):
163 call.name = "other"
164
165
166class TestIRNodeWithParamRef:
167 """Tests for IRNode accepting ParamRef in const field."""
168
169 def test_ir_node_const_with_int(self):
170 """IRNode.const can hold an integer."""
171 node = IRNode(name="&test", opcode=ArithOp.ADD, const=42)
172 assert node.const == 42
173
174 def test_ir_node_const_with_param_ref(self):
175 """IRNode.const can hold a ParamRef."""
176 ref = ParamRef(param="x")
177 node = IRNode(name="&test", opcode=ArithOp.ADD, const=ref)
178 assert isinstance(node.const, ParamRef)
179 assert node.const.param == "x"
180
181 def test_ir_node_const_with_param_ref_and_prefix(self):
182 """IRNode.const can hold a ParamRef with prefix/suffix."""
183 ref = ParamRef(param="addr", prefix="0x", suffix="00")
184 node = IRNode(name="&test", opcode=ArithOp.ADD, const=ref)
185 assert node.const.param == "addr"
186 assert node.const.prefix == "0x"
187 assert node.const.suffix == "00"
188
189 def test_ir_node_const_none(self):
190 """IRNode.const can be None."""
191 node = IRNode(name="&test", opcode=ArithOp.ADD, const=None)
192 assert node.const is None
193
194
195class TestIRGraphMacroFields:
196 """Tests for IRGraph.macro_defs and IRGraph.macro_calls fields."""
197
198 def test_ir_graph_empty_macro_defs(self):
199 """IRGraph starts with empty macro_defs list."""
200 graph = IRGraph()
201 assert graph.macro_defs == []
202
203 def test_ir_graph_empty_macro_calls(self):
204 """IRGraph starts with empty macro_calls list."""
205 graph = IRGraph()
206 assert graph.macro_calls == []
207
208 def test_ir_graph_with_macro_defs(self):
209 """IRGraph can store macro definitions."""
210 body = IRGraph()
211 macro = MacroDef(name="loop", params=(), body=body)
212 graph = IRGraph(macro_defs=[macro])
213 assert len(graph.macro_defs) == 1
214 assert graph.macro_defs[0].name == "loop"
215
216 def test_ir_graph_with_macro_calls(self):
217 """IRGraph can store macro invocations."""
218 call = IRMacroCall(name="loop", positional_args=("&src", "&dest"))
219 graph = IRGraph(macro_calls=[call])
220 assert len(graph.macro_calls) == 1
221 assert graph.macro_calls[0].name == "loop"
222
223 def test_ir_graph_with_both_macro_defs_and_calls(self):
224 """IRGraph can store both macro definitions and invocations."""
225 body = IRGraph()
226 macro_def = MacroDef(name="loop", params=(), body=body)
227 macro_call = IRMacroCall(name="loop", positional_args=("&a", "&b"))
228 graph = IRGraph(macro_defs=[macro_def], macro_calls=[macro_call])
229 assert len(graph.macro_defs) == 1
230 assert len(graph.macro_calls) == 1
231
232
233class TestRegionKindMacro:
234 """Tests for RegionKind.MACRO enum value."""
235
236 def test_region_kind_macro_exists(self):
237 """RegionKind.MACRO enum value exists."""
238 assert hasattr(RegionKind, "MACRO")
239
240 def test_region_kind_macro_value(self):
241 """RegionKind.MACRO has correct string value."""
242 assert RegionKind.MACRO.value == "macro"
243
244 def test_region_kind_macro_type(self):
245 """RegionKind.MACRO is an enum member."""
246 assert isinstance(RegionKind.MACRO, RegionKind)
247
248 def test_all_region_kinds(self):
249 """RegionKind contains expected members."""
250 kinds = {kind.name for kind in RegionKind}
251 assert "FUNCTION" in kinds
252 assert "LOCATION" in kinds
253 assert "MACRO" in kinds