1 | //===- ParserTest.cpp -----------------------------------------------------===// |
2 | // |
3 | // This file is licensed under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "mlir/Parser/Parser.h" |
10 | #include "mlir/AsmParser/AsmParser.h" |
11 | #include "mlir/AsmParser/AsmParserState.h" |
12 | #include "mlir/Dialect/Func/IR/FuncOps.h" |
13 | #include "mlir/Dialect/LLVMIR/LLVMDialect.h" |
14 | #include "mlir/IR/BuiltinOps.h" |
15 | #include "mlir/IR/Verifier.h" |
16 | #include "llvm/Support/SourceMgr.h" |
17 | |
18 | #include "gmock/gmock.h" |
19 | |
20 | using namespace mlir; |
21 | |
22 | namespace { |
23 | TEST(MLIRParser, ParseInvalidIR) { |
24 | std::string moduleStr = R"mlir( |
25 | module attributes {bad} {} |
26 | )mlir" ; |
27 | |
28 | MLIRContext context; |
29 | ParserConfig config(&context, /*verifyAfterParse=*/false); |
30 | |
31 | // Check that we properly parse the op, but it fails the verifier. |
32 | OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(sourceStr: moduleStr, config); |
33 | ASSERT_TRUE(module); |
34 | ASSERT_TRUE(failed(verify(*module))); |
35 | } |
36 | |
37 | TEST(MLIRParser, ParseAtEnd) { |
38 | std::string firstModuleStr = R"mlir( |
39 | "test.first"() : () -> () |
40 | )mlir" ; |
41 | std::string secondModuleStr = R"mlir( |
42 | "test.second"() : () -> () |
43 | )mlir" ; |
44 | |
45 | MLIRContext context; |
46 | context.allowUnregisteredDialects(); |
47 | Block block; |
48 | |
49 | // Parse the first module string. |
50 | LogicalResult firstParse = |
51 | parseSourceString(sourceStr: firstModuleStr, block: &block, config: &context); |
52 | EXPECT_TRUE(succeeded(firstParse)); |
53 | |
54 | // Parse the second module string. |
55 | LogicalResult secondParse = |
56 | parseSourceString(sourceStr: secondModuleStr, block: &block, config: &context); |
57 | EXPECT_TRUE(succeeded(secondParse)); |
58 | |
59 | // Check the we parse at the end. |
60 | EXPECT_EQ(block.front().getName().getStringRef(), "test.first" ); |
61 | EXPECT_EQ(block.back().getName().getStringRef(), "test.second" ); |
62 | } |
63 | |
64 | TEST(MLIRParser, ParseAttr) { |
65 | using namespace testing; |
66 | MLIRContext context; |
67 | Builder b(&context); |
68 | { // Successful parse |
69 | StringLiteral attrAsm = "array<i64: 1, 2, 3>" ; |
70 | size_t numRead = 0; |
71 | Attribute attr = parseAttribute(attrStr: attrAsm, context: &context, type: Type(), numRead: &numRead); |
72 | EXPECT_EQ(attr, b.getDenseI64ArrayAttr({1, 2, 3})); |
73 | EXPECT_EQ(numRead, attrAsm.size()); |
74 | } |
75 | { // Failed parse |
76 | std::vector<std::string> diagnostics; |
77 | ScopedDiagnosticHandler handler(&context, [&](Diagnostic &d) { |
78 | llvm::raw_string_ostream(diagnostics.emplace_back()) |
79 | << d.getLocation() << ": " << d; |
80 | }); |
81 | size_t numRead = 0; |
82 | EXPECT_FALSE(parseAttribute("dense<>" , &context, Type(), &numRead)); |
83 | EXPECT_THAT(diagnostics, ElementsAre("loc(\"dense<>\":1:7): expected ':'" )); |
84 | EXPECT_EQ(numRead, size_t(0)); |
85 | } |
86 | { // Parse with trailing characters |
87 | std::vector<std::string> diagnostics; |
88 | ScopedDiagnosticHandler handler(&context, [&](Diagnostic &d) { |
89 | llvm::raw_string_ostream(diagnostics.emplace_back()) |
90 | << d.getLocation() << ": " << d; |
91 | }); |
92 | EXPECT_FALSE(parseAttribute("10 foo" , &context)); |
93 | EXPECT_THAT( |
94 | diagnostics, |
95 | ElementsAre("loc(\"10 foo\":1:5): found trailing characters: 'foo'" )); |
96 | |
97 | size_t numRead = 0; |
98 | EXPECT_EQ(parseAttribute("10 foo" , &context, Type(), &numRead), |
99 | b.getI64IntegerAttr(10)); |
100 | EXPECT_EQ(numRead, size_t(4)); // includes trailing whitespace |
101 | } |
102 | { // Parse without null-terminator |
103 | StringRef attrAsm("999" , 1); |
104 | Attribute attr = parseAttribute(attrStr: attrAsm, context: &context); |
105 | EXPECT_EQ(attr, b.getI64IntegerAttr(9)); |
106 | } |
107 | } |
108 | |
109 | TEST(MLIRParser, AsmParserLocations) { |
110 | std::string moduleStr = R"mlir( |
111 | func.func @foo() -> !llvm.array<2 x f32> { |
112 | %0 = llvm.mlir.undef : !llvm.array<2 x f32> |
113 | func.return %0 : !llvm.array<2 x f32> |
114 | } |
115 | )mlir" ; |
116 | |
117 | DialectRegistry registry; |
118 | registry.insert<func::FuncDialect, LLVM::LLVMDialect>(); |
119 | MLIRContext context(registry); |
120 | |
121 | auto memBuffer = |
122 | llvm::MemoryBuffer::getMemBuffer(InputData: moduleStr, BufferName: "AsmParserTest.mlir" , |
123 | /*RequiresNullTerminator=*/false); |
124 | ASSERT_TRUE(memBuffer); |
125 | |
126 | llvm::SourceMgr sourceMgr; |
127 | sourceMgr.AddNewSourceBuffer(F: std::move(memBuffer), IncludeLoc: llvm::SMLoc()); |
128 | |
129 | Block block; |
130 | AsmParserState parseState; |
131 | const LogicalResult parseResult = |
132 | parseAsmSourceFile(sourceMgr, block: &block, config: &context, asmState: &parseState); |
133 | ASSERT_TRUE(parseResult.succeeded()); |
134 | |
135 | auto funcOp = *block.getOps<func::FuncOp>().begin(); |
136 | const AsmParserState::OperationDefinition *funcOpDefinition = |
137 | parseState.getOpDef(op: funcOp); |
138 | ASSERT_TRUE(funcOpDefinition); |
139 | |
140 | const std::pair expectedStartFunc{2u, 1u}; |
141 | const std::pair expectedEndFunc{2u, 10u}; |
142 | const std::pair expectedScopeEndFunc{5u, 2u}; |
143 | ASSERT_EQ(sourceMgr.getLineAndColumn(funcOpDefinition->loc.Start), |
144 | expectedStartFunc); |
145 | ASSERT_EQ(sourceMgr.getLineAndColumn(funcOpDefinition->loc.End), |
146 | expectedEndFunc); |
147 | ASSERT_EQ(funcOpDefinition->loc.Start, funcOpDefinition->scopeLoc.Start); |
148 | ASSERT_EQ(sourceMgr.getLineAndColumn(funcOpDefinition->scopeLoc.End), |
149 | expectedScopeEndFunc); |
150 | |
151 | auto llvmUndef = *funcOp.getOps<LLVM::UndefOp>().begin(); |
152 | const AsmParserState::OperationDefinition *llvmUndefDefinition = |
153 | parseState.getOpDef(op: llvmUndef); |
154 | ASSERT_TRUE(llvmUndefDefinition); |
155 | |
156 | const std::pair expectedStartUndef{3u, 8u}; |
157 | const std::pair expectedEndUndef{3u, 23u}; |
158 | const std::pair expectedScopeEndUndef{3u, 46u}; |
159 | ASSERT_EQ(sourceMgr.getLineAndColumn(llvmUndefDefinition->loc.Start), |
160 | expectedStartUndef); |
161 | ASSERT_EQ(sourceMgr.getLineAndColumn(llvmUndefDefinition->loc.End), |
162 | expectedEndUndef); |
163 | ASSERT_EQ(llvmUndefDefinition->loc.Start, |
164 | llvmUndefDefinition->scopeLoc.Start); |
165 | ASSERT_EQ(sourceMgr.getLineAndColumn(llvmUndefDefinition->scopeLoc.End), |
166 | expectedScopeEndUndef); |
167 | |
168 | auto funcReturn = *funcOp.getOps<func::ReturnOp>().begin(); |
169 | const AsmParserState::OperationDefinition *funcReturnDefinition = |
170 | parseState.getOpDef(op: funcReturn); |
171 | ASSERT_TRUE(funcReturnDefinition); |
172 | |
173 | const std::pair expectedStartReturn{4u, 3u}; |
174 | const std::pair expectedEndReturn{4u, 14u}; |
175 | const std::pair expectedScopeEndReturn{4u, 40u}; |
176 | ASSERT_EQ(sourceMgr.getLineAndColumn(funcReturnDefinition->loc.Start), |
177 | expectedStartReturn); |
178 | ASSERT_EQ(sourceMgr.getLineAndColumn(funcReturnDefinition->loc.End), |
179 | expectedEndReturn); |
180 | ASSERT_EQ(funcReturnDefinition->loc.Start, |
181 | funcReturnDefinition->scopeLoc.Start); |
182 | ASSERT_EQ(sourceMgr.getLineAndColumn(funcReturnDefinition->scopeLoc.End), |
183 | expectedScopeEndReturn); |
184 | } |
185 | } // namespace |
186 | |