CIRCT  19.0.0git
Expressions.cpp
Go to the documentation of this file.
1 //===- Expressions.cpp - Slang expression 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 #include "slang/syntax/AllSyntax.h"
11 
12 using namespace circt;
13 using namespace ImportVerilog;
14 
15 // NOLINTBEGIN(misc-no-recursion)
16 namespace {
17 struct ExprVisitor {
18  Context &context;
19  Location loc;
20  OpBuilder &builder;
21 
22  ExprVisitor(Context &context, Location loc)
23  : context(context), loc(loc), builder(context.builder) {}
24 
25  /// Helper function to convert a value to its simple bit vector
26  /// representation, if it has one. Otherwise returns null.
27  Value convertToSimpleBitVector(Value value) {
28  if (!value)
29  return {};
30  if (isa<moore::IntType>(value.getType()))
31  return value;
32  mlir::emitError(loc, "expression of type ")
33  << value.getType() << " cannot be cast to a simple bit vector";
34  return {};
35  }
36 
37  /// Helper function to convert a value to its "truthy" boolean value.
38  Value convertToBool(Value value) {
39  if (!value)
40  return {};
41  if (auto type = dyn_cast_or_null<moore::IntType>(value.getType()))
42  if (type.getBitSize() == 1)
43  return value;
44  if (auto type = dyn_cast_or_null<moore::UnpackedType>(value.getType()))
45  return builder.create<moore::BoolCastOp>(loc, value);
46  mlir::emitError(loc, "expression of type ")
47  << value.getType() << " cannot be cast to a boolean";
48  return {};
49  }
50 
51  // Handle references to the left-hand side of a parent assignment.
52  Value visit(const slang::ast::LValueReferenceExpression &expr) {
53  assert(!context.lvalueStack.empty() && "parent assignments push lvalue");
54  auto lvalue = context.lvalueStack.back();
55  return builder.create<moore::ReadLValueOp>(loc, lvalue);
56  }
57 
58  // Handle named values, such as references to declared variables.
59  Value visit(const slang::ast::NamedValueExpression &expr) {
60  if (auto value = context.valueSymbols.lookup(&expr.symbol))
61  return value;
62  auto d = mlir::emitError(loc, "unknown name `") << expr.symbol.name << "`";
63  d.attachNote(context.convertLocation(expr.symbol.location))
64  << "no value generated for " << slang::ast::toString(expr.symbol.kind);
65  return {};
66  }
67 
68  // Handle type conversions (explicit and implicit).
69  Value visit(const slang::ast::ConversionExpression &expr) {
70  auto type = context.convertType(*expr.type);
71  if (!type)
72  return {};
73  auto operand = context.convertExpression(expr.operand());
74  if (!operand)
75  return {};
76  return builder.create<moore::ConversionOp>(loc, type, operand);
77  }
78 
79  // Handle blocking and non-blocking assignments.
80  Value visit(const slang::ast::AssignmentExpression &expr) {
81  auto lhs = context.convertExpression(expr.left());
82  context.lvalueStack.push_back(lhs);
83  auto rhs = context.convertExpression(expr.right());
84  context.lvalueStack.pop_back();
85  if (!lhs || !rhs)
86  return {};
87 
88  if (lhs.getType() != rhs.getType())
89  rhs = builder.create<moore::ConversionOp>(loc, lhs.getType(), rhs);
90 
91  if (expr.timingControl) {
92  auto loc = context.convertLocation(expr.timingControl->sourceRange);
93  mlir::emitError(loc, "delayed assignments not supported");
94  return {};
95  }
96 
97  if (expr.isNonBlocking())
98  builder.create<moore::NonBlockingAssignOp>(loc, lhs, rhs);
99  else
100  builder.create<moore::BlockingAssignOp>(loc, lhs, rhs);
101  return rhs;
102  }
103 
104  // Helper function to convert an argument to a simple bit vector type, pass it
105  // to a reduction op, and optionally invert the result.
106  template <class ConcreteOp>
107  Value createReduction(Value arg, bool invert) {
108  arg = convertToSimpleBitVector(arg);
109  if (!arg)
110  return {};
111  Value result = builder.create<ConcreteOp>(loc, arg);
112  if (invert)
113  result = builder.create<moore::NotOp>(loc, result);
114  return result;
115  }
116 
117  // Helper function to create pre and post increments and decrements.
118  Value createIncrement(Value arg, bool isInc, bool isPost) {
119  auto preValue = convertToSimpleBitVector(arg);
120  if (!preValue)
121  return {};
122  preValue = builder.create<moore::ReadLValueOp>(loc, preValue);
123  auto one = builder.create<moore::ConstantOp>(
124  loc, cast<moore::IntType>(preValue.getType()), 1);
125  auto postValue =
126  isInc ? builder.create<moore::AddOp>(loc, preValue, one).getResult()
127  : builder.create<moore::SubOp>(loc, preValue, one).getResult();
128  builder.create<moore::BlockingAssignOp>(loc, arg, postValue);
129  return isPost ? preValue : postValue;
130  }
131 
132  // Handle unary operators.
133  Value visit(const slang::ast::UnaryExpression &expr) {
134  auto arg = context.convertExpression(expr.operand());
135  if (!arg)
136  return {};
137 
138  using slang::ast::UnaryOperator;
139  switch (expr.op) {
140  // `+a` is simply `a`, but converted to a simple bit vector type since
141  // this is technically an arithmetic operation.
142  case UnaryOperator::Plus:
143  return convertToSimpleBitVector(arg);
144 
145  case UnaryOperator::Minus:
146  arg = convertToSimpleBitVector(arg);
147  if (!arg)
148  return {};
149  return builder.create<moore::NegOp>(loc, arg);
150 
151  case UnaryOperator::BitwiseNot:
152  arg = convertToSimpleBitVector(arg);
153  if (!arg)
154  return {};
155  return builder.create<moore::NotOp>(loc, arg);
156 
157  case UnaryOperator::BitwiseAnd:
158  return createReduction<moore::ReduceAndOp>(arg, false);
159  case UnaryOperator::BitwiseOr:
160  return createReduction<moore::ReduceOrOp>(arg, false);
161  case UnaryOperator::BitwiseXor:
162  return createReduction<moore::ReduceXorOp>(arg, false);
163  case UnaryOperator::BitwiseNand:
164  return createReduction<moore::ReduceAndOp>(arg, true);
165  case UnaryOperator::BitwiseNor:
166  return createReduction<moore::ReduceOrOp>(arg, true);
167  case UnaryOperator::BitwiseXnor:
168  return createReduction<moore::ReduceXorOp>(arg, true);
169 
170  case UnaryOperator::LogicalNot:
171  arg = convertToBool(arg);
172  if (!arg)
173  return {};
174  return builder.create<moore::NotOp>(loc, arg);
175 
176  case UnaryOperator::Preincrement:
177  return createIncrement(arg, true, false);
178  case UnaryOperator::Predecrement:
179  return createIncrement(arg, false, false);
180  case UnaryOperator::Postincrement:
181  return createIncrement(arg, true, true);
182  case UnaryOperator::Postdecrement:
183  return createIncrement(arg, false, true);
184  }
185 
186  mlir::emitError(loc, "unsupported unary operator");
187  return {};
188  }
189 
190  // Helper function to convert two arguments to a simple bit vector type and
191  // pass them into a binary op.
192  template <class ConcreteOp>
193  Value createBinary(Value lhs, Value rhs) {
194  lhs = convertToSimpleBitVector(lhs);
195  rhs = convertToSimpleBitVector(rhs);
196  if (!lhs || !rhs)
197  return {};
198  return builder.create<ConcreteOp>(loc, lhs, rhs);
199  }
200 
201  // Handle binary operators.
202  Value visit(const slang::ast::BinaryExpression &expr) {
203  auto lhs = context.convertExpression(expr.left());
204  auto rhs = context.convertExpression(expr.right());
205  if (!lhs || !rhs)
206  return {};
207 
208  using slang::ast::BinaryOperator;
209  switch (expr.op) {
210  case BinaryOperator::Add:
211  return createBinary<moore::AddOp>(lhs, rhs);
212  case BinaryOperator::Subtract:
213  return createBinary<moore::SubOp>(lhs, rhs);
214  case BinaryOperator::Multiply:
215  return createBinary<moore::MulOp>(lhs, rhs);
216  case BinaryOperator::Divide:
217  if (expr.type->isSigned())
218  return createBinary<moore::DivSOp>(lhs, rhs);
219  else
220  return createBinary<moore::DivUOp>(lhs, rhs);
221  case BinaryOperator::Mod:
222  if (expr.type->isSigned())
223  return createBinary<moore::ModSOp>(lhs, rhs);
224  else
225  return createBinary<moore::ModUOp>(lhs, rhs);
226 
227  case BinaryOperator::BinaryAnd:
228  return createBinary<moore::AndOp>(lhs, rhs);
229  case BinaryOperator::BinaryOr:
230  return createBinary<moore::OrOp>(lhs, rhs);
231  case BinaryOperator::BinaryXor:
232  return createBinary<moore::XorOp>(lhs, rhs);
233  case BinaryOperator::BinaryXnor: {
234  auto result = createBinary<moore::XorOp>(lhs, rhs);
235  if (!result)
236  return {};
237  return builder.create<moore::NotOp>(loc, result);
238  }
239 
240  case BinaryOperator::Equality:
241  return createBinary<moore::EqOp>(lhs, rhs);
242  case BinaryOperator::Inequality:
243  return createBinary<moore::NeOp>(lhs, rhs);
244  case BinaryOperator::CaseEquality:
245  return createBinary<moore::CaseEqOp>(lhs, rhs);
246  case BinaryOperator::CaseInequality:
247  return createBinary<moore::CaseNeOp>(lhs, rhs);
248  case BinaryOperator::WildcardEquality:
249  return createBinary<moore::WildcardEqOp>(lhs, rhs);
250  case BinaryOperator::WildcardInequality:
251  return createBinary<moore::WildcardNeOp>(lhs, rhs);
252 
253  case BinaryOperator::GreaterThanEqual:
254  if (expr.left().type->isSigned())
255  return createBinary<moore::SgeOp>(lhs, rhs);
256  else
257  return createBinary<moore::UgeOp>(lhs, rhs);
258  case BinaryOperator::GreaterThan:
259  if (expr.left().type->isSigned())
260  return createBinary<moore::SgtOp>(lhs, rhs);
261  else
262  return createBinary<moore::UgtOp>(lhs, rhs);
263  case BinaryOperator::LessThanEqual:
264  if (expr.left().type->isSigned())
265  return createBinary<moore::SleOp>(lhs, rhs);
266  else
267  return createBinary<moore::UleOp>(lhs, rhs);
268  case BinaryOperator::LessThan:
269  if (expr.left().type->isSigned())
270  return createBinary<moore::SltOp>(lhs, rhs);
271  else
272  return createBinary<moore::UltOp>(lhs, rhs);
273 
274  // See IEEE 1800-2017 ยง 11.4.7 "Logical operators".
275  case BinaryOperator::LogicalAnd: {
276  // TODO: This should short-circuit. Put the RHS code into an scf.if.
277  lhs = convertToBool(lhs);
278  rhs = convertToBool(rhs);
279  if (!lhs || !rhs)
280  return {};
281  return builder.create<moore::AndOp>(loc, lhs, rhs);
282  }
283  case BinaryOperator::LogicalOr: {
284  // TODO: This should short-circuit. Put the RHS code into an scf.if.
285  lhs = convertToBool(lhs);
286  rhs = convertToBool(rhs);
287  if (!lhs || !rhs)
288  return {};
289  return builder.create<moore::OrOp>(loc, lhs, rhs);
290  }
291  case BinaryOperator::LogicalImplication: {
292  // `(lhs -> rhs)` equivalent to `(!lhs || rhs)`.
293  lhs = convertToBool(lhs);
294  rhs = convertToBool(rhs);
295  if (!lhs || !rhs)
296  return {};
297  auto notLHS = builder.create<moore::NotOp>(loc, lhs);
298  return builder.create<moore::OrOp>(loc, notLHS, rhs);
299  }
300  case BinaryOperator::LogicalEquivalence: {
301  // `(lhs <-> rhs)` equivalent to `(lhs && rhs) || (!lhs && !rhs)`.
302  lhs = convertToBool(lhs);
303  rhs = convertToBool(rhs);
304  if (!lhs || !rhs)
305  return {};
306  auto notLHS = builder.create<moore::NotOp>(loc, lhs);
307  auto notRHS = builder.create<moore::NotOp>(loc, rhs);
308  auto both = builder.create<moore::AndOp>(loc, lhs, rhs);
309  auto notBoth = builder.create<moore::AndOp>(loc, notLHS, notRHS);
310  return builder.create<moore::OrOp>(loc, both, notBoth);
311  }
312 
313  case BinaryOperator::LogicalShiftLeft:
314  return createBinary<moore::ShlOp>(lhs, rhs);
315  case BinaryOperator::LogicalShiftRight:
316  return createBinary<moore::ShrOp>(lhs, rhs);
317  case BinaryOperator::ArithmeticShiftLeft:
318  return createBinary<moore::ShlOp>(lhs, rhs);
319  case BinaryOperator::ArithmeticShiftRight: {
320  // The `>>>` operator is an arithmetic right shift if the LHS operand is
321  // signed, or a logical right shift if the operand is unsigned.
322  lhs = convertToSimpleBitVector(lhs);
323  rhs = convertToSimpleBitVector(rhs);
324  if (!lhs || !rhs)
325  return {};
326  if (expr.type->isSigned())
327  return builder.create<moore::AShrOp>(loc, lhs, rhs);
328  return builder.create<moore::ShrOp>(loc, lhs, rhs);
329  }
330 
331  case BinaryOperator::Power:
332  break;
333  }
334 
335  mlir::emitError(loc, "unsupported binary operator");
336  return {};
337  }
338 
339  // Materialize a Slang integer literal as a constant op.
340  Value convertSVInt(const slang::SVInt &value, Type type) {
341  if (value.hasUnknown()) {
342  mlir::emitError(loc, "literals with X or Z bits not supported");
343  return {};
344  }
345  if (value.getBitWidth() > 64) {
346  mlir::emitError(loc, "unsupported bit width: literal is ")
347  << value.getBitWidth() << " bits wide; only 64 supported";
348  return {};
349  }
350  auto truncValue = value.as<uint64_t>().value();
351  return builder.create<moore::ConstantOp>(loc, cast<moore::IntType>(type),
352  truncValue);
353  }
354 
355  // Handle `'0`, `'1`, `'x`, and `'z` literals.
356  Value visit(const slang::ast::UnbasedUnsizedIntegerLiteral &expr) {
357  auto type = context.convertType(*expr.type);
358  if (!type)
359  return {};
360  return convertSVInt(expr.getValue(), type);
361  }
362 
363  // Handle integer literals.
364  Value visit(const slang::ast::IntegerLiteral &expr) {
365  auto type = context.convertType(*expr.type);
366  if (!type)
367  return {};
368  return convertSVInt(expr.getValue(), type);
369  }
370 
371  // Handle concatenations.
372  Value visit(const slang::ast::ConcatenationExpression &expr) {
373  SmallVector<Value> operands;
374  for (auto *operand : expr.operands()) {
375  auto value = context.convertExpression(*operand);
376  if (!value)
377  continue;
378  value = convertToSimpleBitVector(value);
379  operands.push_back(value);
380  }
381  return builder.create<moore::ConcatOp>(loc, operands);
382  }
383 
384  // Handle replications.
385  Value visit(const slang::ast::ReplicationExpression &expr) {
386  auto type = context.convertType(*expr.type);
387  if (isa<moore::VoidType>(type))
388  return {};
389 
390  auto value = context.convertExpression(expr.concat());
391  if (!value)
392  return {};
393  return builder.create<moore::ReplicateOp>(loc, type, value);
394  }
395 
396  // Handle single bit selections.
397  Value visit(const slang::ast::ElementSelectExpression &expr) {
398  auto type = context.convertType(*expr.type);
399  auto value = context.convertExpression(expr.value());
400  auto lowBit = context.convertExpression(expr.selector());
401 
402  if (!value || !lowBit)
403  return {};
404  return builder.create<moore::ExtractOp>(loc, type, value, lowBit);
405  }
406 
407  // Handle range bits selections.
408  Value visit(const slang::ast::RangeSelectExpression &expr) {
409  auto type = context.convertType(*expr.type);
410  auto value = context.convertExpression(expr.value());
411  Value lowBit;
412  if (expr.getSelectionKind() == slang::ast::RangeSelectionKind::Simple) {
413  if (expr.left().constant && expr.right().constant) {
414  auto lhs = expr.left().constant->integer().as<uint64_t>().value();
415  auto rhs = expr.right().constant->integer().as<uint64_t>().value();
416  lowBit = lhs < rhs ? context.convertExpression(expr.left())
417  : context.convertExpression(expr.right());
418  } else {
419  mlir::emitError(loc, "unsupported a variable as the index in the")
420  << slang::ast::toString(expr.getSelectionKind()) << "kind";
421  return {};
422  }
423  } else
424  lowBit = context.convertExpression(expr.left());
425 
426  if (!value || !lowBit)
427  return {};
428  return builder.create<moore::ExtractOp>(loc, type, value, lowBit);
429  }
430 
431  /// Emit an error for all other expressions.
432  template <typename T>
433  Value visit(T &&node) {
434  mlir::emitError(loc, "unsupported expression: ")
435  << slang::ast::toString(node.kind);
436  return {};
437  }
438 
439  Value visitInvalid(const slang::ast::Expression &expr) {
440  mlir::emitError(loc, "invalid expression");
441  return {};
442  }
443 };
444 } // namespace
445 
446 Value Context::convertExpression(const slang::ast::Expression &expr) {
447  auto loc = convertLocation(expr.sourceRange);
448  return expr.visit(ExprVisitor(*this, loc));
449 }
450 // NOLINTEND(misc-no-recursion)
assert(baseType &&"element must be base type")
constexpr const char * toString(Flow flow)
Definition: FIRRTLOps.cpp:169
Builder builder
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
SmallVector< Value > lvalueStack
A stack of assignment left-hand side values.
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.