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