Loading [MathJax]/extensions/tex2jax.js
CIRCT 21.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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
13using namespace circt;
14using namespace ImportVerilog;
15using moore::Domain;
16
17/// Convert a Slang `SVInt` to a CIRCT `FVInt`.
18static 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)
31namespace {
32struct 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 if (isa<moore::UnpackedArrayType>(lhs.getType()))
294 return builder.create<moore::UArrayCmpOp>(
295 loc, moore::UArrayCmpPredicate::eq, lhs, rhs);
296 else if (isa<moore::StringType>(lhs.getType()))
297 return builder.create<moore::StringCmpOp>(
298 loc, moore::StringCmpPredicate::eq, lhs, rhs);
299 else
300 return createBinary<moore::EqOp>(lhs, rhs);
301 case BinaryOperator::Inequality:
302 if (isa<moore::UnpackedArrayType>(lhs.getType()))
303 return builder.create<moore::UArrayCmpOp>(
304 loc, moore::UArrayCmpPredicate::ne, lhs, rhs);
305 else if (isa<moore::StringType>(lhs.getType()))
306 return builder.create<moore::StringCmpOp>(
307 loc, moore::StringCmpPredicate::ne, lhs, rhs);
308 else
309 return createBinary<moore::NeOp>(lhs, rhs);
310 case BinaryOperator::CaseEquality:
311 return createBinary<moore::CaseEqOp>(lhs, rhs);
312 case BinaryOperator::CaseInequality:
313 return createBinary<moore::CaseNeOp>(lhs, rhs);
314 case BinaryOperator::WildcardEquality:
315 return createBinary<moore::WildcardEqOp>(lhs, rhs);
316 case BinaryOperator::WildcardInequality:
317 return createBinary<moore::WildcardNeOp>(lhs, rhs);
318
319 case BinaryOperator::GreaterThanEqual:
320 if (expr.left().type->isSigned())
321 return createBinary<moore::SgeOp>(lhs, rhs);
322 else if (isa<moore::StringType>(lhs.getType()))
323 return builder.create<moore::StringCmpOp>(
324 loc, moore::StringCmpPredicate::ge, lhs, rhs);
325 else
326 return createBinary<moore::UgeOp>(lhs, rhs);
327 case BinaryOperator::GreaterThan:
328 if (expr.left().type->isSigned())
329 return createBinary<moore::SgtOp>(lhs, rhs);
330 else if (isa<moore::StringType>(lhs.getType()))
331 return builder.create<moore::StringCmpOp>(
332 loc, moore::StringCmpPredicate::gt, lhs, rhs);
333 else
334 return createBinary<moore::UgtOp>(lhs, rhs);
335 case BinaryOperator::LessThanEqual:
336 if (expr.left().type->isSigned())
337 return createBinary<moore::SleOp>(lhs, rhs);
338 else if (isa<moore::StringType>(lhs.getType()))
339 return builder.create<moore::StringCmpOp>(
340 loc, moore::StringCmpPredicate::le, lhs, rhs);
341 else
342 return createBinary<moore::UleOp>(lhs, rhs);
343 case BinaryOperator::LessThan:
344 if (expr.left().type->isSigned())
345 return createBinary<moore::SltOp>(lhs, rhs);
346 else if (isa<moore::StringType>(lhs.getType()))
347 return builder.create<moore::StringCmpOp>(
348 loc, moore::StringCmpPredicate::lt, lhs, rhs);
349 else
350 return createBinary<moore::UltOp>(lhs, rhs);
351
352 // See IEEE 1800-2017 § 11.4.7 "Logical operators".
353 case BinaryOperator::LogicalAnd: {
354 // TODO: This should short-circuit. Put the RHS code into a separate
355 // block.
356 lhs = context.convertToBool(lhs, domain);
357 if (!lhs)
358 return {};
359 rhs = context.convertToBool(rhs, domain);
360 if (!rhs)
361 return {};
362 return builder.create<moore::AndOp>(loc, lhs, rhs);
363 }
364 case BinaryOperator::LogicalOr: {
365 // TODO: This should short-circuit. Put the RHS code into a separate
366 // block.
367 lhs = context.convertToBool(lhs, domain);
368 if (!lhs)
369 return {};
370 rhs = context.convertToBool(rhs, domain);
371 if (!rhs)
372 return {};
373 return builder.create<moore::OrOp>(loc, lhs, rhs);
374 }
375 case BinaryOperator::LogicalImplication: {
376 // `(lhs -> rhs)` equivalent to `(!lhs || rhs)`.
377 lhs = context.convertToBool(lhs, domain);
378 if (!lhs)
379 return {};
380 rhs = context.convertToBool(rhs, domain);
381 if (!rhs)
382 return {};
383 auto notLHS = builder.create<moore::NotOp>(loc, lhs);
384 return builder.create<moore::OrOp>(loc, notLHS, rhs);
385 }
386 case BinaryOperator::LogicalEquivalence: {
387 // `(lhs <-> rhs)` equivalent to `(lhs && rhs) || (!lhs && !rhs)`.
388 lhs = context.convertToBool(lhs, domain);
389 if (!lhs)
390 return {};
391 rhs = context.convertToBool(rhs, domain);
392 if (!rhs)
393 return {};
394 auto notLHS = builder.create<moore::NotOp>(loc, lhs);
395 auto notRHS = builder.create<moore::NotOp>(loc, rhs);
396 auto both = builder.create<moore::AndOp>(loc, lhs, rhs);
397 auto notBoth = builder.create<moore::AndOp>(loc, notLHS, notRHS);
398 return builder.create<moore::OrOp>(loc, both, notBoth);
399 }
400
401 case BinaryOperator::LogicalShiftLeft:
402 return createBinary<moore::ShlOp>(lhs, rhs);
403 case BinaryOperator::LogicalShiftRight:
404 return createBinary<moore::ShrOp>(lhs, rhs);
405 case BinaryOperator::ArithmeticShiftLeft:
406 return createBinary<moore::ShlOp>(lhs, rhs);
407 case BinaryOperator::ArithmeticShiftRight: {
408 // The `>>>` operator is an arithmetic right shift if the LHS operand is
409 // signed, or a logical right shift if the operand is unsigned.
410 lhs = context.convertToSimpleBitVector(lhs);
411 rhs = context.convertToSimpleBitVector(rhs);
412 if (!lhs || !rhs)
413 return {};
414 if (expr.type->isSigned())
415 return builder.create<moore::AShrOp>(loc, lhs, rhs);
416 return builder.create<moore::ShrOp>(loc, lhs, rhs);
417 }
418 }
419
420 mlir::emitError(loc, "unsupported binary operator");
421 return {};
422 }
423
424 // Handle `'0`, `'1`, `'x`, and `'z` literals.
425 Value visit(const slang::ast::UnbasedUnsizedIntegerLiteral &expr) {
426 return context.materializeSVInt(expr.getValue(), *expr.type, loc);
427 }
428
429 // Handle integer literals.
430 Value visit(const slang::ast::IntegerLiteral &expr) {
431 return context.materializeSVInt(expr.getValue(), *expr.type, loc);
432 }
433
434 // Handle concatenations.
435 Value visit(const slang::ast::ConcatenationExpression &expr) {
436 SmallVector<Value> operands;
437 for (auto *operand : expr.operands()) {
438 // Handle empty replications like `{0{...}}` which may occur within
439 // concatenations. Slang assigns them a `void` type which we can check for
440 // here.
441 if (operand->type->isVoid())
442 continue;
443 auto value = context.convertRvalueExpression(*operand);
444 value = context.convertToSimpleBitVector(value);
445 if (!value)
446 return {};
447 operands.push_back(value);
448 }
449 return builder.create<moore::ConcatOp>(loc, operands);
450 }
451
452 // Handle replications.
453 Value visit(const slang::ast::ReplicationExpression &expr) {
454 auto type = context.convertType(*expr.type);
455 auto value = context.convertRvalueExpression(expr.concat());
456 if (!value)
457 return {};
458 return builder.create<moore::ReplicateOp>(loc, type, value);
459 }
460
461 Value getSelectIndex(Value index, const slang::ConstantRange &range) const {
462 auto indexType = cast<moore::UnpackedType>(index.getType());
463 auto bw = std::max(llvm::Log2_32_Ceil(std::max(std::abs(range.lower()),
464 std::abs(range.upper()))),
465 indexType.getBitSize().value());
466 auto intType =
467 moore::IntType::get(index.getContext(), bw, indexType.getDomain());
468
469 if (range.isLittleEndian()) {
470 if (range.lower() == 0)
471 return index;
472
473 Value newIndex =
474 builder.createOrFold<moore::ConversionOp>(loc, intType, index);
475 Value offset = builder.create<moore::ConstantOp>(
476 loc, intType, range.lower(), /*isSigned = */ range.lower() < 0);
477 return builder.createOrFold<moore::SubOp>(loc, newIndex, offset);
478 }
479
480 if (range.upper() == 0)
481 return builder.createOrFold<moore::NegOp>(loc, index);
482
483 Value newIndex =
484 builder.createOrFold<moore::ConversionOp>(loc, intType, index);
485 Value offset = builder.create<moore::ConstantOp>(
486 loc, intType, range.upper(), /* isSigned = */ range.upper() < 0);
487 return builder.createOrFold<moore::SubOp>(loc, offset, newIndex);
488 }
489
490 // Handle single bit selections.
491 Value visit(const slang::ast::ElementSelectExpression &expr) {
492 auto type = context.convertType(*expr.type);
493 auto value = context.convertRvalueExpression(expr.value());
494 if (!type || !value)
495 return {};
496 auto range = expr.value().type->getFixedRange();
497 if (auto *constValue = expr.selector().constant) {
498 assert(!constValue->hasUnknown());
499 assert(constValue->size() <= 32);
500
501 auto lowBit = constValue->integer().as<uint32_t>().value();
502 return builder.create<moore::ExtractOp>(loc, type, value,
503 range.translateIndex(lowBit));
504 }
505 auto lowBit = context.convertRvalueExpression(expr.selector());
506 if (!lowBit)
507 return {};
508 return builder.create<moore::DynExtractOp>(loc, type, value,
509 getSelectIndex(lowBit, range));
510 }
511
512 // Handle range bits selections.
513 Value visit(const slang::ast::RangeSelectExpression &expr) {
514 auto type = context.convertType(*expr.type);
515 auto value = context.convertRvalueExpression(expr.value());
516 if (!type || !value)
517 return {};
518
519 Value dynLowBit;
520 uint32_t constLowBit;
521 auto *leftConst = expr.left().constant;
522 auto *rightConst = expr.right().constant;
523 if (leftConst) {
524 assert(!leftConst->hasUnknown());
525 assert(leftConst->size() <= 32);
526 }
527 if (rightConst) {
528 assert(!rightConst->hasUnknown());
529 assert(rightConst->size() <= 32);
530 }
531
532 if (expr.getSelectionKind() == slang::ast::RangeSelectionKind::Simple) {
533 if (leftConst && rightConst) {
534 // Estimate whether is big endian or little endian.
535 auto lhs = leftConst->integer().as<uint32_t>().value();
536 auto rhs = rightConst->integer().as<uint32_t>().value();
537 constLowBit = lhs < rhs ? lhs : rhs;
538 } else {
539 mlir::emitError(loc, "unsupported a variable as the index in the")
540 << slang::ast::toString(expr.getSelectionKind()) << "kind";
541 return {};
542 }
543 } else if (expr.getSelectionKind() ==
544 slang::ast::RangeSelectionKind::IndexedDown) {
545 // IndexedDown: arr[7-:8]. It's equivalent to arr[7:0] or arr[0:7]
546 // depending on little endian or bit endian. No matter which situation,
547 // the low bit must be "0".
548 if (leftConst) {
549 auto subtrahend = leftConst->integer().as<uint32_t>().value();
550 auto sliceWidth =
551 expr.right().constant->integer().as<uint32_t>().value();
552 constLowBit = subtrahend - sliceWidth - 1;
553 } else {
554 auto subtrahend = context.convertRvalueExpression(expr.left());
555 auto subtrahendType = cast<moore::UnpackedType>(subtrahend.getType());
556 auto intType = moore::IntType::get(context.getContext(),
557 subtrahendType.getBitSize().value(),
558 subtrahendType.getDomain());
559 auto sliceWidth =
560 expr.right().constant->integer().as<uint32_t>().value() - 1;
561 auto minuend = builder.create<moore::ConstantOp>(
562 loc, intType, sliceWidth, expr.left().type->isSigned());
563 dynLowBit = builder.create<moore::SubOp>(loc, subtrahend, minuend);
564 }
565 } else {
566 // IndexedUp: arr[0+:8]. "0" is the low bit, "8" is the bits slice width.
567 if (leftConst)
568 constLowBit = leftConst->integer().as<uint32_t>().value();
569 else
570 dynLowBit = context.convertRvalueExpression(expr.left());
571 }
572 auto range = expr.value().type->getFixedRange();
573 if (leftConst && rightConst)
574 return builder.create<moore::ExtractOp>(
575 loc, type, value, range.translateIndex(constLowBit));
576 return builder.create<moore::DynExtractOp>(
577 loc, type, value, getSelectIndex(dynLowBit, range));
578 }
579
580 Value visit(const slang::ast::MemberAccessExpression &expr) {
581 auto type = context.convertType(*expr.type);
582 auto valueType = expr.value().type;
583 auto value = context.convertRvalueExpression(expr.value());
584 if (!type || !value)
585 return {};
586 if (valueType->isStruct()) {
587 return builder.create<moore::StructExtractOp>(
588 loc, type, builder.getStringAttr(expr.member.name), value);
589 }
590 if (valueType->isPackedUnion() || valueType->isUnpackedUnion()) {
591 return builder.create<moore::UnionExtractOp>(
592 loc, type, builder.getStringAttr(expr.member.name), value);
593 }
594 mlir::emitError(loc, "expression of type ")
595 << value.getType() << " cannot be accessed";
596 return {};
597 }
598
599 // Handle set membership operator.
600 Value visit(const slang::ast::InsideExpression &expr) {
601 auto lhs = context.convertToSimpleBitVector(
602 context.convertRvalueExpression(expr.left()));
603 if (!lhs)
604 return {};
605 // All conditions for determining whether it is inside.
606 SmallVector<Value> conditions;
607
608 // Traverse open range list.
609 for (const auto *listExpr : expr.rangeList()) {
610 Value cond;
611 // The open range list on the right-hand side of the inside operator is a
612 // comma-separated list of expressions or ranges.
613 if (const auto *openRange =
614 listExpr->as_if<slang::ast::OpenRangeExpression>()) {
615 // Handle ranges.
616 auto lowBound = context.convertToSimpleBitVector(
617 context.convertRvalueExpression(openRange->left()));
618 auto highBound = context.convertToSimpleBitVector(
619 context.convertRvalueExpression(openRange->right()));
620 if (!lowBound || !highBound)
621 return {};
622 Value leftValue, rightValue;
623 // Determine if the expression on the left-hand side is inclusively
624 // within the range.
625 if (openRange->left().type->isSigned() ||
626 expr.left().type->isSigned()) {
627 leftValue = builder.create<moore::SgeOp>(loc, lhs, lowBound);
628 } else {
629 leftValue = builder.create<moore::UgeOp>(loc, lhs, lowBound);
630 }
631 if (openRange->right().type->isSigned() ||
632 expr.left().type->isSigned()) {
633 rightValue = builder.create<moore::SleOp>(loc, lhs, highBound);
634 } else {
635 rightValue = builder.create<moore::UleOp>(loc, lhs, highBound);
636 }
637 cond = builder.create<moore::AndOp>(loc, leftValue, rightValue);
638 } else {
639 // Handle expressions.
640 if (!listExpr->type->isIntegral()) {
641 if (listExpr->type->isUnpackedArray()) {
642 mlir::emitError(
643 loc, "unpacked arrays in 'inside' expressions not supported");
644 return {};
645 }
646 mlir::emitError(
647 loc, "only simple bit vectors supported in 'inside' expressions");
648 return {};
649 }
650
651 auto value = context.convertToSimpleBitVector(
652 context.convertRvalueExpression(*listExpr));
653 if (!value)
654 return {};
655 cond = builder.create<moore::WildcardEqOp>(loc, lhs, value);
656 }
657 conditions.push_back(cond);
658 }
659
660 // Calculate the final result by `or` op.
661 auto result = conditions.back();
662 conditions.pop_back();
663 while (!conditions.empty()) {
664 result = builder.create<moore::OrOp>(loc, conditions.back(), result);
665 conditions.pop_back();
666 }
667 return result;
668 }
669
670 // Handle conditional operator `?:`.
671 Value visit(const slang::ast::ConditionalExpression &expr) {
672 auto type = context.convertType(*expr.type);
673
674 // Handle condition.
675 if (expr.conditions.size() > 1) {
676 mlir::emitError(loc)
677 << "unsupported conditional expression with more than one condition";
678 return {};
679 }
680 const auto &cond = expr.conditions[0];
681 if (cond.pattern) {
682 mlir::emitError(loc) << "unsupported conditional expression with pattern";
683 return {};
684 }
685 auto value =
686 context.convertToBool(context.convertRvalueExpression(*cond.expr));
687 if (!value)
688 return {};
689 auto conditionalOp = builder.create<moore::ConditionalOp>(loc, type, value);
690
691 // Create blocks for true region and false region.
692 auto &trueBlock = conditionalOp.getTrueRegion().emplaceBlock();
693 auto &falseBlock = conditionalOp.getFalseRegion().emplaceBlock();
694
695 OpBuilder::InsertionGuard g(builder);
696
697 // Handle left expression.
698 builder.setInsertionPointToStart(&trueBlock);
699 auto trueValue = context.convertRvalueExpression(expr.left(), type);
700 if (!trueValue)
701 return {};
702 builder.create<moore::YieldOp>(loc, trueValue);
703
704 // Handle right expression.
705 builder.setInsertionPointToStart(&falseBlock);
706 auto falseValue = context.convertRvalueExpression(expr.right(), type);
707 if (!falseValue)
708 return {};
709 builder.create<moore::YieldOp>(loc, falseValue);
710
711 return conditionalOp.getResult();
712 }
713
714 /// Handle calls.
715 Value visit(const slang::ast::CallExpression &expr) {
716 // Class method calls are currently not supported.
717 if (expr.thisClass()) {
718 mlir::emitError(loc, "unsupported class method call");
719 return {};
720 }
721
722 // Try to materialize constant values directly.
723 auto constant = context.evaluateConstant(expr);
724 if (auto value = context.materializeConstant(constant, *expr.type, loc))
725 return value;
726
727 return std::visit(
728 [&](auto &subroutine) { return visitCall(expr, subroutine); },
729 expr.subroutine);
730 }
731
732 /// Handle subroutine calls.
733 Value visitCall(const slang::ast::CallExpression &expr,
734 const slang::ast::SubroutineSymbol *subroutine) {
735 auto *lowering = context.declareFunction(*subroutine);
736 if (!lowering)
737 return {};
738
739 // Convert the call arguments. Input arguments are converted to an rvalue.
740 // All other arguments are converted to lvalues and passed into the function
741 // by reference.
742 SmallVector<Value> arguments;
743 for (auto [callArg, declArg] :
744 llvm::zip(expr.arguments(), subroutine->getArguments())) {
745
746 // Unpack the `<expr> = EmptyArgument` pattern emitted by Slang for output
747 // and inout arguments.
748 auto *expr = callArg;
749 if (const auto *assign = expr->as_if<slang::ast::AssignmentExpression>())
750 expr = &assign->left();
751
752 Value value;
753 if (declArg->direction == slang::ast::ArgumentDirection::In)
754 value = context.convertRvalueExpression(*expr);
755 else
756 value = context.convertLvalueExpression(*expr);
757 if (!value)
758 return {};
759 arguments.push_back(value);
760 }
761
762 // Create the call.
763 auto callOp =
764 builder.create<mlir::func::CallOp>(loc, lowering->op, arguments);
765
766 // For calls to void functions we need to have a value to return from this
767 // function. Create a dummy `unrealized_conversion_cast`, which will get
768 // deleted again later on.
769 if (callOp.getNumResults() == 0)
770 return builder
771 .create<mlir::UnrealizedConversionCastOp>(
772 loc, moore::VoidType::get(context.getContext()), ValueRange{})
773 .getResult(0);
774
775 return callOp.getResult(0);
776 }
777
778 /// Handle system calls.
779 Value visitCall(const slang::ast::CallExpression &expr,
780 const slang::ast::CallExpression::SystemCallInfo &info) {
781 const auto &subroutine = *info.subroutine;
782 auto args = expr.arguments();
783
784 if (args.size() == 1) {
785 auto value = context.convertRvalueExpression(*args[0]);
786 if (!value)
787 return {};
788 auto result = context.convertSystemCallArity1(subroutine, loc, value);
789 if (failed(result))
790 return {};
791 if (*result)
792 return *result;
793 }
794
795 mlir::emitError(loc) << "unsupported system call `" << subroutine.name
796 << "`";
797 return {};
798 }
799
800 /// Handle string literals.
801 Value visit(const slang::ast::StringLiteral &expr) {
802 auto type = context.convertType(*expr.type);
803 return builder.create<moore::StringConstantOp>(loc, type, expr.getValue());
804 }
805
806 /// Handle real literals.
807 Value visit(const slang::ast::RealLiteral &expr) {
808 return builder.create<moore::RealLiteralOp>(
809 loc, builder.getF64FloatAttr(expr.getValue()));
810 }
811
812 /// Handle assignment patterns.
813 Value visitAssignmentPattern(
814 const slang::ast::AssignmentPatternExpressionBase &expr,
815 unsigned replCount = 1) {
816 auto type = context.convertType(*expr.type);
817
818 // Convert the individual elements first.
819 auto elementCount = expr.elements().size();
820 SmallVector<Value> elements;
821 elements.reserve(replCount * elementCount);
822 for (auto elementExpr : expr.elements()) {
823 auto value = context.convertRvalueExpression(*elementExpr);
824 if (!value)
825 return {};
826 elements.push_back(value);
827 }
828 for (unsigned replIdx = 1; replIdx < replCount; ++replIdx)
829 for (unsigned elementIdx = 0; elementIdx < elementCount; ++elementIdx)
830 elements.push_back(elements[elementIdx]);
831
832 // Handle integers.
833 if (auto intType = dyn_cast<moore::IntType>(type)) {
834 assert(intType.getWidth() == elements.size());
835 std::reverse(elements.begin(), elements.end());
836 return builder.create<moore::ConcatOp>(loc, intType, elements);
837 }
838
839 // Handle packed structs.
840 if (auto structType = dyn_cast<moore::StructType>(type)) {
841 assert(structType.getMembers().size() == elements.size());
842 return builder.create<moore::StructCreateOp>(loc, structType, elements);
843 }
844
845 // Handle unpacked structs.
846 if (auto structType = dyn_cast<moore::UnpackedStructType>(type)) {
847 assert(structType.getMembers().size() == elements.size());
848 return builder.create<moore::StructCreateOp>(loc, structType, elements);
849 }
850
851 // Handle packed arrays.
852 if (auto arrayType = dyn_cast<moore::ArrayType>(type)) {
853 assert(arrayType.getSize() == elements.size());
854 return builder.create<moore::ArrayCreateOp>(loc, arrayType, elements);
855 }
856
857 // Handle unpacked arrays.
858 if (auto arrayType = dyn_cast<moore::UnpackedArrayType>(type)) {
859 assert(arrayType.getSize() == elements.size());
860 return builder.create<moore::ArrayCreateOp>(loc, arrayType, elements);
861 }
862
863 mlir::emitError(loc) << "unsupported assignment pattern with type " << type;
864 return {};
865 }
866
867 Value visit(const slang::ast::SimpleAssignmentPatternExpression &expr) {
868 return visitAssignmentPattern(expr);
869 }
870
871 Value visit(const slang::ast::StructuredAssignmentPatternExpression &expr) {
872 return visitAssignmentPattern(expr);
873 }
874
875 Value visit(const slang::ast::ReplicatedAssignmentPatternExpression &expr) {
876 auto count =
877 context.evaluateConstant(expr.count()).integer().as<unsigned>();
878 assert(count && "Slang guarantees constant non-zero replication count");
879 return visitAssignmentPattern(expr, *count);
880 }
881
882 Value visit(const slang::ast::StreamingConcatenationExpression &expr) {
883 SmallVector<Value> operands;
884 for (auto stream : expr.streams()) {
885 auto operandLoc = context.convertLocation(stream.operand->sourceRange);
886 if (!stream.constantWithWidth.has_value() && stream.withExpr) {
887 mlir::emitError(operandLoc)
888 << "Moore only support streaming "
889 "concatenation with fixed size 'with expression'";
890 return {};
891 }
892 Value value;
893 if (stream.constantWithWidth.has_value()) {
894 value = context.convertRvalueExpression(*stream.withExpr);
895 auto type = cast<moore::UnpackedType>(value.getType());
896 auto intType = moore::IntType::get(
897 context.getContext(), type.getBitSize().value(), type.getDomain());
898 // Do not care if it's signed, because we will not do expansion.
899 value = context.materializeConversion(intType, value, false, loc);
900 } else {
901 value = context.convertRvalueExpression(*stream.operand);
902 }
903
904 value = context.convertToSimpleBitVector(value);
905 if (!value)
906 return {};
907 operands.push_back(value);
908 }
909 Value value;
910
911 if (operands.size() == 1) {
912 // There must be at least one element, otherwise slang will report an
913 // error.
914 value = operands.front();
915 } else {
916 value = builder.create<moore::ConcatOp>(loc, operands).getResult();
917 }
918
919 if (expr.sliceSize == 0) {
920 return value;
921 }
922
923 auto type = cast<moore::IntType>(value.getType());
924 SmallVector<Value> slicedOperands;
925 auto iterMax = type.getWidth() / expr.sliceSize;
926 auto remainSize = type.getWidth() % expr.sliceSize;
927
928 for (size_t i = 0; i < iterMax; i++) {
929 auto extractResultType = moore::IntType::get(
930 context.getContext(), expr.sliceSize, type.getDomain());
931
932 auto extracted = builder.create<moore::ExtractOp>(
933 loc, extractResultType, value, i * expr.sliceSize);
934 slicedOperands.push_back(extracted);
935 }
936 // Handle other wire
937 if (remainSize) {
938 auto extractResultType = moore::IntType::get(
939 context.getContext(), remainSize, type.getDomain());
940
941 auto extracted = builder.create<moore::ExtractOp>(
942 loc, extractResultType, value, iterMax * expr.sliceSize);
943 slicedOperands.push_back(extracted);
944 }
945
946 return builder.create<moore::ConcatOp>(loc, slicedOperands);
947 }
948
949 /// Emit an error for all other expressions.
950 template <typename T>
951 Value visit(T &&node) {
952 mlir::emitError(loc, "unsupported expression: ")
953 << slang::ast::toString(node.kind);
954 return {};
955 }
956
957 Value visitInvalid(const slang::ast::Expression &expr) {
958 mlir::emitError(loc, "invalid expression");
959 return {};
960 }
961};
962} // namespace
963
964namespace {
965struct LvalueExprVisitor {
966 Context &context;
967 Location loc;
968 OpBuilder &builder;
969
970 LvalueExprVisitor(Context &context, Location loc)
971 : context(context), loc(loc), builder(context.builder) {}
972
973 // Handle named values, such as references to declared variables.
974 Value visit(const slang::ast::NamedValueExpression &expr) {
975 if (auto value = context.valueSymbols.lookup(&expr.symbol))
976 return value;
977 auto d = mlir::emitError(loc, "unknown name `") << expr.symbol.name << "`";
978 d.attachNote(context.convertLocation(expr.symbol.location))
979 << "no lvalue generated for " << slang::ast::toString(expr.symbol.kind);
980 return {};
981 }
982
983 // Handle hierarchical values, such as `Top.sub.var = x`.
984 Value visit(const slang::ast::HierarchicalValueExpression &expr) {
985 if (auto value = context.valueSymbols.lookup(&expr.symbol))
986 return value;
987
988 // Emit an error for those hierarchical values not recorded in the
989 // `valueSymbols`.
990 auto d = mlir::emitError(loc, "unknown hierarchical name `")
991 << expr.symbol.name << "`";
992 d.attachNote(context.convertLocation(expr.symbol.location))
993 << "no lvalue generated for " << slang::ast::toString(expr.symbol.kind);
994 return {};
995 }
996
997 // Handle concatenations.
998 Value visit(const slang::ast::ConcatenationExpression &expr) {
999 SmallVector<Value> operands;
1000 for (auto *operand : expr.operands()) {
1001 auto value = context.convertLvalueExpression(*operand);
1002 if (!value)
1003 return {};
1004 operands.push_back(value);
1005 }
1006 return builder.create<moore::ConcatRefOp>(loc, operands);
1007 }
1008
1009 // Handle single bit selections.
1010 Value visit(const slang::ast::ElementSelectExpression &expr) {
1011 auto type = context.convertType(*expr.type);
1012 auto value = context.convertLvalueExpression(expr.value());
1013 if (!type || !value)
1014 return {};
1015 if (auto *constValue = expr.selector().constant) {
1016 assert(!constValue->hasUnknown());
1017 assert(constValue->size() <= 32);
1018
1019 auto lowBit = constValue->integer().as<uint32_t>().value();
1020 return builder.create<moore::ExtractRefOp>(
1021 loc, moore::RefType::get(cast<moore::UnpackedType>(type)), value,
1022 lowBit);
1023 }
1024 auto lowBit = context.convertRvalueExpression(expr.selector());
1025 if (!lowBit)
1026 return {};
1027 return builder.create<moore::DynExtractRefOp>(
1028 loc, moore::RefType::get(cast<moore::UnpackedType>(type)), value,
1029 lowBit);
1030 }
1031
1032 // Handle range bits selections.
1033 Value visit(const slang::ast::RangeSelectExpression &expr) {
1034 auto type = context.convertType(*expr.type);
1035 auto value = context.convertLvalueExpression(expr.value());
1036 if (!type || !value)
1037 return {};
1038
1039 Value dynLowBit;
1040 uint32_t constLowBit;
1041 auto *leftConst = expr.left().constant;
1042 auto *rightConst = expr.right().constant;
1043 if (leftConst) {
1044 assert(!leftConst->hasUnknown());
1045 assert(leftConst->size() <= 32);
1046 }
1047 if (rightConst) {
1048 assert(!rightConst->hasUnknown());
1049 assert(rightConst->size() <= 32);
1050 }
1051
1052 if (expr.getSelectionKind() == slang::ast::RangeSelectionKind::Simple) {
1053 if (leftConst && rightConst) {
1054 // Estimate whether is big endian or little endian.
1055 auto lhs = leftConst->integer().as<uint32_t>().value();
1056 auto rhs = rightConst->integer().as<uint32_t>().value();
1057 constLowBit = lhs < rhs ? lhs : rhs;
1058 } else {
1059 mlir::emitError(loc, "unsupported a variable as the index in the")
1060 << slang::ast::toString(expr.getSelectionKind()) << "kind";
1061 return {};
1062 }
1063 } else if (expr.getSelectionKind() ==
1064 slang::ast::RangeSelectionKind::IndexedDown) {
1065 // IndexedDown: arr[7-:8]. It's equivalent to arr[7:0] or arr[0:7]
1066 // depending on little endian or bit endian. No matter which situation,
1067 // the low bit must be "0".
1068 if (leftConst) {
1069 auto subtrahend = leftConst->integer().as<uint32_t>().value();
1070 auto sliceWidth =
1071 expr.right().constant->integer().as<uint32_t>().value();
1072 constLowBit = subtrahend - sliceWidth - 1;
1073 } else {
1074 auto subtrahend = context.convertRvalueExpression(expr.left());
1075 auto subtrahendType = cast<moore::UnpackedType>(subtrahend.getType());
1076 auto intType = moore::IntType::get(context.getContext(),
1077 subtrahendType.getBitSize().value(),
1078 subtrahendType.getDomain());
1079 auto sliceWidth =
1080 expr.right().constant->integer().as<uint32_t>().value() - 1;
1081 auto minuend =
1082 builder.create<moore::ConstantOp>(loc, intType, sliceWidth);
1083 dynLowBit = builder.create<moore::SubOp>(loc, subtrahend, minuend);
1084 }
1085 } else {
1086 // IndexedUp: arr[0+:8]. "0" is the low bit, "8" is the bits slice width.
1087 if (leftConst)
1088 constLowBit = leftConst->integer().as<uint32_t>().value();
1089 else
1090 dynLowBit = context.convertRvalueExpression(expr.left());
1091 }
1092 if (leftConst && rightConst)
1093 return builder.create<moore::ExtractRefOp>(
1094 loc, moore::RefType::get(cast<moore::UnpackedType>(type)), value,
1095 constLowBit);
1096 return builder.create<moore::DynExtractRefOp>(
1097 loc, moore::RefType::get(cast<moore::UnpackedType>(type)), value,
1098 dynLowBit);
1099 }
1100
1101 Value visit(const slang::ast::StreamingConcatenationExpression &expr) {
1102 SmallVector<Value> operands;
1103 for (auto stream : expr.streams()) {
1104 auto operandLoc = context.convertLocation(stream.operand->sourceRange);
1105 if (!stream.constantWithWidth.has_value() && stream.withExpr) {
1106 mlir::emitError(operandLoc)
1107 << "Moore only support streaming "
1108 "concatenation with fixed size 'with expression'";
1109 return {};
1110 }
1111 Value value;
1112 if (stream.constantWithWidth.has_value()) {
1113 value = context.convertLvalueExpression(*stream.withExpr);
1114 auto type = cast<moore::UnpackedType>(
1115 cast<moore::RefType>(value.getType()).getNestedType());
1116 auto intType = moore::RefType::get(moore::IntType::get(
1117 context.getContext(), type.getBitSize().value(), type.getDomain()));
1118 // Do not care if it's signed, because we will not do expansion.
1119 value = context.materializeConversion(intType, value, false, loc);
1120 } else {
1121 value = context.convertLvalueExpression(*stream.operand);
1122 }
1123
1124 if (!value)
1125 return {};
1126 operands.push_back(value);
1127 }
1128 Value value;
1129 if (operands.size() == 1) {
1130 // There must be at least one element, otherwise slang will report an
1131 // error.
1132 value = operands.front();
1133 } else {
1134 value = builder.create<moore::ConcatRefOp>(loc, operands).getResult();
1135 }
1136
1137 if (expr.sliceSize == 0) {
1138 return value;
1139 }
1140
1141 auto type = cast<moore::IntType>(
1142 cast<moore::RefType>(value.getType()).getNestedType());
1143 SmallVector<Value> slicedOperands;
1144 auto widthSum = type.getWidth();
1145 auto domain = type.getDomain();
1146 auto iterMax = widthSum / expr.sliceSize;
1147 auto remainSize = widthSum % expr.sliceSize;
1148
1149 for (size_t i = 0; i < iterMax; i++) {
1150 auto extractResultType = moore::RefType::get(
1151 moore::IntType::get(context.getContext(), expr.sliceSize, domain));
1152
1153 auto extracted = builder.create<moore::ExtractRefOp>(
1154 loc, extractResultType, value, i * expr.sliceSize);
1155 slicedOperands.push_back(extracted);
1156 }
1157 // Handle other wire
1158 if (remainSize) {
1159 auto extractResultType = moore::RefType::get(
1160 moore::IntType::get(context.getContext(), remainSize, domain));
1161
1162 auto extracted = builder.create<moore::ExtractRefOp>(
1163 loc, extractResultType, value, iterMax * expr.sliceSize);
1164 slicedOperands.push_back(extracted);
1165 }
1166
1167 return builder.create<moore::ConcatRefOp>(loc, slicedOperands);
1168 }
1169
1170 Value visit(const slang::ast::MemberAccessExpression &expr) {
1171 auto type = context.convertType(*expr.type);
1172 auto valueType = expr.value().type;
1173 auto value = context.convertLvalueExpression(expr.value());
1174 if (!type || !value)
1175 return {};
1176 if (valueType->isStruct()) {
1177 return builder.create<moore::StructExtractRefOp>(
1178 loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
1179 builder.getStringAttr(expr.member.name), value);
1180 }
1181 if (valueType->isPackedUnion() || valueType->isUnpackedUnion()) {
1182 return builder.create<moore::UnionExtractRefOp>(
1183 loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
1184 builder.getStringAttr(expr.member.name), value);
1185 }
1186 mlir::emitError(loc, "expression of type ")
1187 << value.getType() << " cannot be accessed";
1188 return {};
1189 }
1190
1191 /// Emit an error for all other expressions.
1192 template <typename T>
1193 Value visit(T &&node) {
1194 return context.convertRvalueExpression(node);
1195 }
1196
1197 Value visitInvalid(const slang::ast::Expression &expr) {
1198 mlir::emitError(loc, "invalid expression");
1199 return {};
1200 }
1201};
1202} // namespace
1203
1204Value Context::convertRvalueExpression(const slang::ast::Expression &expr,
1205 Type requiredType) {
1206 auto loc = convertLocation(expr.sourceRange);
1207 auto value = expr.visit(RvalueExprVisitor(*this, loc));
1208 if (value && requiredType)
1209 value =
1210 materializeConversion(requiredType, value, expr.type->isSigned(), loc);
1211 return value;
1212}
1213
1214Value Context::convertLvalueExpression(const slang::ast::Expression &expr) {
1215 auto loc = convertLocation(expr.sourceRange);
1216 return expr.visit(LvalueExprVisitor(*this, loc));
1217}
1218// NOLINTEND(misc-no-recursion)
1219
1220/// Helper function to convert a value to its "truthy" boolean value.
1221Value Context::convertToBool(Value value) {
1222 if (!value)
1223 return {};
1224 if (auto type = dyn_cast_or_null<moore::IntType>(value.getType()))
1225 if (type.getBitSize() == 1)
1226 return value;
1227 if (auto type = dyn_cast_or_null<moore::UnpackedType>(value.getType()))
1228 return builder.create<moore::BoolCastOp>(value.getLoc(), value);
1229 mlir::emitError(value.getLoc(), "expression of type ")
1230 << value.getType() << " cannot be cast to a boolean";
1231 return {};
1232}
1233
1234/// Materialize a Slang integer literal as a constant op.
1235Value Context::materializeSVInt(const slang::SVInt &svint,
1236 const slang::ast::Type &astType, Location loc) {
1237 auto type = convertType(astType);
1238 if (!type)
1239 return {};
1240
1241 bool typeIsFourValued = false;
1242 if (auto unpackedType = dyn_cast<moore::UnpackedType>(type))
1243 typeIsFourValued = unpackedType.getDomain() == moore::Domain::FourValued;
1244
1245 auto fvint = convertSVIntToFVInt(svint);
1246 auto intType = moore::IntType::get(getContext(), fvint.getBitWidth(),
1247 fvint.hasUnknown() || typeIsFourValued
1250 Value result = builder.create<moore::ConstantOp>(loc, intType, fvint);
1251 if (result.getType() != type)
1252 result = builder.create<moore::ConversionOp>(loc, type, result);
1253 return result;
1254}
1255
1256Value Context::materializeConstant(const slang::ConstantValue &constant,
1257 const slang::ast::Type &type, Location loc) {
1258 if (constant.isInteger())
1259 return materializeSVInt(constant.integer(), type, loc);
1260 return {};
1261}
1262
1263slang::ConstantValue
1264Context::evaluateConstant(const slang::ast::Expression &expr) {
1265 using slang::ast::EvalFlags;
1266 slang::ast::EvalContext evalContext(
1267 compilation, EvalFlags::CacheResults | EvalFlags::SpecparamsAllowed);
1268 return expr.eval(evalContext);
1269}
1270
1271/// Helper function to convert a value to its "truthy" boolean value and
1272/// convert it to the given domain.
1273Value Context::convertToBool(Value value, Domain domain) {
1274 value = convertToBool(value);
1275 if (!value)
1276 return {};
1277 auto type = moore::IntType::get(getContext(), 1, domain);
1278 if (value.getType() == type)
1279 return value;
1280 return builder.create<moore::ConversionOp>(value.getLoc(), type, value);
1281}
1282
1284 if (!value)
1285 return {};
1286 if (isa<moore::IntType>(value.getType()))
1287 return value;
1288
1289 // Some operations in Slang's AST, for example bitwise or `|`, don't cast
1290 // packed struct/array operands to simple bit vectors but directly operate
1291 // on the struct/array. Since the corresponding IR ops operate only on
1292 // simple bit vectors, insert a conversion in this case.
1293 if (auto packed = dyn_cast<moore::PackedType>(value.getType())) {
1294 if (auto bits = packed.getBitSize()) {
1295 auto sbvType =
1296 moore::IntType::get(value.getContext(), *bits, packed.getDomain());
1297 return builder.create<moore::ConversionOp>(value.getLoc(), sbvType,
1298 value);
1299 }
1300 }
1301
1302 mlir::emitError(value.getLoc()) << "expression of type " << value.getType()
1303 << " cannot be cast to a simple bit vector";
1304 return {};
1305}
1306
1307Value Context::materializeConversion(Type type, Value value, bool isSigned,
1308 Location loc) {
1309 if (type == value.getType())
1310 return value;
1311 auto dstPacked = dyn_cast<moore::PackedType>(type);
1312 auto srcPacked = dyn_cast<moore::PackedType>(value.getType());
1313
1314 // Resize the value if needed.
1315 if (dstPacked && srcPacked && dstPacked.getBitSize() &&
1316 srcPacked.getBitSize() &&
1317 *dstPacked.getBitSize() != *srcPacked.getBitSize()) {
1318 auto dstWidth = *dstPacked.getBitSize();
1319 auto srcWidth = *srcPacked.getBitSize();
1320
1321 // Convert the value to a simple bit vector which we can extend or truncate.
1322 auto srcWidthType = moore::IntType::get(value.getContext(), srcWidth,
1323 srcPacked.getDomain());
1324 if (value.getType() != srcWidthType)
1325 value = builder.create<moore::ConversionOp>(value.getLoc(), srcWidthType,
1326 value);
1327
1328 // Create truncation or sign/zero extension ops depending on the source and
1329 // destination width.
1330 auto dstWidthType = moore::IntType::get(value.getContext(), dstWidth,
1331 srcPacked.getDomain());
1332 if (dstWidth < srcWidth) {
1333 value = builder.create<moore::TruncOp>(loc, dstWidthType, value);
1334 } else if (dstWidth > srcWidth) {
1335 if (isSigned)
1336 value = builder.create<moore::SExtOp>(loc, dstWidthType, value);
1337 else
1338 value = builder.create<moore::ZExtOp>(loc, dstWidthType, value);
1339 }
1340 }
1341
1342 if (value.getType() != type)
1343 value = builder.create<moore::ConversionOp>(loc, type, value);
1344 return value;
1345}
1346
1347FailureOr<Value>
1348Context::convertSystemCallArity1(const slang::ast::SystemSubroutine &subroutine,
1349 Location loc, Value value) {
1350 auto systemCallRes =
1351 llvm::StringSwitch<std::function<FailureOr<Value>()>>(subroutine.name)
1352 // Signed and unsigned system functions.
1353 .Case("$signed", [&]() { return value; })
1354 .Case("$unsigned", [&]() { return value; })
1355
1356 // Math functions in SystemVerilog.
1357 .Case("$clog2",
1358 [&]() -> FailureOr<Value> {
1359 value = convertToSimpleBitVector(value);
1360 if (!value)
1361 return failure();
1362 return (Value)builder.create<moore::Clog2BIOp>(loc, value);
1363 })
1364 .Case("$ln",
1365 [&]() -> Value {
1366 return builder.create<moore::LnBIOp>(loc, value);
1367 })
1368 .Case("$log10",
1369 [&]() -> Value {
1370 return builder.create<moore::Log10BIOp>(loc, value);
1371 })
1372 .Case("$sin",
1373 [&]() -> Value {
1374 return builder.create<moore::SinBIOp>(loc, value);
1375 })
1376 .Case("$cos",
1377 [&]() -> Value {
1378 return builder.create<moore::CosBIOp>(loc, value);
1379 })
1380 .Case("$tan",
1381 [&]() -> Value {
1382 return builder.create<moore::TanBIOp>(loc, value);
1383 })
1384 .Case("$exp",
1385 [&]() -> Value {
1386 return builder.create<moore::ExpBIOp>(loc, value);
1387 })
1388 .Case("$sqrt",
1389 [&]() -> Value {
1390 return builder.create<moore::SqrtBIOp>(loc, value);
1391 })
1392 .Case("$floor",
1393 [&]() -> Value {
1394 return builder.create<moore::FloorBIOp>(loc, value);
1395 })
1396 .Case("$ceil",
1397 [&]() -> Value {
1398 return builder.create<moore::CeilBIOp>(loc, value);
1399 })
1400 .Case("$asin",
1401 [&]() -> Value {
1402 return builder.create<moore::AsinBIOp>(loc, value);
1403 })
1404 .Case("$acos",
1405 [&]() -> Value {
1406 return builder.create<moore::AcosBIOp>(loc, value);
1407 })
1408 .Case("$atan",
1409 [&]() -> Value {
1410 return builder.create<moore::AtanBIOp>(loc, value);
1411 })
1412 .Case("$sinh",
1413 [&]() -> Value {
1414 return builder.create<moore::SinhBIOp>(loc, value);
1415 })
1416 .Case("$cosh",
1417 [&]() -> Value {
1418 return builder.create<moore::CoshBIOp>(loc, value);
1419 })
1420 .Case("$tanh",
1421 [&]() -> Value {
1422 return builder.create<moore::TanhBIOp>(loc, value);
1423 })
1424 .Case("$asinh",
1425 [&]() -> Value {
1426 return builder.create<moore::AsinhBIOp>(loc, value);
1427 })
1428 .Case("$acosh",
1429 [&]() -> Value {
1430 return builder.create<moore::AcoshBIOp>(loc, value);
1431 })
1432 .Case("$atanh",
1433 [&]() -> Value {
1434 return builder.create<moore::AtanhBIOp>(loc, value);
1435 })
1436 .Default([&]() -> Value { return {}; });
1437 return systemCallRes();
1438}
assert(baseType &&"element must be base type")
static FVInt convertSVIntToFVInt(const slang::SVInt &svint)
Convert a Slang SVInt to a CIRCT FVInt.
Four-valued arbitrary precision integers.
Definition FVInt.h:37
void info(Twine message)
Definition LSPUtils.cpp:20
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.
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.
FailureOr< Value > convertSystemCallArity1(const slang::ast::SystemSubroutine &subroutine, Location loc, Value value)
Convert system function calls only have arity-1.
FunctionLowering * declareFunction(const slang::ast::SubroutineSymbol &subroutine)
Convert a function and its arguments to a function declaration in the IR.
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.