CIRCT  20.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/ast/SystemSubroutine.h"
11 #include "slang/syntax/AllSyntax.h"
12 
13 using namespace circt;
14 using namespace ImportVerilog;
15 using moore::Domain;
16 
17 /// Convert a Slang `SVInt` to a CIRCT `FVInt`.
18 static FVInt convertSVIntToFVInt(const slang::SVInt &svint) {
19  if (svint.hasUnknown()) {
20  unsigned numWords = svint.getNumWords() / 2;
21  auto value = ArrayRef<uint64_t>(svint.getRawPtr(), numWords);
22  auto unknown = ArrayRef<uint64_t>(svint.getRawPtr() + numWords, numWords);
23  return FVInt(APInt(svint.getBitWidth(), value),
24  APInt(svint.getBitWidth(), unknown));
25  }
26  auto value = ArrayRef<uint64_t>(svint.getRawPtr(), svint.getNumWords());
27  return FVInt(APInt(svint.getBitWidth(), value));
28 }
29 
30 // NOLINTBEGIN(misc-no-recursion)
31 namespace {
32 struct RvalueExprVisitor {
33  Context &context;
34  Location loc;
35  OpBuilder &builder;
36 
37  RvalueExprVisitor(Context &context, Location loc)
38  : context(context), loc(loc), builder(context.builder) {}
39 
40  // Handle references to the left-hand side of a parent assignment.
41  Value visit(const slang::ast::LValueReferenceExpression &expr) {
42  assert(!context.lvalueStack.empty() && "parent assignments push lvalue");
43  auto lvalue = context.lvalueStack.back();
44  return builder.create<moore::ReadOp>(loc, lvalue);
45  }
46 
47  // Handle named values, such as references to declared variables.
48  Value visit(const slang::ast::NamedValueExpression &expr) {
49  if (auto value = context.valueSymbols.lookup(&expr.symbol)) {
50  if (isa<moore::RefType>(value.getType())) {
51  auto readOp = builder.create<moore::ReadOp>(loc, value);
52  if (context.rvalueReadCallback)
53  context.rvalueReadCallback(readOp);
54  value = readOp.getResult();
55  }
56  return value;
57  }
58 
59  // Try to materialize constant values directly.
60  auto constant = context.evaluateConstant(expr);
61  if (auto value = context.materializeConstant(constant, *expr.type, loc))
62  return value;
63 
64  // Otherwise some other part of ImportVerilog should have added an MLIR
65  // value for this expression's symbol to the `context.valueSymbols` table.
66  auto d = mlir::emitError(loc, "unknown name `") << expr.symbol.name << "`";
67  d.attachNote(context.convertLocation(expr.symbol.location))
68  << "no rvalue generated for " << slang::ast::toString(expr.symbol.kind);
69  return {};
70  }
71 
72  // Handle type conversions (explicit and implicit).
73  Value visit(const slang::ast::ConversionExpression &expr) {
74  auto type = context.convertType(*expr.type);
75  if (!type)
76  return {};
77  return context.convertRvalueExpression(expr.operand(), type);
78  }
79 
80  // Handle blocking and non-blocking assignments.
81  Value visit(const slang::ast::AssignmentExpression &expr) {
82  auto lhs = context.convertLvalueExpression(expr.left());
83  if (!lhs)
84  return {};
85 
86  context.lvalueStack.push_back(lhs);
87  auto rhs = context.convertRvalueExpression(
88  expr.right(), cast<moore::RefType>(lhs.getType()).getNestedType());
89  context.lvalueStack.pop_back();
90  if (!rhs)
91  return {};
92 
93  if (expr.timingControl) {
94  auto loc = context.convertLocation(expr.timingControl->sourceRange);
95  mlir::emitError(loc, "delayed assignments not supported");
96  return {};
97  }
98 
99  if (expr.isNonBlocking())
100  builder.create<moore::NonBlockingAssignOp>(loc, lhs, rhs);
101  else
102  builder.create<moore::BlockingAssignOp>(loc, lhs, rhs);
103  return rhs;
104  }
105 
106  // Helper function to convert an argument to a simple bit vector type, pass it
107  // to a reduction op, and optionally invert the result.
108  template <class ConcreteOp>
109  Value createReduction(Value arg, bool invert) {
110  arg = context.convertToSimpleBitVector(arg);
111  if (!arg)
112  return {};
113  Value result = builder.create<ConcreteOp>(loc, arg);
114  if (invert)
115  result = builder.create<moore::NotOp>(loc, result);
116  return result;
117  }
118 
119  // Helper function to create pre and post increments and decrements.
120  Value createIncrement(Value arg, bool isInc, bool isPost) {
121  auto preValue = builder.create<moore::ReadOp>(loc, arg);
122  auto one = builder.create<moore::ConstantOp>(
123  loc, cast<moore::IntType>(preValue.getType()), 1);
124  auto postValue =
125  isInc ? builder.create<moore::AddOp>(loc, preValue, one).getResult()
126  : builder.create<moore::SubOp>(loc, preValue, one).getResult();
127  builder.create<moore::BlockingAssignOp>(loc, arg, postValue);
128  if (isPost)
129  return preValue;
130  return postValue;
131  }
132 
133  // Handle unary operators.
134  Value visit(const slang::ast::UnaryExpression &expr) {
135  using slang::ast::UnaryOperator;
136  Value arg;
137  if (expr.op == UnaryOperator::Preincrement ||
138  expr.op == UnaryOperator::Predecrement ||
139  expr.op == UnaryOperator::Postincrement ||
140  expr.op == UnaryOperator::Postdecrement)
141  arg = context.convertLvalueExpression(expr.operand());
142  else
143  arg = context.convertRvalueExpression(expr.operand());
144  if (!arg)
145  return {};
146 
147  switch (expr.op) {
148  // `+a` is simply `a`, but converted to a simple bit vector type since
149  // this is technically an arithmetic operation.
150  case UnaryOperator::Plus:
151  return context.convertToSimpleBitVector(arg);
152 
153  case UnaryOperator::Minus:
154  arg = context.convertToSimpleBitVector(arg);
155  if (!arg)
156  return {};
157  return builder.create<moore::NegOp>(loc, arg);
158 
159  case UnaryOperator::BitwiseNot:
160  arg = context.convertToSimpleBitVector(arg);
161  if (!arg)
162  return {};
163  return builder.create<moore::NotOp>(loc, arg);
164 
165  case UnaryOperator::BitwiseAnd:
166  return createReduction<moore::ReduceAndOp>(arg, false);
167  case UnaryOperator::BitwiseOr:
168  return createReduction<moore::ReduceOrOp>(arg, false);
169  case UnaryOperator::BitwiseXor:
170  return createReduction<moore::ReduceXorOp>(arg, false);
171  case UnaryOperator::BitwiseNand:
172  return createReduction<moore::ReduceAndOp>(arg, true);
173  case UnaryOperator::BitwiseNor:
174  return createReduction<moore::ReduceOrOp>(arg, true);
175  case UnaryOperator::BitwiseXnor:
176  return createReduction<moore::ReduceXorOp>(arg, true);
177 
178  case UnaryOperator::LogicalNot:
179  arg = context.convertToBool(arg);
180  if (!arg)
181  return {};
182  return builder.create<moore::NotOp>(loc, arg);
183 
184  case UnaryOperator::Preincrement:
185  return createIncrement(arg, true, false);
186  case UnaryOperator::Predecrement:
187  return createIncrement(arg, false, false);
188  case UnaryOperator::Postincrement:
189  return createIncrement(arg, true, true);
190  case UnaryOperator::Postdecrement:
191  return createIncrement(arg, false, true);
192  }
193 
194  mlir::emitError(loc, "unsupported unary operator");
195  return {};
196  }
197 
198  // Helper function to convert two arguments to a simple bit vector type and
199  // pass them into a binary op.
200  template <class ConcreteOp>
201  Value createBinary(Value lhs, Value rhs) {
202  lhs = context.convertToSimpleBitVector(lhs);
203  if (!lhs)
204  return {};
205  rhs = context.convertToSimpleBitVector(rhs);
206  if (!rhs)
207  return {};
208  return builder.create<ConcreteOp>(loc, lhs, rhs);
209  }
210 
211  // Handle binary operators.
212  Value visit(const slang::ast::BinaryExpression &expr) {
213  auto lhs = context.convertRvalueExpression(expr.left());
214  if (!lhs)
215  return {};
216  auto rhs = context.convertRvalueExpression(expr.right());
217  if (!rhs)
218  return {};
219 
220  // Determine the domain of the result.
221  Domain domain = Domain::TwoValued;
222  if (expr.type->isFourState() || expr.left().type->isFourState() ||
223  expr.right().type->isFourState())
224  domain = Domain::FourValued;
225 
226  using slang::ast::BinaryOperator;
227  switch (expr.op) {
228  case BinaryOperator::Add:
229  return createBinary<moore::AddOp>(lhs, rhs);
230  case BinaryOperator::Subtract:
231  return createBinary<moore::SubOp>(lhs, rhs);
232  case BinaryOperator::Multiply:
233  return createBinary<moore::MulOp>(lhs, rhs);
234  case BinaryOperator::Divide:
235  if (expr.type->isSigned())
236  return createBinary<moore::DivSOp>(lhs, rhs);
237  else
238  return createBinary<moore::DivUOp>(lhs, rhs);
239  case BinaryOperator::Mod:
240  if (expr.type->isSigned())
241  return createBinary<moore::ModSOp>(lhs, rhs);
242  else
243  return createBinary<moore::ModUOp>(lhs, rhs);
244  case BinaryOperator::Power: {
245  // Slang casts the LHS and result of the `**` operator to a four-valued
246  // type, since the operator can return X even for two-valued inputs. To
247  // maintain uniform types across operands and results, cast the RHS to
248  // that four-valued type as well.
249  auto rhsCast =
250  builder.create<moore::ConversionOp>(loc, lhs.getType(), rhs);
251  if (expr.type->isSigned())
252  return createBinary<moore::PowSOp>(lhs, rhsCast);
253  else
254  return createBinary<moore::PowUOp>(lhs, rhsCast);
255  }
256 
257  case BinaryOperator::BinaryAnd:
258  return createBinary<moore::AndOp>(lhs, rhs);
259  case BinaryOperator::BinaryOr:
260  return createBinary<moore::OrOp>(lhs, rhs);
261  case BinaryOperator::BinaryXor:
262  return createBinary<moore::XorOp>(lhs, rhs);
263  case BinaryOperator::BinaryXnor: {
264  auto result = createBinary<moore::XorOp>(lhs, rhs);
265  if (!result)
266  return {};
267  return builder.create<moore::NotOp>(loc, result);
268  }
269 
270  case BinaryOperator::Equality:
271  return createBinary<moore::EqOp>(lhs, rhs);
272  case BinaryOperator::Inequality:
273  return createBinary<moore::NeOp>(lhs, rhs);
274  case BinaryOperator::CaseEquality:
275  return createBinary<moore::CaseEqOp>(lhs, rhs);
276  case BinaryOperator::CaseInequality:
277  return createBinary<moore::CaseNeOp>(lhs, rhs);
278  case BinaryOperator::WildcardEquality:
279  return createBinary<moore::WildcardEqOp>(lhs, rhs);
280  case BinaryOperator::WildcardInequality:
281  return createBinary<moore::WildcardNeOp>(lhs, rhs);
282 
283  case BinaryOperator::GreaterThanEqual:
284  if (expr.left().type->isSigned())
285  return createBinary<moore::SgeOp>(lhs, rhs);
286  else
287  return createBinary<moore::UgeOp>(lhs, rhs);
288  case BinaryOperator::GreaterThan:
289  if (expr.left().type->isSigned())
290  return createBinary<moore::SgtOp>(lhs, rhs);
291  else
292  return createBinary<moore::UgtOp>(lhs, rhs);
293  case BinaryOperator::LessThanEqual:
294  if (expr.left().type->isSigned())
295  return createBinary<moore::SleOp>(lhs, rhs);
296  else
297  return createBinary<moore::UleOp>(lhs, rhs);
298  case BinaryOperator::LessThan:
299  if (expr.left().type->isSigned())
300  return createBinary<moore::SltOp>(lhs, rhs);
301  else
302  return createBinary<moore::UltOp>(lhs, rhs);
303 
304  // See IEEE 1800-2017 ยง 11.4.7 "Logical operators".
305  case BinaryOperator::LogicalAnd: {
306  // TODO: This should short-circuit. Put the RHS code into a separate
307  // block.
308  lhs = context.convertToBool(lhs, domain);
309  if (!lhs)
310  return {};
311  rhs = context.convertToBool(rhs, domain);
312  if (!rhs)
313  return {};
314  return builder.create<moore::AndOp>(loc, lhs, rhs);
315  }
316  case BinaryOperator::LogicalOr: {
317  // TODO: This should short-circuit. Put the RHS code into a separate
318  // block.
319  lhs = context.convertToBool(lhs, domain);
320  if (!lhs)
321  return {};
322  rhs = context.convertToBool(rhs, domain);
323  if (!rhs)
324  return {};
325  return builder.create<moore::OrOp>(loc, lhs, rhs);
326  }
327  case BinaryOperator::LogicalImplication: {
328  // `(lhs -> rhs)` equivalent to `(!lhs || rhs)`.
329  lhs = context.convertToBool(lhs, domain);
330  if (!lhs)
331  return {};
332  rhs = context.convertToBool(rhs, domain);
333  if (!rhs)
334  return {};
335  auto notLHS = builder.create<moore::NotOp>(loc, lhs);
336  return builder.create<moore::OrOp>(loc, notLHS, rhs);
337  }
338  case BinaryOperator::LogicalEquivalence: {
339  // `(lhs <-> rhs)` equivalent to `(lhs && rhs) || (!lhs && !rhs)`.
340  lhs = context.convertToBool(lhs, domain);
341  if (!lhs)
342  return {};
343  rhs = context.convertToBool(rhs, domain);
344  if (!rhs)
345  return {};
346  auto notLHS = builder.create<moore::NotOp>(loc, lhs);
347  auto notRHS = builder.create<moore::NotOp>(loc, rhs);
348  auto both = builder.create<moore::AndOp>(loc, lhs, rhs);
349  auto notBoth = builder.create<moore::AndOp>(loc, notLHS, notRHS);
350  return builder.create<moore::OrOp>(loc, both, notBoth);
351  }
352 
353  case BinaryOperator::LogicalShiftLeft:
354  return createBinary<moore::ShlOp>(lhs, rhs);
355  case BinaryOperator::LogicalShiftRight:
356  return createBinary<moore::ShrOp>(lhs, rhs);
357  case BinaryOperator::ArithmeticShiftLeft:
358  return createBinary<moore::ShlOp>(lhs, rhs);
359  case BinaryOperator::ArithmeticShiftRight: {
360  // The `>>>` operator is an arithmetic right shift if the LHS operand is
361  // signed, or a logical right shift if the operand is unsigned.
362  lhs = context.convertToSimpleBitVector(lhs);
363  rhs = context.convertToSimpleBitVector(rhs);
364  if (!lhs || !rhs)
365  return {};
366  if (expr.type->isSigned())
367  return builder.create<moore::AShrOp>(loc, lhs, rhs);
368  return builder.create<moore::ShrOp>(loc, lhs, rhs);
369  }
370  }
371 
372  mlir::emitError(loc, "unsupported binary operator");
373  return {};
374  }
375 
376  // Handle `'0`, `'1`, `'x`, and `'z` literals.
377  Value visit(const slang::ast::UnbasedUnsizedIntegerLiteral &expr) {
378  return context.materializeSVInt(expr.getValue(), *expr.type, loc);
379  }
380 
381  // Handle integer literals.
382  Value visit(const slang::ast::IntegerLiteral &expr) {
383  return context.materializeSVInt(expr.getValue(), *expr.type, loc);
384  }
385 
386  // Handle concatenations.
387  Value visit(const slang::ast::ConcatenationExpression &expr) {
388  SmallVector<Value> operands;
389  for (auto *operand : expr.operands()) {
390  auto value = context.convertRvalueExpression(*operand);
391  if (!value)
392  continue;
393  value = context.convertToSimpleBitVector(value);
394  operands.push_back(value);
395  }
396  return builder.create<moore::ConcatOp>(loc, operands);
397  }
398 
399  // Handle replications.
400  Value visit(const slang::ast::ReplicationExpression &expr) {
401  auto type = context.convertType(*expr.type);
402  if (isa<moore::VoidType>(type))
403  return {};
404 
405  auto value = context.convertRvalueExpression(expr.concat());
406  if (!value)
407  return {};
408  return builder.create<moore::ReplicateOp>(loc, type, value);
409  }
410 
411  // Handle single bit selections.
412  Value visit(const slang::ast::ElementSelectExpression &expr) {
413  auto type = context.convertType(*expr.type);
414  auto value = context.convertRvalueExpression(expr.value());
415  if (!type || !value)
416  return {};
417  if (auto *constValue = expr.selector().constant) {
418  assert(!constValue->hasUnknown());
419  assert(constValue->size() <= 32);
420 
421  auto lowBit = constValue->integer().as<uint32_t>().value();
422  return builder.create<moore::ExtractOp>(loc, type, value, lowBit);
423  }
424  auto lowBit = context.convertRvalueExpression(expr.selector());
425  if (!lowBit)
426  return {};
427  return builder.create<moore::DynExtractOp>(loc, type, value, lowBit);
428  }
429 
430  // Handle range bits selections.
431  Value visit(const slang::ast::RangeSelectExpression &expr) {
432  auto type = context.convertType(*expr.type);
433  auto value = context.convertRvalueExpression(expr.value());
434  if (!type || !value)
435  return {};
436 
437  Value dynLowBit;
438  uint32_t constLowBit;
439  auto *leftConst = expr.left().constant;
440  auto *rightConst = expr.right().constant;
441  if (leftConst) {
442  assert(!leftConst->hasUnknown());
443  assert(leftConst->size() <= 32);
444  }
445  if (rightConst) {
446  assert(!rightConst->hasUnknown());
447  assert(rightConst->size() <= 32);
448  }
449 
450  if (expr.getSelectionKind() == slang::ast::RangeSelectionKind::Simple) {
451  if (leftConst && rightConst) {
452  // Estimate whether is big endian or little endian.
453  auto lhs = leftConst->integer().as<uint32_t>().value();
454  auto rhs = rightConst->integer().as<uint32_t>().value();
455  constLowBit = lhs < rhs ? lhs : rhs;
456  } else {
457  mlir::emitError(loc, "unsupported a variable as the index in the")
458  << slang::ast::toString(expr.getSelectionKind()) << "kind";
459  return {};
460  }
461  } else if (expr.getSelectionKind() ==
462  slang::ast::RangeSelectionKind::IndexedDown) {
463  // IndexedDown: arr[7-:8]. It's equivalent to arr[7:0] or arr[0:7]
464  // depending on little endian or bit endian. No matter which situation,
465  // the low bit must be "0".
466  if (leftConst) {
467  auto subtrahend = leftConst->integer().as<uint32_t>().value();
468  auto sliceWidth =
469  expr.right().constant->integer().as<uint32_t>().value();
470  constLowBit = subtrahend - sliceWidth - 1;
471  } else {
472  auto subtrahend = context.convertRvalueExpression(expr.left());
473  auto subtrahendType = cast<moore::UnpackedType>(subtrahend.getType());
474  auto intType = moore::IntType::get(context.getContext(),
475  subtrahendType.getBitSize().value(),
476  subtrahendType.getDomain());
477  auto sliceWidth =
478  expr.right().constant->integer().as<uint32_t>().value() - 1;
479  auto minuend =
480  builder.create<moore::ConstantOp>(loc, intType, sliceWidth);
481  dynLowBit = builder.create<moore::SubOp>(loc, subtrahend, minuend);
482  }
483  } else {
484  // IndexedUp: arr[0+:8]. "0" is the low bit, "8" is the bits slice width.
485  if (leftConst)
486  constLowBit = leftConst->integer().as<uint32_t>().value();
487  else
488  dynLowBit = context.convertRvalueExpression(expr.left());
489  }
490  if (leftConst && rightConst)
491  return builder.create<moore::ExtractOp>(loc, type, value, constLowBit);
492  return builder.create<moore::DynExtractOp>(loc, type, value, dynLowBit);
493  }
494 
495  Value visit(const slang::ast::MemberAccessExpression &expr) {
496  auto type = context.convertType(*expr.type);
497  auto valueType = expr.value().type;
498  auto value = context.convertRvalueExpression(expr.value());
499  if (!type || !value)
500  return {};
501  if (valueType->isStruct()) {
502  return builder.create<moore::StructExtractOp>(
503  loc, type, builder.getStringAttr(expr.member.name), value);
504  }
505  if (valueType->isPackedUnion() || valueType->isUnpackedUnion()) {
506  return builder.create<moore::UnionExtractOp>(
507  loc, type, builder.getStringAttr(expr.member.name), value);
508  }
509  mlir::emitError(loc, "expression of type ")
510  << value.getType() << " cannot be accessed";
511  return {};
512  }
513 
514  // Handle set membership operator.
515  Value visit(const slang::ast::InsideExpression &expr) {
516  auto lhs = context.convertToSimpleBitVector(
517  context.convertRvalueExpression(expr.left()));
518  if (!lhs)
519  return {};
520  // All conditions for determining whether it is inside.
521  SmallVector<Value> conditions;
522 
523  // Traverse open range list.
524  for (const auto *listExpr : expr.rangeList()) {
525  Value cond;
526  // The open range list on the right-hand side of the inside operator is a
527  // comma-separated list of expressions or ranges.
528  if (const auto *openRange =
529  listExpr->as_if<slang::ast::OpenRangeExpression>()) {
530  // Handle ranges.
531  auto lowBound = context.convertToSimpleBitVector(
532  context.convertRvalueExpression(openRange->left()));
533  auto highBound = context.convertToSimpleBitVector(
534  context.convertRvalueExpression(openRange->right()));
535  if (!lowBound || !highBound)
536  return {};
537  Value leftValue, rightValue;
538  // Determine if the expression on the left-hand side is inclusively
539  // within the range.
540  if (openRange->left().type->isSigned() ||
541  expr.left().type->isSigned()) {
542  leftValue = builder.create<moore::SgeOp>(loc, lhs, lowBound);
543  } else {
544  leftValue = builder.create<moore::UgeOp>(loc, lhs, lowBound);
545  }
546  if (openRange->right().type->isSigned() ||
547  expr.left().type->isSigned()) {
548  rightValue = builder.create<moore::SleOp>(loc, lhs, highBound);
549  } else {
550  rightValue = builder.create<moore::UleOp>(loc, lhs, highBound);
551  }
552  cond = builder.create<moore::AndOp>(loc, leftValue, rightValue);
553  } else {
554  // Handle expressions.
555  if (!listExpr->type->isSimpleBitVector()) {
556  if (listExpr->type->isUnpackedArray()) {
557  mlir::emitError(
558  loc, "unpacked arrays in 'inside' expressions not supported");
559  return {};
560  }
561  mlir::emitError(
562  loc, "only simple bit vectors supported in 'inside' expressions");
563  return {};
564  }
565  auto value = context.convertToSimpleBitVector(
566  context.convertRvalueExpression(*listExpr));
567  if (!value)
568  return {};
569  cond = builder.create<moore::WildcardEqOp>(loc, lhs, value);
570  }
571  conditions.push_back(cond);
572  }
573 
574  // Calculate the final result by `or` op.
575  auto result = conditions.back();
576  conditions.pop_back();
577  while (!conditions.empty()) {
578  result = builder.create<moore::OrOp>(loc, conditions.back(), result);
579  conditions.pop_back();
580  }
581  return result;
582  }
583 
584  // Handle conditional operator `?:`.
585  Value visit(const slang::ast::ConditionalExpression &expr) {
586  auto type = context.convertType(*expr.type);
587 
588  // Handle condition.
589  if (expr.conditions.size() > 1) {
590  mlir::emitError(loc)
591  << "unsupported conditional expression with more than one condition";
592  return {};
593  }
594  const auto &cond = expr.conditions[0];
595  if (cond.pattern) {
596  mlir::emitError(loc) << "unsupported conditional expression with pattern";
597  return {};
598  }
599  auto value =
600  context.convertToBool(context.convertRvalueExpression(*cond.expr));
601  if (!value)
602  return {};
603  auto conditionalOp = builder.create<moore::ConditionalOp>(loc, type, value);
604 
605  // Create blocks for true region and false region.
606  auto &trueBlock = conditionalOp.getTrueRegion().emplaceBlock();
607  auto &falseBlock = conditionalOp.getFalseRegion().emplaceBlock();
608 
609  OpBuilder::InsertionGuard g(builder);
610 
611  // Handle left expression.
612  builder.setInsertionPointToStart(&trueBlock);
613  auto trueValue = context.convertRvalueExpression(expr.left(), type);
614  if (!trueValue)
615  return {};
616  builder.create<moore::YieldOp>(loc, trueValue);
617 
618  // Handle right expression.
619  builder.setInsertionPointToStart(&falseBlock);
620  auto falseValue = context.convertRvalueExpression(expr.right(), type);
621  if (!falseValue)
622  return {};
623  builder.create<moore::YieldOp>(loc, falseValue);
624 
625  return conditionalOp.getResult();
626  }
627 
628  /// Handle calls.
629  Value visit(const slang::ast::CallExpression &expr) {
630  // Class method calls are currently not supported.
631  if (expr.thisClass()) {
632  mlir::emitError(loc, "unsupported class method call");
633  return {};
634  }
635 
636  // Try to materialize constant values directly.
637  auto constant = context.evaluateConstant(expr);
638  if (auto value = context.materializeConstant(constant, *expr.type, loc))
639  return value;
640 
641  return std::visit(
642  [&](auto &subroutine) { return visitCall(expr, subroutine); },
643  expr.subroutine);
644  }
645 
646  /// Handle subroutine calls.
647  Value visitCall(const slang::ast::CallExpression &expr,
648  const slang::ast::SubroutineSymbol *subroutine) {
649  auto *lowering = context.declareFunction(*subroutine);
650  if (!lowering)
651  return {};
652 
653  // Convert the call arguments. Input arguments are converted to an rvalue.
654  // All other arguments are converted to lvalues and passed into the function
655  // by reference.
656  SmallVector<Value> arguments;
657  for (auto [callArg, declArg] :
658  llvm::zip(expr.arguments(), subroutine->getArguments())) {
659 
660  // Unpack the `<expr> = EmptyArgument` pattern emitted by Slang for output
661  // and inout arguments.
662  auto *expr = callArg;
663  if (const auto *assign = expr->as_if<slang::ast::AssignmentExpression>())
664  expr = &assign->left();
665 
666  Value value;
667  if (declArg->direction == slang::ast::ArgumentDirection::In)
668  value = context.convertRvalueExpression(*expr);
669  else
670  value = context.convertLvalueExpression(*expr);
671  if (!value)
672  return {};
673  arguments.push_back(value);
674  }
675 
676  // Create the call.
677  auto callOp =
678  builder.create<mlir::func::CallOp>(loc, lowering->op, arguments);
679 
680  // For calls to void functions we need to have a value to return from this
681  // function. Create a dummy `unrealized_conversion_cast`, which will get
682  // deleted again later on.
683  if (callOp.getNumResults() == 0)
684  return builder
685  .create<mlir::UnrealizedConversionCastOp>(
686  loc, moore::VoidType::get(context.getContext()), ValueRange{})
687  .getResult(0);
688 
689  return callOp.getResult(0);
690  }
691 
692  /// Handle system calls.
693  Value visitCall(const slang::ast::CallExpression &expr,
694  const slang::ast::CallExpression::SystemCallInfo &info) {
695  const auto &subroutine = *info.subroutine;
696  auto args = expr.arguments();
697 
698  if (subroutine.name == "$signed" || subroutine.name == "$unsigned")
699  return context.convertRvalueExpression(*args[0]);
700 
701  if (subroutine.name == "$clog2") {
702  auto value = context.convertToSimpleBitVector(
703  context.convertRvalueExpression(*args[0]));
704  if (!value)
705  return {};
706  return builder.create<moore::Clog2BIOp>(loc, value);
707  }
708 
709  mlir::emitError(loc) << "unsupported system call `" << subroutine.name
710  << "`";
711  return {};
712  }
713 
714  /// Handle string literals.
715  Value visit(const slang::ast::StringLiteral &expr) {
716  auto type = context.convertType(*expr.type);
717  return builder.create<moore::StringConstantOp>(loc, type, expr.getValue());
718  }
719 
720  /// Handle assignment patterns.
721  Value visitAssignmentPattern(
722  const slang::ast::AssignmentPatternExpressionBase &expr,
723  unsigned replCount = 1) {
724  auto type = context.convertType(*expr.type);
725 
726  // Convert the individual elements first.
727  auto elementCount = expr.elements().size();
728  SmallVector<Value> elements;
729  elements.reserve(replCount * elementCount);
730  for (auto elementExpr : expr.elements()) {
731  auto value = context.convertRvalueExpression(*elementExpr);
732  if (!value)
733  return {};
734  elements.push_back(value);
735  }
736  for (unsigned replIdx = 1; replIdx < replCount; ++replIdx)
737  for (unsigned elementIdx = 0; elementIdx < elementCount; ++elementIdx)
738  elements.push_back(elements[elementIdx]);
739 
740  // Handle integers.
741  if (auto intType = dyn_cast<moore::IntType>(type)) {
742  assert(intType.getWidth() == elements.size());
743  std::reverse(elements.begin(), elements.end());
744  return builder.create<moore::ConcatOp>(loc, intType, elements);
745  }
746 
747  // Handle packed structs.
748  if (auto structType = dyn_cast<moore::StructType>(type)) {
749  assert(structType.getMembers().size() == elements.size());
750  return builder.create<moore::StructCreateOp>(loc, structType, elements);
751  }
752 
753  // Handle unpacked structs.
754  if (auto structType = dyn_cast<moore::UnpackedStructType>(type)) {
755  assert(structType.getMembers().size() == elements.size());
756  return builder.create<moore::StructCreateOp>(loc, structType, elements);
757  }
758 
759  // Handle packed arrays.
760  if (auto arrayType = dyn_cast<moore::ArrayType>(type)) {
761  assert(arrayType.getSize() == elements.size());
762  return builder.create<moore::ArrayCreateOp>(loc, arrayType, elements);
763  }
764 
765  // Handle unpacked arrays.
766  if (auto arrayType = dyn_cast<moore::UnpackedArrayType>(type)) {
767  assert(arrayType.getSize() == elements.size());
768  return builder.create<moore::ArrayCreateOp>(loc, arrayType, elements);
769  }
770 
771  mlir::emitError(loc) << "unsupported assignment pattern with type " << type;
772  return {};
773  }
774 
775  Value visit(const slang::ast::SimpleAssignmentPatternExpression &expr) {
776  return visitAssignmentPattern(expr);
777  }
778 
779  Value visit(const slang::ast::StructuredAssignmentPatternExpression &expr) {
780  return visitAssignmentPattern(expr);
781  }
782 
783  Value visit(const slang::ast::ReplicatedAssignmentPatternExpression &expr) {
784  auto count =
785  context.evaluateConstant(expr.count()).integer().as<unsigned>();
786  assert(count && "Slang guarantees constant non-zero replication count");
787  return visitAssignmentPattern(expr, *count);
788  }
789 
790  /// Emit an error for all other expressions.
791  template <typename T>
792  Value visit(T &&node) {
793  mlir::emitError(loc, "unsupported expression: ")
794  << slang::ast::toString(node.kind);
795  return {};
796  }
797 
798  Value visitInvalid(const slang::ast::Expression &expr) {
799  mlir::emitError(loc, "invalid expression");
800  return {};
801  }
802 };
803 } // namespace
804 
805 namespace {
806 struct LvalueExprVisitor {
807  Context &context;
808  Location loc;
809  OpBuilder &builder;
810 
811  LvalueExprVisitor(Context &context, Location loc)
812  : context(context), loc(loc), builder(context.builder) {}
813 
814  // Handle named values, such as references to declared variables.
815  Value visit(const slang::ast::NamedValueExpression &expr) {
816  if (auto value = context.valueSymbols.lookup(&expr.symbol))
817  return value;
818  auto d = mlir::emitError(loc, "unknown name `") << expr.symbol.name << "`";
819  d.attachNote(context.convertLocation(expr.symbol.location))
820  << "no lvalue generated for " << slang::ast::toString(expr.symbol.kind);
821  return {};
822  }
823 
824  // Handle concatenations.
825  Value visit(const slang::ast::ConcatenationExpression &expr) {
826  SmallVector<Value> operands;
827  for (auto *operand : expr.operands()) {
828  auto value = context.convertLvalueExpression(*operand);
829  if (!value)
830  continue;
831  operands.push_back(value);
832  }
833  return builder.create<moore::ConcatRefOp>(loc, operands);
834  }
835 
836  // Handle single bit selections.
837  Value visit(const slang::ast::ElementSelectExpression &expr) {
838  auto type = context.convertType(*expr.type);
839  auto value = context.convertLvalueExpression(expr.value());
840  if (!type || !value)
841  return {};
842  if (auto *constValue = expr.selector().constant) {
843  assert(!constValue->hasUnknown());
844  assert(constValue->size() <= 32);
845 
846  auto lowBit = constValue->integer().as<uint32_t>().value();
847  return builder.create<moore::ExtractRefOp>(
848  loc, moore::RefType::get(cast<moore::UnpackedType>(type)), value,
849  lowBit);
850  }
851  auto lowBit = context.convertRvalueExpression(expr.selector());
852  if (!lowBit)
853  return {};
854  return builder.create<moore::DynExtractRefOp>(
855  loc, moore::RefType::get(cast<moore::UnpackedType>(type)), value,
856  lowBit);
857  }
858 
859  // Handle range bits selections.
860  Value visit(const slang::ast::RangeSelectExpression &expr) {
861  auto type = context.convertType(*expr.type);
862  auto value = context.convertLvalueExpression(expr.value());
863  if (!type || !value)
864  return {};
865 
866  Value dynLowBit;
867  uint32_t constLowBit;
868  auto *leftConst = expr.left().constant;
869  auto *rightConst = expr.right().constant;
870  if (leftConst) {
871  assert(!leftConst->hasUnknown());
872  assert(leftConst->size() <= 32);
873  }
874  if (rightConst) {
875  assert(!rightConst->hasUnknown());
876  assert(rightConst->size() <= 32);
877  }
878 
879  if (expr.getSelectionKind() == slang::ast::RangeSelectionKind::Simple) {
880  if (leftConst && rightConst) {
881  // Estimate whether is big endian or little endian.
882  auto lhs = leftConst->integer().as<uint32_t>().value();
883  auto rhs = rightConst->integer().as<uint32_t>().value();
884  constLowBit = lhs < rhs ? lhs : rhs;
885  } else {
886  mlir::emitError(loc, "unsupported a variable as the index in the")
887  << slang::ast::toString(expr.getSelectionKind()) << "kind";
888  return {};
889  }
890  } else if (expr.getSelectionKind() ==
891  slang::ast::RangeSelectionKind::IndexedDown) {
892  // IndexedDown: arr[7-:8]. It's equivalent to arr[7:0] or arr[0:7]
893  // depending on little endian or bit endian. No matter which situation,
894  // the low bit must be "0".
895  if (leftConst) {
896  auto subtrahend = leftConst->integer().as<uint32_t>().value();
897  auto sliceWidth =
898  expr.right().constant->integer().as<uint32_t>().value();
899  constLowBit = subtrahend - sliceWidth - 1;
900  } else {
901  auto subtrahend = context.convertRvalueExpression(expr.left());
902  auto subtrahendType = cast<moore::UnpackedType>(subtrahend.getType());
903  auto intType = moore::IntType::get(context.getContext(),
904  subtrahendType.getBitSize().value(),
905  subtrahendType.getDomain());
906  auto sliceWidth =
907  expr.right().constant->integer().as<uint32_t>().value() - 1;
908  auto minuend =
909  builder.create<moore::ConstantOp>(loc, intType, sliceWidth);
910  dynLowBit = builder.create<moore::SubOp>(loc, subtrahend, minuend);
911  }
912  } else {
913  // IndexedUp: arr[0+:8]. "0" is the low bit, "8" is the bits slice width.
914  if (leftConst)
915  constLowBit = leftConst->integer().as<uint32_t>().value();
916  else
917  dynLowBit = context.convertRvalueExpression(expr.left());
918  }
919  if (leftConst && rightConst)
920  return builder.create<moore::ExtractRefOp>(
921  loc, moore::RefType::get(cast<moore::UnpackedType>(type)), value,
922  constLowBit);
923  return builder.create<moore::DynExtractRefOp>(
924  loc, moore::RefType::get(cast<moore::UnpackedType>(type)), value,
925  dynLowBit);
926  }
927 
928  Value visit(const slang::ast::MemberAccessExpression &expr) {
929  auto type = context.convertType(*expr.type);
930  auto valueType = expr.value().type;
931  auto value = context.convertLvalueExpression(expr.value());
932  if (!type || !value)
933  return {};
934  if (valueType->isStruct()) {
935  return builder.create<moore::StructExtractRefOp>(
936  loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
937  builder.getStringAttr(expr.member.name), value);
938  }
939  if (valueType->isPackedUnion() || valueType->isUnpackedUnion()) {
940  return builder.create<moore::UnionExtractRefOp>(
941  loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
942  builder.getStringAttr(expr.member.name), value);
943  }
944  mlir::emitError(loc, "expression of type ")
945  << value.getType() << " cannot be accessed";
946  return {};
947  }
948 
949  /// Emit an error for all other expressions.
950  template <typename T>
951  Value visit(T &&node) {
952  return context.convertRvalueExpression(node);
953  }
954 
955  Value visitInvalid(const slang::ast::Expression &expr) {
956  mlir::emitError(loc, "invalid expression");
957  return {};
958  }
959 };
960 } // namespace
961 
962 Value Context::convertRvalueExpression(const slang::ast::Expression &expr,
963  Type requiredType) {
964  auto loc = convertLocation(expr.sourceRange);
965  auto value = expr.visit(RvalueExprVisitor(*this, loc));
966  if (value && requiredType)
967  value =
968  materializeConversion(requiredType, value, expr.type->isSigned(), loc);
969  return value;
970 }
971 
972 Value Context::convertLvalueExpression(const slang::ast::Expression &expr) {
973  auto loc = convertLocation(expr.sourceRange);
974  return expr.visit(LvalueExprVisitor(*this, loc));
975 }
976 // NOLINTEND(misc-no-recursion)
977 
978 /// Helper function to convert a value to its "truthy" boolean value.
979 Value Context::convertToBool(Value value) {
980  if (!value)
981  return {};
982  if (auto type = dyn_cast_or_null<moore::IntType>(value.getType()))
983  if (type.getBitSize() == 1)
984  return value;
985  if (auto type = dyn_cast_or_null<moore::UnpackedType>(value.getType()))
986  return builder.create<moore::BoolCastOp>(value.getLoc(), value);
987  mlir::emitError(value.getLoc(), "expression of type ")
988  << value.getType() << " cannot be cast to a boolean";
989  return {};
990 }
991 
992 /// Materialize a Slang integer literal as a constant op.
993 Value Context::materializeSVInt(const slang::SVInt &svint,
994  const slang::ast::Type &astType, Location loc) {
995  auto type = convertType(astType);
996  if (!type)
997  return {};
998 
999  bool typeIsFourValued = false;
1000  if (auto unpackedType = dyn_cast<moore::UnpackedType>(type))
1001  typeIsFourValued = unpackedType.getDomain() == moore::Domain::FourValued;
1002 
1003  auto fvint = convertSVIntToFVInt(svint);
1004  auto intType = moore::IntType::get(getContext(), fvint.getBitWidth(),
1005  fvint.hasUnknown() || typeIsFourValued
1008  Value result = builder.create<moore::ConstantOp>(loc, intType, fvint);
1009  if (result.getType() != type)
1010  result = builder.create<moore::ConversionOp>(loc, type, result);
1011  return result;
1012 }
1013 
1014 Value Context::materializeConstant(const slang::ConstantValue &constant,
1015  const slang::ast::Type &type, Location loc) {
1016  if (constant.isInteger())
1017  return materializeSVInt(constant.integer(), type, loc);
1018  return {};
1019 }
1020 
1021 slang::ConstantValue
1022 Context::evaluateConstant(const slang::ast::Expression &expr) {
1023  using slang::ast::EvalFlags;
1024  slang::ast::EvalContext evalContext(
1025  compilation, EvalFlags::CacheResults | EvalFlags::SpecparamsAllowed);
1026  return expr.eval(evalContext);
1027 }
1028 
1029 /// Helper function to convert a value to its "truthy" boolean value and
1030 /// convert it to the given domain.
1031 Value Context::convertToBool(Value value, Domain domain) {
1032  value = convertToBool(value);
1033  if (!value)
1034  return {};
1035  auto type = moore::IntType::get(getContext(), 1, domain);
1036  if (value.getType() == type)
1037  return value;
1038  return builder.create<moore::ConversionOp>(value.getLoc(), type, value);
1039 }
1040 
1042  if (!value)
1043  return {};
1044  if (isa<moore::IntType>(value.getType()))
1045  return value;
1046 
1047  // Some operations in Slang's AST, for example bitwise or `|`, don't cast
1048  // packed struct/array operands to simple bit vectors but directly operate
1049  // on the struct/array. Since the corresponding IR ops operate only on
1050  // simple bit vectors, insert a conversion in this case.
1051  if (auto packed = dyn_cast<moore::PackedType>(value.getType())) {
1052  if (auto bits = packed.getBitSize()) {
1053  auto sbvType =
1054  moore::IntType::get(value.getContext(), *bits, packed.getDomain());
1055  return builder.create<moore::ConversionOp>(value.getLoc(), sbvType,
1056  value);
1057  }
1058  }
1059 
1060  mlir::emitError(value.getLoc()) << "expression of type " << value.getType()
1061  << " cannot be cast to a simple bit vector";
1062  return {};
1063 }
1064 
1065 Value Context::materializeConversion(Type type, Value value, bool isSigned,
1066  Location loc) {
1067  if (type == value.getType())
1068  return value;
1069  auto dstPacked = dyn_cast<moore::PackedType>(type);
1070  auto srcPacked = dyn_cast<moore::PackedType>(value.getType());
1071 
1072  // Resize the value if needed.
1073  if (dstPacked && srcPacked && dstPacked.getBitSize() &&
1074  srcPacked.getBitSize() &&
1075  *dstPacked.getBitSize() != *srcPacked.getBitSize()) {
1076  auto dstWidth = *dstPacked.getBitSize();
1077  auto srcWidth = *srcPacked.getBitSize();
1078 
1079  // Convert the value to a simple bit vector which we can extend or truncate.
1080  auto srcWidthType = moore::IntType::get(value.getContext(), srcWidth,
1081  srcPacked.getDomain());
1082  if (value.getType() != srcWidthType)
1083  value = builder.create<moore::ConversionOp>(value.getLoc(), srcWidthType,
1084  value);
1085 
1086  // Create truncation or sign/zero extension ops depending on the source and
1087  // destination width.
1088  auto dstWidthType = moore::IntType::get(value.getContext(), dstWidth,
1089  srcPacked.getDomain());
1090  if (dstWidth < srcWidth) {
1091  value = builder.create<moore::TruncOp>(loc, dstWidthType, value);
1092  } else if (dstWidth > srcWidth) {
1093  if (isSigned)
1094  value = builder.create<moore::SExtOp>(loc, dstWidthType, value);
1095  else
1096  value = builder.create<moore::ZExtOp>(loc, dstWidthType, value);
1097  }
1098  }
1099 
1100  if (value.getType() != type)
1101  value = builder.create<moore::ConversionOp>(loc, type, value);
1102  return value;
1103 }
assert(baseType &&"element must be base type")
static FVInt convertSVIntToFVInt(const slang::SVInt &svint)
Convert a Slang SVInt to a CIRCT FVInt.
Definition: Expressions.cpp:18
const char * toString(Flow flow)
Definition: FIRRTLOps.cpp:197
Four-valued arbitrary precision integers.
Definition: FVInt.h:37
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
Domain
The number of values each bit of a type can assume.
Definition: MooreTypes.h:47
@ FourValued
Four-valued types such as logic or integer.
@ TwoValued
Two-valued types such as bit or int.
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 materializeConversion(Type type, Value value, bool isSigned, Location loc)
Helper function to insert the necessary operations to cast a value from one type to another.
Value convertLvalueExpression(const slang::ast::Expression &expr)
Value materializeConstant(const slang::ConstantValue &constant, const slang::ast::Type &type, Location loc)
Helper function to materialize a ConstantValue as an SSA value.
slang::ConstantValue evaluateConstant(const slang::ast::Expression &expr)
Evaluate the constant value of an expression.
slang::ast::Compilation & compilation
OpBuilder builder
The builder used to create IR operations.
std::function< void(moore::ReadOp)> rvalueReadCallback
A listener called for every variable or net being read.
Type convertType(const slang::ast::Type &type, LocationAttr loc={})
Convert a slang type into an MLIR type.
Definition: Types.cpp:167
Value materializeSVInt(const slang::SVInt &svint, const slang::ast::Type &type, Location loc)
Helper function to materialize an SVInt as an SSA value.
Value convertToBool(Value value)
Helper function to convert a value to its "truthy" boolean value.
Value convertRvalueExpression(const slang::ast::Expression &expr, Type requiredType={})
Value convertToSimpleBitVector(Value value)
Helper function to convert a value to its simple bit vector representation, if it has one.
FunctionLowering * declareFunction(const slang::ast::SubroutineSymbol &subroutine)
Convert a function and its arguments to a function declaration in the IR.
Definition: Structure.cpp:816
MLIRContext * getContext()
Return the MLIR context.
SmallVector< Value > lvalueStack
A stack of assignment left-hand side values.
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.