Jlm
Loading...
Searching...
No Matches
BaseHlsTests.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2026 Magnus Sjalander <work@sjalander.com>
3 * See COPYING for terms of redistribution.
4 */
5
6#include <gtest/gtest.h>
7
11
12using namespace jlm::rvsdg;
13using namespace jlm::hls;
14using namespace jlm::llvm;
15
16// Test helper class that exposes protected methods for testing
18{
19public:
20 // Expose protected methods as public for testing
27 using BaseHLS::JlmSize;
28
29 // Expose protected members as public for testing
32
33 // Override pure virtual GetText to make the class instantiable
34 std::string
36 {
37 return "";
38 }
39
40 std::string
41 extension() override
42 {
43 return ".txt";
44 }
45};
46
47// Test isForbiddenChar
49{
50 // Test all 256 possible char values.
51 // Only A-Z, a-z, 0-9, and _ are allowed; everything else is forbidden.
52 for (int i = 0; i < 256; i++)
53 {
54 char c = static_cast<char>(static_cast<unsigned char>(i));
55 bool expected =
56 !(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || c == '_');
57 EXPECT_EQ(isForbiddenChar(c), expected) << "for char value " << i;
58 }
59}
60
61// Test JlmSize with different types
63{
64 // Test with bit type
65 auto bitType = BitType::Create(32);
67
68 // Test with pointer type
70 int expectedPtrSize = sizeof(void *) * 8;
72
73 // Test with control type (returns ceil(log2(nalternatives())))
75 EXPECT_EQ(TestableBaseHLS().JlmSize(controlType.get()), 2); // ceil(log2(4)) = 2
76
77 // Test with float type (32-bit)
78 auto floatType = FloatingPointType::Create(fpsize::flt);
80
81 // Test with double type (64-bit)
82 auto doubleType = FloatingPointType::Create(fpsize::dbl);
84
85 // Test with half precision type (16-bit)
86 auto halfType = FloatingPointType::Create(fpsize::half);
88
89 // Test with array type
92 EXPECT_EQ(TestableBaseHLS().JlmSize(arrayType.get()), 128); // 32 * 4
93
94 // Test with nested array type
97 EXPECT_EQ(TestableBaseHLS().JlmSize(nestedArray.get()), 64); // 8 * 4 * 2
98
99 // Test with vector type (FixedVectorType)
102 EXPECT_EQ(TestableBaseHLS().JlmSize(vectorType.get()), 64); // 32 * 2
103
104 // Test with scalable vector type
106 EXPECT_EQ(TestableBaseHLS().JlmSize(scalableVectorType.get()), 48); // 16 * 3
107
108 // Test with state type (returns 1 for StateKind types)
111
112 // Test with bundle type (returns 0 - this is a known hack in the implementation)
113 std::vector<std::pair<std::string, std::shared_ptr<const Type>>> elements;
114 elements.emplace_back("addr", PointerType::Create());
115 elements.emplace_back("data", bitType);
116 auto bundleType = std::make_shared<jlm::hls::BundleType>(std::move(elements));
118
119 // Test that exception is thrown for unknown types
121 EXPECT_THROW(TestableBaseHLS().JlmSize(testType.get()), std::logic_error);
122}
123
124// Test get_port_name with various port types and indices
126{
127 // Arrange: Create a lambda with multiple arguments and results
130 auto & rvsdg = rvsdgModule.Rvsdg();
131
132 auto lambda = LambdaNode::Create(
133 rvsdg.GetRootRegion(),
135 FunctionType::Create({ valueType, valueType, valueType }, { valueType, valueType }),
136 "f",
137 Linkage::externalLinkage));
138
139 // Finalize the lambda to create results
140 lambda->finalize({ lambda->GetFunctionArguments()[0], lambda->GetFunctionArguments()[1] });
141
142 auto baseHls = TestableBaseHLS();
143
144 // Act & Assert: Test GraphImport (input) - should be "a0"
145 auto & import = GraphImport::Create(rvsdg, valueType, "input0");
146 EXPECT_EQ(baseHls.get_port_name(&import), "a0");
147
148 // Test NodeOutput
149 auto node = TestOperation::createNode(&rvsdg.GetRootRegion(), {}, { valueType });
150 EXPECT_EQ(baseHls.get_port_name(node->output(0)), "o0");
151
152 // Test RegionArgument with various indices
153 EXPECT_EQ(baseHls.get_port_name(lambda->GetFunctionArguments()[0]), "a0");
154 EXPECT_EQ(baseHls.get_port_name(lambda->GetFunctionArguments()[1]), "a1");
155 EXPECT_EQ(baseHls.get_port_name(lambda->GetFunctionArguments()[2]), "a2");
156
157 // Test RegionResult with various indices
158 EXPECT_EQ(baseHls.get_port_name(lambda->GetFunctionResults()[0]), "r0");
159 EXPECT_EQ(baseHls.get_port_name(lambda->GetFunctionResults()[1]), "r1");
160}
161
162// Test get_node_name and node name generation
164{
165 auto bitType = BitType::Create(32);
167 auto & rvsdg = rvsdgModule.Rvsdg();
169
170 // Test node name generation with mock entry
171 auto node1 = TestOperation::createNode(&rvsdg.GetRootRegion(), {}, { bitType });
172 baseHls.node_map[node1] = "test_node";
173 EXPECT_FALSE(baseHls.get_node_name(node1).empty());
174
175 // Test fallback when node is not in map
176 auto node2 = TestOperation::createNode(&rvsdg.GetRootRegion(), {}, { bitType });
177 auto nodeName2 = baseHls.get_node_name(node2);
178 EXPECT_FALSE(nodeName2.empty());
179 EXPECT_EQ(nodeName2.substr(0, 3), "op_");
180 EXPECT_NE(std::string::npos, nodeName2.find("TestOperation"));
181
182 // Test that multiple nodes get different names
183 auto node3 = TestOperation::createNode(&rvsdg.GetRootRegion(), {}, { bitType });
184 baseHls.node_map[node3] = "test_node";
185 EXPECT_NE(baseHls.get_node_name(node2), baseHls.get_node_name(node3));
186
187 // Test that no forbidden characters are in generated names
188 for (size_t i = 0; i < nodeName2.size(); ++i)
189 {
191 }
192}
193
194// Test get_reg_args with various argument types
196{
198 std::vector<std::pair<std::string, std::shared_ptr<const Type>>> elements;
199 elements.emplace_back("addr", PointerType::Create());
200 elements.emplace_back("data", valueType);
201 auto memResType = std::make_shared<BundleType>(std::move(elements));
203 auto & rvsdg = rvsdgModule.Rvsdg();
204
205 // Test with only register arguments
207 rvsdg.GetRootRegion(),
209 FunctionType::Create({ valueType, valueType }, { valueType }),
210 "f",
211 Linkage::externalLinkage));
213 EXPECT_EQ(regArgs.size(), 2u);
214
215 // Test with memory responses (should filter them out)
217 rvsdg.GetRootRegion(),
219 FunctionType::Create({ valueType, memResType }, { valueType }),
220 "f",
221 Linkage::externalLinkage));
223 EXPECT_EQ(regArgs.size(), 1u); // Only the first argument is in reg_args
224 EXPECT_EQ(regArgs[0], lambda2->GetFunctionArguments()[0]);
225
226 // Test with empty lambda
228 rvsdg.GetRootRegion(),
229 LlvmLambdaOperation::Create(FunctionType::Create({}, {}), "f", Linkage::externalLinkage));
231 EXPECT_TRUE(regArgs.empty());
232
233 // Test with only memory responses (no register args)
235 rvsdg.GetRootRegion(),
237 FunctionType::Create({ memResType }, {}),
238 "f",
239 Linkage::externalLinkage));
241 EXPECT_TRUE(regArgs.empty());
242
243 // Test with mixed register and memory arguments
245 rvsdg.GetRootRegion(),
247 FunctionType::Create({ valueType, memResType }, { valueType }),
248 "f",
249 Linkage::externalLinkage));
251 EXPECT_EQ(regArgs.size(), 1u);
252 EXPECT_EQ(regArgs[0], lambda5->GetFunctionArguments()[0]);
253}
254
255// Test get_reg_results with various result types
257{
259 std::vector<std::pair<std::string, std::shared_ptr<const Type>>> elements;
260 elements.emplace_back("addr", PointerType::Create());
261 auto memReqType = std::make_shared<BundleType>(std::move(elements));
263 auto & rvsdg = rvsdgModule.Rvsdg();
264
265 // Test with only register results
267 rvsdg.GetRootRegion(),
269 FunctionType::Create({ valueType }, { valueType, valueType }),
270 "f",
271 Linkage::externalLinkage));
272 lambda1->finalize({ lambda1->GetFunctionArguments()[0], lambda1->GetFunctionArguments()[0] });
274 EXPECT_EQ(regResults.size(), 2u);
275 EXPECT_EQ(regResults[0], lambda1->GetFunctionResults()[0]);
276 EXPECT_EQ(regResults[1], lambda1->GetFunctionResults()[1]);
277
278 // Test with empty lambda
280 rvsdg.GetRootRegion(),
281 LlvmLambdaOperation::Create(FunctionType::Create({}, {}), "f", Linkage::externalLinkage));
283 EXPECT_TRUE(regResults.empty());
284}
285
286// Test get_mem_reqs and get_mem_resps
288{
290 std::vector<std::pair<std::string, std::shared_ptr<const Type>>> elements;
291 elements.emplace_back("addr", PointerType::Create());
292 elements.emplace_back("data", valueType);
293 auto memReqType = std::make_shared<BundleType>(std::move(elements));
295 auto & rvsdg = rvsdgModule.Rvsdg();
296
297 // Test no memory requests
299 rvsdg.GetRootRegion(),
301 FunctionType::Create({ valueType }, { valueType }),
302 "f",
303 Linkage::externalLinkage));
305 EXPECT_TRUE(memReqs.empty());
306
307 // Test no memory responses
309 rvsdg.GetRootRegion(),
311 FunctionType::Create({ valueType }, { valueType }),
312 "f",
313 Linkage::externalLinkage));
315 EXPECT_TRUE(memResps.empty());
316
317 // Test memory response extraction with BundleType arguments
318 std::vector<std::pair<std::string, std::shared_ptr<const Type>>> respElements;
319 respElements.emplace_back("addr", PointerType::Create());
320 auto memRespType = std::make_shared<BundleType>(std::move(respElements));
321
323 rvsdg.GetRootRegion(),
325 FunctionType::Create({ memRespType }, { valueType }),
326 "f",
327 Linkage::externalLinkage));
329 EXPECT_EQ(memResps.size(), 1u);
330 EXPECT_EQ(memResps[0], lambda3->GetFunctionArguments()[0]);
331
332 // Test empty lambda with only memory responses
334 rvsdg.GetRootRegion(),
336 FunctionType::Create({ memRespType }, {}),
337 "f",
338 Linkage::externalLinkage));
341 EXPECT_TRUE(regArgs.empty());
342 EXPECT_EQ(memResps.size(), 1u);
343
344 // Test mixed register and memory arguments
346 rvsdg.GetRootRegion(),
348 FunctionType::Create({ valueType, memRespType }, { valueType }),
349 "f",
350 Linkage::externalLinkage));
353 EXPECT_EQ(regArgs.size(), 1u);
354 EXPECT_EQ(memResps.size(), 1u);
355
356 // Test multiple BundleType arguments (multiple memory responses)
357 std::vector<std::pair<std::string, std::shared_ptr<const Type>>> respElements2;
358 respElements2.emplace_back("data", BitType::Create(32));
359 auto memRespType2 = std::make_shared<BundleType>(std::move(respElements2));
360
362 rvsdg.GetRootRegion(),
364 FunctionType::Create({ memRespType, memRespType2 }, { valueType }),
365 "f",
366 Linkage::externalLinkage));
369 EXPECT_TRUE(regArgs.empty());
370 EXPECT_EQ(memResps.size(), 2u);
371}
372
373// Test port naming with many inputs/outputs to verify index counter behavior
375{
377 auto & rvsdg = rvsdgModule.Rvsdg();
378
379 auto lambda = LambdaNode::Create(
380 rvsdg.GetRootRegion(),
383 { BitType::Create(32),
384 BitType::Create(32),
385 BitType::Create(32),
386 BitType::Create(32),
387 BitType::Create(32) },
388 { TestType::createValueType(),
389 TestType::createValueType(),
390 TestType::createValueType() }),
391 "f",
392 Linkage::externalLinkage));
393
394 // Act & Assert: Verify all argument indices are unique and sequential
395 EXPECT_EQ(TestableBaseHLS().get_port_name(lambda->GetFunctionArguments()[0]), "a0");
396 EXPECT_EQ(TestableBaseHLS().get_port_name(lambda->GetFunctionArguments()[1]), "a1");
397 EXPECT_EQ(TestableBaseHLS().get_port_name(lambda->GetFunctionArguments()[2]), "a2");
398 EXPECT_EQ(TestableBaseHLS().get_port_name(lambda->GetFunctionArguments()[3]), "a3");
399 EXPECT_EQ(TestableBaseHLS().get_port_name(lambda->GetFunctionArguments()[4]), "a4");
400}
TEST(BaseHlsTests, TestIsForbiddenChar)
std::int64_t expected
std::vector< rvsdg::RegionResult * > get_mem_reqs(const rvsdg::LambdaNode &lambda)
Definition base-hls.hpp:93
std::vector< rvsdg::RegionArgument * > get_reg_args(const rvsdg::LambdaNode &lambda)
Definition base-hls.hpp:112
std::string extension() override
std::string GetText(LlvmRvsdgModule &rm) override
std::vector< rvsdg::RegionResult * > get_reg_results(const rvsdg::LambdaNode &lambda)
Definition base-hls.hpp:130
std::vector< rvsdg::RegionArgument * > get_mem_resps(const rvsdg::LambdaNode &lambda)
Definition base-hls.hpp:75
std::unordered_map< const rvsdg::Node *, std::string > node_map
Definition base-hls.hpp:44
std::vector< rvsdg::RegionResult * > get_mem_reqs(const rvsdg::LambdaNode &lambda)
Definition base-hls.hpp:93
std::vector< rvsdg::RegionArgument * > get_reg_args(const rvsdg::LambdaNode &lambda)
Definition base-hls.hpp:112
static std::string get_port_name(jlm::rvsdg::Input *port)
Definition base-hls.cpp:62
static int JlmSize(const jlm::rvsdg::Type *type)
Definition base-hls.cpp:110
std::unordered_map< jlm::rvsdg::Output *, std::string > output_map
Definition base-hls.hpp:45
std::string get_node_name(const rvsdg::Node *node)
Definition base-hls.cpp:28
std::vector< rvsdg::RegionResult * > get_reg_results(const rvsdg::LambdaNode &lambda)
Definition base-hls.hpp:130
std::vector< rvsdg::RegionArgument * > get_mem_resps(const rvsdg::LambdaNode &lambda)
Definition base-hls.hpp:75
static std::shared_ptr< const ArrayType > Create(std::shared_ptr< const Type > type, size_t nelements)
Definition types.hpp:98
static std::shared_ptr< const FixedVectorType > Create(std::shared_ptr< const rvsdg::Type > type, size_t size)
Definition types.hpp:413
static std::shared_ptr< const FloatingPointType > Create(fpsize size)
Definition types.cpp:117
static std::unique_ptr< LlvmLambdaOperation > Create(std::shared_ptr< const jlm::rvsdg::FunctionType > type, std::string name, const jlm::llvm::Linkage &linkage, jlm::llvm::CallingConvention callingConvention, jlm::llvm::AttributeSet attributes)
Definition lambda.hpp:84
static std::shared_ptr< const PointerType > Create()
Definition types.cpp:45
static std::shared_ptr< const ScalableVectorType > Create(std::shared_ptr< const rvsdg::Type > type, size_t size)
Definition types.hpp:438
static std::shared_ptr< const BitType > Create(std::size_t nbits)
Creates bit type of specified width.
Definition type.cpp:45
static std::shared_ptr< const ControlType > Create(std::size_t nalternatives)
Instantiates control type.
Definition control.cpp:50
static std::shared_ptr< const FunctionType > Create(std::vector< std::shared_ptr< const jlm::rvsdg::Type > > argumentTypes, std::vector< std::shared_ptr< const jlm::rvsdg::Type > > resultTypes)
static LambdaNode * Create(rvsdg::Region &parent, std::unique_ptr< LambdaOperation > operation)
Definition lambda.cpp:141
static SimpleNode * createNode(Region *region, const std::vector< Output * > &operands, std::vector< std::shared_ptr< const Type > > resultTypes)
static std::shared_ptr< const TestType > createStateType()
Definition TestType.cpp:60
static std::shared_ptr< const TestType > createValueType()
Definition TestType.cpp:67
int JlmSize(const jlm::rvsdg::Type *type)
Definition hls.cpp:344
bool isForbiddenChar(char c)
Definition base-hls.cpp:16
Global memory state passed between functions.
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872