CIRCT  19.0.0git
Statements.cpp
Go to the documentation of this file.
1 //===- Statements.cpp - Slang statement conversion ------------------------===//
2 //
3 // Part of the LLVM Project, 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 
10 
11 using namespace mlir;
12 using namespace circt;
13 using namespace ImportVerilog;
14 
15 // NOLINTBEGIN(misc-no-recursion)
16 namespace {
17 struct StmtVisitor {
18  Context &context;
19  Location loc;
20  OpBuilder &builder;
21 
22  StmtVisitor(Context &context, Location loc)
23  : context(context), loc(loc), builder(context.builder) {}
24 
25  // Skip empty statements (stray semicolons).
26  LogicalResult visit(const slang::ast::EmptyStatement &) { return success(); }
27 
28  // Convert every statement in a statement list. The Verilog syntax follows a
29  // similar philosophy as C/C++, where things like `if` and `for` accept a
30  // single statement as body. But then a `{...}` block is a valid statement,
31  // which allows for the `if {...}` syntax. In Verilog, things like `final`
32  // accept a single body statement, but that can be a `begin ... end` block,
33  // which in turn has a single body statement, which then commonly is a list of
34  // statements.
35  LogicalResult visit(const slang::ast::StatementList &stmts) {
36  for (auto *stmt : stmts.list)
37  if (failed(context.convertStatement(*stmt)))
38  return failure();
39  return success();
40  }
41 
42  // Inline `begin ... end` blocks into the parent.
43  LogicalResult visit(const slang::ast::BlockStatement &stmt) {
44  return context.convertStatement(stmt.body);
45  }
46 
47  // Handle expression statements.
48  LogicalResult visit(const slang::ast::ExpressionStatement &stmt) {
49  return success(context.convertExpression(stmt.expr));
50  }
51 
52  // Handle variable declarations.
53  LogicalResult visit(const slang::ast::VariableDeclStatement &stmt) {
54  const auto &var = stmt.symbol;
55  auto type = context.convertType(*var.getDeclaredType());
56  if (!type)
57  return failure();
58 
59  Value initial;
60  if (const auto *init = var.getInitializer()) {
61  initial = context.convertExpression(*init);
62  if (!initial)
63  return failure();
64  }
65 
66  // Collect local temporary variables.
67  auto varOp = builder.create<moore::VariableOp>(
68  loc, type, builder.getStringAttr(var.name), initial);
69  context.valueSymbols.insertIntoScope(context.valueSymbols.getCurScope(),
70  &var, varOp);
71  return success();
72  }
73 
74  // Handle if statements.
75  LogicalResult visit(const slang::ast::ConditionalStatement &stmt) {
76  // Generate the condition. There may be multiple conditions linked with the
77  // `&&&` operator.
78  Value allConds;
79  for (const auto &condition : stmt.conditions) {
80  if (condition.pattern)
81  return mlir::emitError(loc,
82  "match patterns in if conditions not supported");
83  auto cond = context.convertExpression(*condition.expr);
84  if (!cond)
85  return failure();
86  cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
87  if (allConds)
88  allConds = builder.create<moore::AndOp>(loc, allConds, cond);
89  else
90  allConds = cond;
91  }
92  assert(allConds && "slang guarantees at least one condition");
93  allConds =
94  builder.create<moore::ConversionOp>(loc, builder.getI1Type(), allConds);
95 
96  // Generate the if operation.
97  auto ifOp =
98  builder.create<scf::IfOp>(loc, allConds, stmt.ifFalse != nullptr);
99  OpBuilder::InsertionGuard guard(builder);
100 
101  // Generate the "then" body.
102  builder.setInsertionPoint(ifOp.thenYield());
103  if (failed(context.convertStatement(stmt.ifTrue)))
104  return failure();
105 
106  // Generate the "else" body if present.
107  if (stmt.ifFalse) {
108  builder.setInsertionPoint(ifOp.elseYield());
109  if (failed(context.convertStatement(*stmt.ifFalse)))
110  return failure();
111  }
112 
113  return success();
114  }
115 
116  // Handle case statements.
117  LogicalResult visit(const slang::ast::CaseStatement &caseStmt) {
118  auto caseExpr = context.convertExpression(caseStmt.expr);
119  auto items = caseStmt.items;
120  // Used to generate the condition of the default case statement.
121  SmallVector<Value> defaultConds;
122  // Traverse the case items.
123  for (auto item : items) {
124  // One statement will be matched with multi-conditions.
125  // Like case(cond) 0, 1 : y = x; endcase.
126  SmallVector<Value> allConds;
127  for (const auto *expr : item.expressions) {
128  auto itemExpr = context.convertExpression(*expr);
129  auto newEqOp = builder.create<moore::EqOp>(loc, caseExpr, itemExpr);
130  allConds.push_back(newEqOp);
131  }
132  // Bound all conditions of an item into one.
133  auto cond = allConds.back();
134  allConds.pop_back();
135  while (!allConds.empty()) {
136  cond = builder.create<moore::OrOp>(loc, allConds.back(), cond);
137  allConds.pop_back();
138  }
139  // Gather all items' conditions.
140  defaultConds.push_back(cond);
141  cond =
142  builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
143  auto ifOp = builder.create<mlir::scf::IfOp>(loc, cond);
144  OpBuilder::InsertionGuard guard(builder);
145  builder.setInsertionPoint(ifOp.thenYield());
146  if (failed(context.convertStatement(*item.stmt)))
147  return failure();
148  }
149  // Handle the 'default case' statement if it exists.
150  if (caseStmt.defaultCase) {
151  auto cond = defaultConds.back();
152  defaultConds.pop_back();
153  while (!defaultConds.empty()) {
154  cond = builder.create<moore::OrOp>(loc, defaultConds.back(), cond);
155  defaultConds.pop_back();
156  }
157  cond = builder.create<moore::NotOp>(loc, cond);
158  cond =
159  builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
160  auto ifOp = builder.create<mlir::scf::IfOp>(loc, cond);
161  OpBuilder::InsertionGuard guard(builder);
162  builder.setInsertionPoint(ifOp.thenYield());
163  if (failed(context.convertStatement(*caseStmt.defaultCase)))
164  return failure();
165  }
166  return success();
167  }
168 
169  // Handle `for` loops.
170  LogicalResult visit(const slang::ast::ForLoopStatement &stmt) {
171  if (!stmt.loopVars.empty())
172  return mlir::emitError(loc,
173  "variables in for loop initializer not supported");
174 
175  // Generate the initializers.
176  for (auto *initExpr : stmt.initializers)
177  if (!context.convertExpression(*initExpr))
178  return failure();
179 
180  // Create the while op.
181  auto whileOp = builder.create<scf::WhileOp>(loc, TypeRange{}, ValueRange{});
182  OpBuilder::InsertionGuard guard(builder);
183 
184  // In the "before" region, check that the condition holds.
185  builder.createBlock(&whileOp.getBefore());
186  auto cond = context.convertExpression(*stmt.stopExpr);
187  if (!cond)
188  return failure();
189  cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
190  cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
191  builder.create<mlir::scf::ConditionOp>(loc, cond, ValueRange{});
192 
193  // In the "after" region, generate the loop body and step expressions.
194  builder.createBlock(&whileOp.getAfter());
195  if (failed(context.convertStatement(stmt.body)))
196  return failure();
197  for (auto *stepExpr : stmt.steps)
198  if (!context.convertExpression(*stepExpr))
199  return failure();
200  builder.create<mlir::scf::YieldOp>(loc);
201 
202  return success();
203  }
204 
205  // Handle `repeat` loops.
206  LogicalResult visit(const slang::ast::RepeatLoopStatement &stmt) {
207  // Create the while op and feed in the repeat count as the initial counter
208  // value.
209  auto count = context.convertExpression(stmt.count);
210  if (!count)
211  return failure();
212  auto type = cast<moore::IntType>(count.getType());
213  auto whileOp = builder.create<scf::WhileOp>(loc, type, count);
214  OpBuilder::InsertionGuard guard(builder);
215 
216  // In the "before" region, check that the counter is non-zero.
217  auto *block = builder.createBlock(&whileOp.getBefore(), {}, type, loc);
218  auto counterArg = block->getArgument(0);
219  auto cond = builder.createOrFold<moore::BoolCastOp>(loc, counterArg);
220  cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
221  builder.create<scf::ConditionOp>(loc, cond, counterArg);
222 
223  // In the "after" region, generate the loop body and decrement the counter.
224  block = builder.createBlock(&whileOp.getAfter(), {}, type, loc);
225  if (failed(context.convertStatement(stmt.body)))
226  return failure();
227  counterArg = block->getArgument(0);
228  auto constOne = builder.create<moore::ConstantOp>(loc, type, 1);
229  auto subOp = builder.create<moore::SubOp>(loc, counterArg, constOne);
230  builder.create<scf::YieldOp>(loc, ValueRange{subOp});
231 
232  return success();
233  }
234 
235  // Handle `while` loops.
236  LogicalResult visit(const slang::ast::WhileLoopStatement &stmt) {
237  // Create the while op.
238  auto whileOp = builder.create<scf::WhileOp>(loc, TypeRange{}, ValueRange{});
239  OpBuilder::InsertionGuard guard(builder);
240 
241  // In the "before" region, check that the condition holds.
242  builder.createBlock(&whileOp.getBefore());
243  auto cond = context.convertExpression(stmt.cond);
244  if (!cond)
245  return failure();
246  cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
247  cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
248  builder.create<mlir::scf::ConditionOp>(loc, cond, ValueRange{});
249 
250  // In the "after" region, generate the loop body.
251  builder.createBlock(&whileOp.getAfter());
252  if (failed(context.convertStatement(stmt.body)))
253  return failure();
254  builder.create<mlir::scf::YieldOp>(loc);
255 
256  return success();
257  }
258 
259  // Handle `do ... while` loops.
260  LogicalResult visit(const slang::ast::DoWhileLoopStatement &stmt) {
261  // Create the while op.
262  auto whileOp = builder.create<scf::WhileOp>(loc, TypeRange{}, ValueRange{});
263  OpBuilder::InsertionGuard guard(builder);
264 
265  // In the "before" region, generate the loop body and check that the
266  // condition holds.
267  builder.createBlock(&whileOp.getBefore());
268  if (failed(context.convertStatement(stmt.body)))
269  return failure();
270  auto cond = context.convertExpression(stmt.cond);
271  if (!cond)
272  return failure();
273  cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
274  cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
275  builder.create<mlir::scf::ConditionOp>(loc, cond, ValueRange{});
276 
277  // Generate an empty "after" region.
278  builder.createBlock(&whileOp.getAfter());
279  builder.create<mlir::scf::YieldOp>(loc);
280 
281  return success();
282  }
283 
284  // Handle `forever` loops.
285  LogicalResult visit(const slang::ast::ForeverLoopStatement &stmt) {
286  // Create the while op.
287  auto whileOp = builder.create<scf::WhileOp>(loc, TypeRange{}, ValueRange{});
288  OpBuilder::InsertionGuard guard(builder);
289 
290  // In the "before" region, return true for the condition.
291  builder.createBlock(&whileOp.getBefore());
292  auto cond = builder.create<hw::ConstantOp>(loc, builder.getI1Type(), 1);
293  builder.create<mlir::scf::ConditionOp>(loc, cond, ValueRange{});
294 
295  // In the "after" region, generate the loop body.
296  builder.createBlock(&whileOp.getAfter());
297  if (failed(context.convertStatement(stmt.body)))
298  return failure();
299  builder.create<mlir::scf::YieldOp>(loc);
300 
301  return success();
302  }
303 
304  // Ignore timing control for now.
305  LogicalResult visit(const slang::ast::TimedStatement &stmt) {
306  return success();
307  }
308 
309  /// Emit an error for all other statements.
310  template <typename T>
311  LogicalResult visit(T &&stmt) {
312  mlir::emitError(loc, "unsupported statement: ")
313  << slang::ast::toString(stmt.kind);
314  return mlir::failure();
315  }
316 
317  LogicalResult visitInvalid(const slang::ast::Statement &stmt) {
318  mlir::emitError(loc, "invalid statement: ")
319  << slang::ast::toString(stmt.kind);
320  return mlir::failure();
321  }
322 };
323 } // namespace
324 
325 LogicalResult Context::convertStatement(const slang::ast::Statement &stmt) {
326  auto loc = convertLocation(stmt.sourceRange);
327  return stmt.visit(StmtVisitor(*this, loc));
328 }
329 // NOLINTEND(misc-no-recursion)
assert(baseType &&"element must be base type")
constexpr const char * toString(Flow flow)
Definition: FIRRTLOps.cpp:169
static Location convertLocation(MLIRContext *context, const slang::SourceManager &sourceManager, SmallDenseMap< slang::BufferID, StringRef > &bufferFilePaths, slang::SourceLocation loc)
Convert a slang SourceLocation to an MLIR Location.
Builder builder
This helps visit TypeOp nodes.
Definition: HWVisitors.h:87
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
A helper class to facilitate the conversion from a Slang AST to MLIR operations.
Value convertExpression(const slang::ast::Expression &expr)
Type convertType(const slang::ast::Type &type, LocationAttr loc={})
Convert a slang type into an MLIR type.
Definition: Types.cpp:151
LogicalResult convertStatement(const slang::ast::Statement &stmt)
Definition: Statements.cpp:325