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