CIRCT 22.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/EvalContext.h"
11#include "slang/ast/SystemSubroutine.h"
12#include "slang/syntax/AllSyntax.h"
13
14using namespace circt;
15using namespace ImportVerilog;
16using moore::Domain;
17
18/// Convert a Slang `SVInt` to a CIRCT `FVInt`.
19static FVInt convertSVIntToFVInt(const slang::SVInt &svint) {
20 if (svint.hasUnknown()) {
21 unsigned numWords = svint.getNumWords() / 2;
22 auto value = ArrayRef<uint64_t>(svint.getRawPtr(), numWords);
23 auto unknown = ArrayRef<uint64_t>(svint.getRawPtr() + numWords, numWords);
24 return FVInt(APInt(svint.getBitWidth(), value),
25 APInt(svint.getBitWidth(), unknown));
26 }
27 auto value = ArrayRef<uint64_t>(svint.getRawPtr(), svint.getNumWords());
28 return FVInt(APInt(svint.getBitWidth(), value));
29}
30
31/// Map an index into an array, with bounds `range`, to a bit offset of the
32/// underlying bit storage. This is a dynamic version of
33/// `slang::ConstantRange::translateIndex`.
34static Value getSelectIndex(Context &context, Location loc, Value index,
35 const slang::ConstantRange &range) {
36 auto &builder = context.builder;
37 auto indexType = cast<moore::UnpackedType>(index.getType());
38 auto bw = std::max(llvm::Log2_32_Ceil(std::max(std::abs(range.lower()),
39 std::abs(range.upper()))),
40 indexType.getBitSize().value());
41
42 auto offset = range.isLittleEndian() ? range.lower() : range.upper();
43 bool isSigned = (offset < 0);
44 auto intType =
45 moore::IntType::get(index.getContext(), bw, indexType.getDomain());
46 index = context.materializeConversion(intType, index, isSigned, loc);
47
48 if (offset == 0) {
49 if (range.isLittleEndian())
50 return index;
51 else
52 return moore::NegOp::create(builder, loc, index);
53 }
54
55 auto offsetConst =
56 moore::ConstantOp::create(builder, loc, intType, offset, isSigned);
57 if (range.isLittleEndian())
58 return moore::SubOp::create(builder, loc, index, offsetConst);
59 else
60 return moore::SubOp::create(builder, loc, offsetConst, index);
61}
62
63/// Get the currently active timescale as an integer number of femtoseconds.
64static uint64_t getTimeScaleInFemtoseconds(Context &context) {
65 static_assert(int(slang::TimeUnit::Seconds) == 0);
66 static_assert(int(slang::TimeUnit::Milliseconds) == 1);
67 static_assert(int(slang::TimeUnit::Microseconds) == 2);
68 static_assert(int(slang::TimeUnit::Nanoseconds) == 3);
69 static_assert(int(slang::TimeUnit::Picoseconds) == 4);
70 static_assert(int(slang::TimeUnit::Femtoseconds) == 5);
71
72 static_assert(int(slang::TimeScaleMagnitude::One) == 1);
73 static_assert(int(slang::TimeScaleMagnitude::Ten) == 10);
74 static_assert(int(slang::TimeScaleMagnitude::Hundred) == 100);
75
76 auto exp = static_cast<unsigned>(context.timeScale.base.unit);
77 assert(exp <= 5);
78 exp = 5 - exp;
79 auto scale = static_cast<uint64_t>(context.timeScale.base.magnitude);
80 while (exp-- > 0)
81 scale *= 1000;
82 return scale;
83}
84
85namespace {
86/// A visitor handling expressions that can be lowered as lvalue and rvalue.
87struct ExprVisitor {
88 Context &context;
89 Location loc;
90 OpBuilder &builder;
91 bool isLvalue;
92
93 ExprVisitor(Context &context, Location loc, bool isLvalue)
94 : context(context), loc(loc), builder(context.builder),
95 isLvalue(isLvalue) {}
96
97 /// Convert an expression either as an lvalue or rvalue, depending on whether
98 /// this is an lvalue or rvalue visitor. This is useful for projections such
99 /// as `a[i]`, where you want `a` as an lvalue if you want `a[i]` as an
100 /// lvalue, or `a` as an rvalue if you want `a[i]` as an rvalue.
101 Value convertLvalueOrRvalueExpression(const slang::ast::Expression &expr) {
102 if (isLvalue)
103 return context.convertLvalueExpression(expr);
104 return context.convertRvalueExpression(expr);
105 }
106
107 /// Handle single bit selections.
108 Value visit(const slang::ast::ElementSelectExpression &expr) {
109 auto type = context.convertType(*expr.type);
110 auto value = convertLvalueOrRvalueExpression(expr.value());
111 if (!type || !value)
112 return {};
113
114 // We only support indexing into a few select types for now.
115 auto derefType = value.getType();
116 if (isLvalue)
117 derefType = cast<moore::RefType>(derefType).getNestedType();
118 if (!isa<moore::IntType, moore::ArrayType, moore::UnpackedArrayType>(
119 derefType)) {
120 mlir::emitError(loc) << "unsupported expression: element select into "
121 << expr.value().type->toString() << "\n";
122 return {};
123 }
124
125 auto resultType =
126 isLvalue ? moore::RefType::get(cast<moore::UnpackedType>(type)) : type;
127 auto range = expr.value().type->getFixedRange();
128 if (auto *constValue = expr.selector().getConstant();
129 constValue && constValue->isInteger()) {
130 assert(!constValue->hasUnknown());
131 assert(constValue->size() <= 32);
132
133 auto lowBit = constValue->integer().as<uint32_t>().value();
134 if (isLvalue)
135 return moore::ExtractRefOp::create(builder, loc, resultType, value,
136 range.translateIndex(lowBit));
137 else
138 return moore::ExtractOp::create(builder, loc, resultType, value,
139 range.translateIndex(lowBit));
140 }
141
142 auto lowBit = context.convertRvalueExpression(expr.selector());
143 if (!lowBit)
144 return {};
145 lowBit = getSelectIndex(context, loc, lowBit, range);
146 if (isLvalue)
147 return moore::DynExtractRefOp::create(builder, loc, resultType, value,
148 lowBit);
149 else
150 return moore::DynExtractOp::create(builder, loc, resultType, value,
151 lowBit);
152 }
153
154 /// Handle range bit selections.
155 Value visit(const slang::ast::RangeSelectExpression &expr) {
156 auto type = context.convertType(*expr.type);
157 auto value = convertLvalueOrRvalueExpression(expr.value());
158 if (!type || !value)
159 return {};
160
161 std::optional<int32_t> constLeft;
162 std::optional<int32_t> constRight;
163 if (auto *constant = expr.left().getConstant())
164 constLeft = constant->integer().as<int32_t>();
165 if (auto *constant = expr.right().getConstant())
166 constRight = constant->integer().as<int32_t>();
167
168 // We currently require the right-hand-side of the range to be constant.
169 // This catches things like `[42:$]` which we don't support at the moment.
170 if (!constRight) {
171 mlir::emitError(loc)
172 << "unsupported expression: range select with non-constant bounds";
173 return {};
174 }
175
176 // We need to determine the right bound of the range. This is the address of
177 // the least significant bit of the underlying bit storage, which is the
178 // offset we want to pass to the extract op.
179 //
180 // The arrays [6:2] and [2:6] both have 5 bits worth of underlying storage.
181 // The left and right bound of the range only determine the addressing
182 // scheme of the storage bits:
183 //
184 // Storage bits: 4 3 2 1 0 <-- extract op works on storage bits
185 // [6:2] indices: 6 5 4 3 2 ("little endian" in Slang terms)
186 // [2:6] indices: 2 3 4 5 6 ("big endian" in Slang terms)
187 //
188 // Before we can extract, we need to map the range select left and right
189 // bounds from these indices to actual bit positions in the storage.
190
191 Value offsetDyn;
192 int32_t offsetConst = 0;
193 auto range = expr.value().type->getFixedRange();
194
195 using slang::ast::RangeSelectionKind;
196 if (expr.getSelectionKind() == RangeSelectionKind::Simple) {
197 // For a constant range [a:b], we want the offset of the lowest storage
198 // bit from which we are starting the extract. For a range [5:3] this is
199 // bit index 3; for a range [3:5] this is bit index 5. Both of these are
200 // later translated map to bit offset 1 (see bit indices above).
201 assert(constRight && "constness checked in slang");
202 offsetConst = *constRight;
203 } else {
204 // For an indexed range [a+:b] or [a-:b], determining the lowest storage
205 // bit is a bit more complicated. We start out with the base index `a`.
206 // This is the lower *index* of the range, but not the lower *storage bit
207 // position*.
208 //
209 // The range [a+:b] expands to [a+b-1:a] for a [6:2] range, or [a:a+b-1]
210 // for a [2:6] range. The range [a-:b] expands to [a:a-b+1] for a [6:2]
211 // range, or [a-b+1:a] for a [2:6] range.
212 if (constLeft) {
213 offsetConst = *constLeft;
214 } else {
215 offsetDyn = context.convertRvalueExpression(expr.left());
216 if (!offsetDyn)
217 return {};
218 }
219
220 // For a [a-:b] select on [2:6] and a [a+:b] select on [6:2], the range
221 // expands to [a-b+1:a] and [a+b-1:a]. In this case, the right bound which
222 // corresponds to the lower *storage bit offset*, is just `a` and there's
223 // no further tweaking to do.
224 int32_t offsetAdd = 0;
225
226 // For a [a-:b] select on [6:2], the range expands to [a:a-b+1]. We
227 // therefore have to take the `a` from above and adjust it by `-b+1` to
228 // arrive at the right bound.
229 if (expr.getSelectionKind() == RangeSelectionKind::IndexedDown &&
230 range.isLittleEndian()) {
231 assert(constRight && "constness checked in slang");
232 offsetAdd = 1 - *constRight;
233 }
234
235 // For a [a+:b] select on [2:6], the range expands to [a:a+b-1]. We
236 // therefore have to take the `a` from above and adjust it by `+b-1` to
237 // arrive at the right bound.
238 if (expr.getSelectionKind() == RangeSelectionKind::IndexedUp &&
239 !range.isLittleEndian()) {
240 assert(constRight && "constness checked in slang");
241 offsetAdd = *constRight - 1;
242 }
243
244 // Adjust the offset such that it matches the right bound of the range.
245 if (offsetAdd != 0) {
246 if (offsetDyn)
247 offsetDyn = moore::AddOp::create(
248 builder, loc, offsetDyn,
249 moore::ConstantOp::create(
250 builder, loc, cast<moore::IntType>(offsetDyn.getType()),
251 offsetAdd,
252 /*isSigned=*/offsetAdd < 0));
253 else
254 offsetConst += offsetAdd;
255 }
256 }
257
258 // Create a dynamic or constant extract. Use `getSelectIndex` and
259 // `ConstantRange::translateIndex` to map from the bit indices provided by
260 // the user to the actual storage bit position. Since `offset*` corresponds
261 // to the right bound of the range, which provides the index of the least
262 // significant selected storage bit, we get the bit offset at which we want
263 // to start extracting.
264 auto resultType =
265 isLvalue ? moore::RefType::get(cast<moore::UnpackedType>(type)) : type;
266
267 if (offsetDyn) {
268 offsetDyn = getSelectIndex(context, loc, offsetDyn, range);
269 if (isLvalue) {
270 return moore::DynExtractRefOp::create(builder, loc, resultType, value,
271 offsetDyn);
272 } else {
273 return moore::DynExtractOp::create(builder, loc, resultType, value,
274 offsetDyn);
275 }
276 } else {
277 offsetConst = range.translateIndex(offsetConst);
278 if (isLvalue) {
279 return moore::ExtractRefOp::create(builder, loc, resultType, value,
280 offsetConst);
281 } else {
282 return moore::ExtractOp::create(builder, loc, resultType, value,
283 offsetConst);
284 }
285 }
286 }
287
288 /// Handle concatenations.
289 Value visit(const slang::ast::ConcatenationExpression &expr) {
290 SmallVector<Value> operands;
291 for (auto *operand : expr.operands()) {
292 // Handle empty replications like `{0{...}}` which may occur within
293 // concatenations. Slang assigns them a `void` type which we can check for
294 // here.
295 if (operand->type->isVoid())
296 continue;
297 auto value = convertLvalueOrRvalueExpression(*operand);
298 if (!value)
299 return {};
300 if (!isLvalue)
301 value = context.convertToSimpleBitVector(value);
302 if (!value)
303 return {};
304 operands.push_back(value);
305 }
306 if (isLvalue)
307 return moore::ConcatRefOp::create(builder, loc, operands);
308 else
309 return moore::ConcatOp::create(builder, loc, operands);
310 }
311
312 /// Handle member accesses.
313 Value visit(const slang::ast::MemberAccessExpression &expr) {
314 auto type = context.convertType(*expr.type);
315 auto valueType = expr.value().type;
316 auto value = convertLvalueOrRvalueExpression(expr.value());
317 if (!type || !value)
318 return {};
319
320 auto resultType =
321 isLvalue ? moore::RefType::get(cast<moore::UnpackedType>(type)) : type;
322 auto memberName = builder.getStringAttr(expr.member.name);
323
324 // Handle structs.
325 if (valueType->isStruct()) {
326 if (isLvalue)
327 return moore::StructExtractRefOp::create(builder, loc, resultType,
328 memberName, value);
329 else
330 return moore::StructExtractOp::create(builder, loc, resultType,
331 memberName, value);
332 }
333
334 // Handle unions.
335 if (valueType->isPackedUnion() || valueType->isUnpackedUnion()) {
336 if (isLvalue)
337 return moore::UnionExtractRefOp::create(builder, loc, resultType,
338 memberName, value);
339 else
340 return moore::UnionExtractOp::create(builder, loc, type, memberName,
341 value);
342 }
343
344 mlir::emitError(loc, "expression of type ")
345 << value.getType() << " has no member fields";
346 return {};
347 }
348};
349} // namespace
350
351//===----------------------------------------------------------------------===//
352// Rvalue Conversion
353//===----------------------------------------------------------------------===//
354
355// NOLINTBEGIN(misc-no-recursion)
356namespace {
357struct RvalueExprVisitor : public ExprVisitor {
358 RvalueExprVisitor(Context &context, Location loc)
359 : ExprVisitor(context, loc, /*isLvalue=*/false) {}
360 using ExprVisitor::visit;
361
362 // Handle references to the left-hand side of a parent assignment.
363 Value visit(const slang::ast::LValueReferenceExpression &expr) {
364 assert(!context.lvalueStack.empty() && "parent assignments push lvalue");
365 auto lvalue = context.lvalueStack.back();
366 return moore::ReadOp::create(builder, loc, lvalue);
367 }
368
369 // Handle named values, such as references to declared variables.
370 Value visit(const slang::ast::NamedValueExpression &expr) {
371 if (auto value = context.valueSymbols.lookup(&expr.symbol)) {
372 if (isa<moore::RefType>(value.getType())) {
373 auto readOp = moore::ReadOp::create(builder, loc, value);
374 if (context.rvalueReadCallback)
375 context.rvalueReadCallback(readOp);
376 value = readOp.getResult();
377 }
378 return value;
379 }
380
381 // Try to materialize constant values directly.
382 auto constant = context.evaluateConstant(expr);
383 if (auto value = context.materializeConstant(constant, *expr.type, loc))
384 return value;
385
386 // Otherwise some other part of ImportVerilog should have added an MLIR
387 // value for this expression's symbol to the `context.valueSymbols` table.
388 auto d = mlir::emitError(loc, "unknown name `") << expr.symbol.name << "`";
389 d.attachNote(context.convertLocation(expr.symbol.location))
390 << "no rvalue generated for " << slang::ast::toString(expr.symbol.kind);
391 return {};
392 }
393
394 // Handle hierarchical values, such as `x = Top.sub.var`.
395 Value visit(const slang::ast::HierarchicalValueExpression &expr) {
396 auto hierLoc = context.convertLocation(expr.symbol.location);
397 if (auto value = context.valueSymbols.lookup(&expr.symbol)) {
398 if (isa<moore::RefType>(value.getType())) {
399 auto readOp = moore::ReadOp::create(builder, hierLoc, value);
400 if (context.rvalueReadCallback)
401 context.rvalueReadCallback(readOp);
402 value = readOp.getResult();
403 }
404 return value;
405 }
406
407 // Emit an error for those hierarchical values not recorded in the
408 // `valueSymbols`.
409 auto d = mlir::emitError(loc, "unknown hierarchical name `")
410 << expr.symbol.name << "`";
411 d.attachNote(hierLoc) << "no rvalue generated for "
412 << slang::ast::toString(expr.symbol.kind);
413 return {};
414 }
415
416 // Handle type conversions (explicit and implicit).
417 Value visit(const slang::ast::ConversionExpression &expr) {
418 auto type = context.convertType(*expr.type);
419 if (!type)
420 return {};
421 return context.convertRvalueExpression(expr.operand(), type);
422 }
423
424 // Handle blocking and non-blocking assignments.
425 Value visit(const slang::ast::AssignmentExpression &expr) {
426 auto lhs = context.convertLvalueExpression(expr.left());
427 if (!lhs)
428 return {};
429
430 context.lvalueStack.push_back(lhs);
431 auto rhs = context.convertRvalueExpression(
432 expr.right(), cast<moore::RefType>(lhs.getType()).getNestedType());
433 context.lvalueStack.pop_back();
434 if (!rhs)
435 return {};
436
437 if (expr.timingControl) {
438 auto loc = context.convertLocation(expr.timingControl->sourceRange);
439 mlir::emitError(loc, "delayed assignments not supported");
440 return {};
441 }
442
443 if (expr.isNonBlocking())
444 moore::NonBlockingAssignOp::create(builder, loc, lhs, rhs);
445 else
446 moore::BlockingAssignOp::create(builder, loc, lhs, rhs);
447 return rhs;
448 }
449
450 // Helper function to convert an argument to a simple bit vector type, pass it
451 // to a reduction op, and optionally invert the result.
452 template <class ConcreteOp>
453 Value createReduction(Value arg, bool invert) {
454 arg = context.convertToSimpleBitVector(arg);
455 if (!arg)
456 return {};
457 Value result = ConcreteOp::create(builder, loc, arg);
458 if (invert)
459 result = moore::NotOp::create(builder, loc, result);
460 return result;
461 }
462
463 // Helper function to create pre and post increments and decrements.
464 Value createIncrement(Value arg, bool isInc, bool isPost) {
465 auto preValue = moore::ReadOp::create(builder, loc, arg);
466 Value postValue;
467 // Catch the special case where a signed 1 bit value (i1) is incremented,
468 // as +1 can not be expressed as a signed 1 bit value. For any 1-bit number
469 // negating is equivalent to incrementing.
470 if (moore::isIntType(preValue.getType(), 1)) {
471 postValue = moore::NotOp::create(builder, loc, preValue).getResult();
472 } else {
473
474 auto one = moore::ConstantOp::create(
475 builder, loc, cast<moore::IntType>(preValue.getType()), 1);
476 postValue =
477 isInc ? moore::AddOp::create(builder, loc, preValue, one).getResult()
478 : moore::SubOp::create(builder, loc, preValue, one).getResult();
479 moore::BlockingAssignOp::create(builder, loc, arg, postValue);
480 }
481 if (isPost)
482 return preValue;
483 return postValue;
484 }
485
486 // Handle unary operators.
487 Value visit(const slang::ast::UnaryExpression &expr) {
488 using slang::ast::UnaryOperator;
489 Value arg;
490 if (expr.op == UnaryOperator::Preincrement ||
491 expr.op == UnaryOperator::Predecrement ||
492 expr.op == UnaryOperator::Postincrement ||
493 expr.op == UnaryOperator::Postdecrement)
494 arg = context.convertLvalueExpression(expr.operand());
495 else
496 arg = context.convertRvalueExpression(expr.operand());
497 if (!arg)
498 return {};
499
500 switch (expr.op) {
501 // `+a` is simply `a`, but converted to a simple bit vector type since
502 // this is technically an arithmetic operation.
503 case UnaryOperator::Plus:
504 return context.convertToSimpleBitVector(arg);
505
506 case UnaryOperator::Minus:
507 arg = context.convertToSimpleBitVector(arg);
508 if (!arg)
509 return {};
510 return moore::NegOp::create(builder, loc, arg);
511
512 case UnaryOperator::BitwiseNot:
513 arg = context.convertToSimpleBitVector(arg);
514 if (!arg)
515 return {};
516 return moore::NotOp::create(builder, loc, arg);
517
518 case UnaryOperator::BitwiseAnd:
519 return createReduction<moore::ReduceAndOp>(arg, false);
520 case UnaryOperator::BitwiseOr:
521 return createReduction<moore::ReduceOrOp>(arg, false);
522 case UnaryOperator::BitwiseXor:
523 return createReduction<moore::ReduceXorOp>(arg, false);
524 case UnaryOperator::BitwiseNand:
525 return createReduction<moore::ReduceAndOp>(arg, true);
526 case UnaryOperator::BitwiseNor:
527 return createReduction<moore::ReduceOrOp>(arg, true);
528 case UnaryOperator::BitwiseXnor:
529 return createReduction<moore::ReduceXorOp>(arg, true);
530
531 case UnaryOperator::LogicalNot:
532 arg = context.convertToBool(arg);
533 if (!arg)
534 return {};
535 return moore::NotOp::create(builder, loc, arg);
536
537 case UnaryOperator::Preincrement:
538 return createIncrement(arg, true, false);
539 case UnaryOperator::Predecrement:
540 return createIncrement(arg, false, false);
541 case UnaryOperator::Postincrement:
542 return createIncrement(arg, true, true);
543 case UnaryOperator::Postdecrement:
544 return createIncrement(arg, false, true);
545 }
546
547 mlir::emitError(loc, "unsupported unary operator");
548 return {};
549 }
550
551 // Helper function to convert two arguments to a simple bit vector type and
552 // pass them into a binary op.
553 template <class ConcreteOp>
554 Value createBinary(Value lhs, Value rhs) {
555 lhs = context.convertToSimpleBitVector(lhs);
556 if (!lhs)
557 return {};
558 rhs = context.convertToSimpleBitVector(rhs);
559 if (!rhs)
560 return {};
561 return ConcreteOp::create(builder, loc, lhs, rhs);
562 }
563
564 // Handle binary operators.
565 Value visit(const slang::ast::BinaryExpression &expr) {
566 auto lhs = context.convertRvalueExpression(expr.left());
567 if (!lhs)
568 return {};
569 auto rhs = context.convertRvalueExpression(expr.right());
570 if (!rhs)
571 return {};
572
573 // Determine the domain of the result.
574 Domain domain = Domain::TwoValued;
575 if (expr.type->isFourState() || expr.left().type->isFourState() ||
576 expr.right().type->isFourState())
577 domain = Domain::FourValued;
578
579 using slang::ast::BinaryOperator;
580 switch (expr.op) {
581 case BinaryOperator::Add:
582 return createBinary<moore::AddOp>(lhs, rhs);
583 case BinaryOperator::Subtract:
584 return createBinary<moore::SubOp>(lhs, rhs);
585 case BinaryOperator::Multiply:
586 return createBinary<moore::MulOp>(lhs, rhs);
587 case BinaryOperator::Divide:
588 if (expr.type->isSigned())
589 return createBinary<moore::DivSOp>(lhs, rhs);
590 else
591 return createBinary<moore::DivUOp>(lhs, rhs);
592 case BinaryOperator::Mod:
593 if (expr.type->isSigned())
594 return createBinary<moore::ModSOp>(lhs, rhs);
595 else
596 return createBinary<moore::ModUOp>(lhs, rhs);
597 case BinaryOperator::Power: {
598 // Slang casts the LHS and result of the `**` operator to a four-valued
599 // type, since the operator can return X even for two-valued inputs. To
600 // maintain uniform types across operands and results, cast the RHS to
601 // that four-valued type as well.
602 auto rhsCast = context.materializeConversion(
603 lhs.getType(), rhs, expr.right().type->isSigned(), rhs.getLoc());
604 if (expr.type->isSigned())
605 return createBinary<moore::PowSOp>(lhs, rhsCast);
606 else
607 return createBinary<moore::PowUOp>(lhs, rhsCast);
608 }
609
610 case BinaryOperator::BinaryAnd:
611 return createBinary<moore::AndOp>(lhs, rhs);
612 case BinaryOperator::BinaryOr:
613 return createBinary<moore::OrOp>(lhs, rhs);
614 case BinaryOperator::BinaryXor:
615 return createBinary<moore::XorOp>(lhs, rhs);
616 case BinaryOperator::BinaryXnor: {
617 auto result = createBinary<moore::XorOp>(lhs, rhs);
618 if (!result)
619 return {};
620 return moore::NotOp::create(builder, loc, result);
621 }
622
623 case BinaryOperator::Equality:
624 if (isa<moore::UnpackedArrayType>(lhs.getType()))
625 return moore::UArrayCmpOp::create(
626 builder, loc, moore::UArrayCmpPredicate::eq, lhs, rhs);
627 else if (isa<moore::StringType>(lhs.getType()))
628 return moore::StringCmpOp::create(
629 builder, loc, moore::StringCmpPredicate::eq, lhs, rhs);
630 else
631 return createBinary<moore::EqOp>(lhs, rhs);
632 case BinaryOperator::Inequality:
633 if (isa<moore::UnpackedArrayType>(lhs.getType()))
634 return moore::UArrayCmpOp::create(
635 builder, loc, moore::UArrayCmpPredicate::ne, lhs, rhs);
636 else if (isa<moore::StringType>(lhs.getType()))
637 return moore::StringCmpOp::create(
638 builder, loc, moore::StringCmpPredicate::ne, lhs, rhs);
639 else
640 return createBinary<moore::NeOp>(lhs, rhs);
641 case BinaryOperator::CaseEquality:
642 return createBinary<moore::CaseEqOp>(lhs, rhs);
643 case BinaryOperator::CaseInequality:
644 return createBinary<moore::CaseNeOp>(lhs, rhs);
645 case BinaryOperator::WildcardEquality:
646 return createBinary<moore::WildcardEqOp>(lhs, rhs);
647 case BinaryOperator::WildcardInequality:
648 return createBinary<moore::WildcardNeOp>(lhs, rhs);
649
650 case BinaryOperator::GreaterThanEqual:
651 if (expr.left().type->isSigned())
652 return createBinary<moore::SgeOp>(lhs, rhs);
653 else if (isa<moore::StringType>(lhs.getType()))
654 return moore::StringCmpOp::create(
655 builder, loc, moore::StringCmpPredicate::ge, lhs, rhs);
656 else
657 return createBinary<moore::UgeOp>(lhs, rhs);
658 case BinaryOperator::GreaterThan:
659 if (expr.left().type->isSigned())
660 return createBinary<moore::SgtOp>(lhs, rhs);
661 else if (isa<moore::StringType>(lhs.getType()))
662 return moore::StringCmpOp::create(
663 builder, loc, moore::StringCmpPredicate::gt, lhs, rhs);
664 else
665 return createBinary<moore::UgtOp>(lhs, rhs);
666 case BinaryOperator::LessThanEqual:
667 if (expr.left().type->isSigned())
668 return createBinary<moore::SleOp>(lhs, rhs);
669 else if (isa<moore::StringType>(lhs.getType()))
670 return moore::StringCmpOp::create(
671 builder, loc, moore::StringCmpPredicate::le, lhs, rhs);
672 else
673 return createBinary<moore::UleOp>(lhs, rhs);
674 case BinaryOperator::LessThan:
675 if (expr.left().type->isSigned())
676 return createBinary<moore::SltOp>(lhs, rhs);
677 else if (isa<moore::StringType>(lhs.getType()))
678 return moore::StringCmpOp::create(
679 builder, loc, moore::StringCmpPredicate::lt, lhs, rhs);
680 else
681 return createBinary<moore::UltOp>(lhs, rhs);
682
683 // See IEEE 1800-2017 § 11.4.7 "Logical operators".
684 case BinaryOperator::LogicalAnd: {
685 // TODO: This should short-circuit. Put the RHS code into a separate
686 // block.
687 lhs = context.convertToBool(lhs, domain);
688 if (!lhs)
689 return {};
690 rhs = context.convertToBool(rhs, domain);
691 if (!rhs)
692 return {};
693 return moore::AndOp::create(builder, loc, lhs, rhs);
694 }
695 case BinaryOperator::LogicalOr: {
696 // TODO: This should short-circuit. Put the RHS code into a separate
697 // block.
698 lhs = context.convertToBool(lhs, domain);
699 if (!lhs)
700 return {};
701 rhs = context.convertToBool(rhs, domain);
702 if (!rhs)
703 return {};
704 return moore::OrOp::create(builder, loc, lhs, rhs);
705 }
706 case BinaryOperator::LogicalImplication: {
707 // `(lhs -> rhs)` equivalent to `(!lhs || rhs)`.
708 lhs = context.convertToBool(lhs, domain);
709 if (!lhs)
710 return {};
711 rhs = context.convertToBool(rhs, domain);
712 if (!rhs)
713 return {};
714 auto notLHS = moore::NotOp::create(builder, loc, lhs);
715 return moore::OrOp::create(builder, loc, notLHS, rhs);
716 }
717 case BinaryOperator::LogicalEquivalence: {
718 // `(lhs <-> rhs)` equivalent to `(lhs && rhs) || (!lhs && !rhs)`.
719 lhs = context.convertToBool(lhs, domain);
720 if (!lhs)
721 return {};
722 rhs = context.convertToBool(rhs, domain);
723 if (!rhs)
724 return {};
725 auto notLHS = moore::NotOp::create(builder, loc, lhs);
726 auto notRHS = moore::NotOp::create(builder, loc, rhs);
727 auto both = moore::AndOp::create(builder, loc, lhs, rhs);
728 auto notBoth = moore::AndOp::create(builder, loc, notLHS, notRHS);
729 return moore::OrOp::create(builder, loc, both, notBoth);
730 }
731
732 case BinaryOperator::LogicalShiftLeft:
733 return createBinary<moore::ShlOp>(lhs, rhs);
734 case BinaryOperator::LogicalShiftRight:
735 return createBinary<moore::ShrOp>(lhs, rhs);
736 case BinaryOperator::ArithmeticShiftLeft:
737 return createBinary<moore::ShlOp>(lhs, rhs);
738 case BinaryOperator::ArithmeticShiftRight: {
739 // The `>>>` operator is an arithmetic right shift if the LHS operand is
740 // signed, or a logical right shift if the operand is unsigned.
741 lhs = context.convertToSimpleBitVector(lhs);
742 rhs = context.convertToSimpleBitVector(rhs);
743 if (!lhs || !rhs)
744 return {};
745 if (expr.type->isSigned())
746 return moore::AShrOp::create(builder, loc, lhs, rhs);
747 return moore::ShrOp::create(builder, loc, lhs, rhs);
748 }
749 }
750
751 mlir::emitError(loc, "unsupported binary operator");
752 return {};
753 }
754
755 // Handle `'0`, `'1`, `'x`, and `'z` literals.
756 Value visit(const slang::ast::UnbasedUnsizedIntegerLiteral &expr) {
757 return context.materializeSVInt(expr.getValue(), *expr.type, loc);
758 }
759
760 // Handle integer literals.
761 Value visit(const slang::ast::IntegerLiteral &expr) {
762 return context.materializeSVInt(expr.getValue(), *expr.type, loc);
763 }
764
765 // Handle time literals.
766 Value visit(const slang::ast::TimeLiteral &expr) {
767 // The time literal is expressed in the current time scale. Determine the
768 // conversion factor to convert the literal from the current time scale into
769 // femtoseconds, and round the scaled value to femtoseconds.
770 double scale = getTimeScaleInFemtoseconds(context);
771 double value = std::round(expr.getValue() * scale);
772 assert(value >= 0.0);
773
774 // Check that the value does not exceed what we can represent in the IR.
775 // Casting the maximum uint64 value to double changes its value from
776 // 18446744073709551615 to 18446744073709551616, which makes the comparison
777 // overestimate the largest number we can represent. To avoid this, round
778 // the maximum value down to the closest number that only has the front 53
779 // bits set. This matches the mantissa of a double, plus the implicit
780 // leading 1, ensuring that we can accurately represent the limit.
781 static constexpr uint64_t limit =
782 (std::numeric_limits<uint64_t>::max() >> 11) << 11;
783 if (value > limit) {
784 mlir::emitError(loc) << "time value is larger than " << limit << " fs";
785 return {};
786 }
787
788 return moore::ConstantTimeOp::create(builder, loc,
789 static_cast<uint64_t>(value));
790 }
791
792 // Handle replications.
793 Value visit(const slang::ast::ReplicationExpression &expr) {
794 auto type = context.convertType(*expr.type);
795 auto value = context.convertRvalueExpression(expr.concat());
796 if (!value)
797 return {};
798 return moore::ReplicateOp::create(builder, loc, type, value);
799 }
800
801 // Handle set membership operator.
802 Value visit(const slang::ast::InsideExpression &expr) {
803 auto lhs = context.convertToSimpleBitVector(
804 context.convertRvalueExpression(expr.left()));
805 if (!lhs)
806 return {};
807 // All conditions for determining whether it is inside.
808 SmallVector<Value> conditions;
809
810 // Traverse open range list.
811 for (const auto *listExpr : expr.rangeList()) {
812 Value cond;
813 // The open range list on the right-hand side of the inside operator is a
814 // comma-separated list of expressions or ranges.
815 if (const auto *openRange =
816 listExpr->as_if<slang::ast::ValueRangeExpression>()) {
817 // Handle ranges.
818 auto lowBound = context.convertToSimpleBitVector(
819 context.convertRvalueExpression(openRange->left()));
820 auto highBound = context.convertToSimpleBitVector(
821 context.convertRvalueExpression(openRange->right()));
822 if (!lowBound || !highBound)
823 return {};
824 Value leftValue, rightValue;
825 // Determine if the expression on the left-hand side is inclusively
826 // within the range.
827 if (openRange->left().type->isSigned() ||
828 expr.left().type->isSigned()) {
829 leftValue = moore::SgeOp::create(builder, loc, lhs, lowBound);
830 } else {
831 leftValue = moore::UgeOp::create(builder, loc, lhs, lowBound);
832 }
833 if (openRange->right().type->isSigned() ||
834 expr.left().type->isSigned()) {
835 rightValue = moore::SleOp::create(builder, loc, lhs, highBound);
836 } else {
837 rightValue = moore::UleOp::create(builder, loc, lhs, highBound);
838 }
839 cond = moore::AndOp::create(builder, loc, leftValue, rightValue);
840 } else {
841 // Handle expressions.
842 if (!listExpr->type->isIntegral()) {
843 if (listExpr->type->isUnpackedArray()) {
844 mlir::emitError(
845 loc, "unpacked arrays in 'inside' expressions not supported");
846 return {};
847 }
848 mlir::emitError(
849 loc, "only simple bit vectors supported in 'inside' expressions");
850 return {};
851 }
852
853 auto value = context.convertToSimpleBitVector(
854 context.convertRvalueExpression(*listExpr));
855 if (!value)
856 return {};
857 cond = moore::WildcardEqOp::create(builder, loc, lhs, value);
858 }
859 conditions.push_back(cond);
860 }
861
862 // Calculate the final result by `or` op.
863 auto result = conditions.back();
864 conditions.pop_back();
865 while (!conditions.empty()) {
866 result = moore::OrOp::create(builder, loc, conditions.back(), result);
867 conditions.pop_back();
868 }
869 return result;
870 }
871
872 // Handle conditional operator `?:`.
873 Value visit(const slang::ast::ConditionalExpression &expr) {
874 auto type = context.convertType(*expr.type);
875
876 // Handle condition.
877 if (expr.conditions.size() > 1) {
878 mlir::emitError(loc)
879 << "unsupported conditional expression with more than one condition";
880 return {};
881 }
882 const auto &cond = expr.conditions[0];
883 if (cond.pattern) {
884 mlir::emitError(loc) << "unsupported conditional expression with pattern";
885 return {};
886 }
887 auto value =
888 context.convertToBool(context.convertRvalueExpression(*cond.expr));
889 if (!value)
890 return {};
891 auto conditionalOp =
892 moore::ConditionalOp::create(builder, loc, type, value);
893
894 // Create blocks for true region and false region.
895 auto &trueBlock = conditionalOp.getTrueRegion().emplaceBlock();
896 auto &falseBlock = conditionalOp.getFalseRegion().emplaceBlock();
897
898 OpBuilder::InsertionGuard g(builder);
899
900 // Handle left expression.
901 builder.setInsertionPointToStart(&trueBlock);
902 auto trueValue = context.convertRvalueExpression(expr.left(), type);
903 if (!trueValue)
904 return {};
905 moore::YieldOp::create(builder, loc, trueValue);
906
907 // Handle right expression.
908 builder.setInsertionPointToStart(&falseBlock);
909 auto falseValue = context.convertRvalueExpression(expr.right(), type);
910 if (!falseValue)
911 return {};
912 moore::YieldOp::create(builder, loc, falseValue);
913
914 return conditionalOp.getResult();
915 }
916
917 /// Handle calls.
918 Value visit(const slang::ast::CallExpression &expr) {
919 // Class method calls are currently not supported.
920 if (expr.thisClass()) {
921 mlir::emitError(loc, "unsupported class method call");
922 return {};
923 }
924
925 // Try to materialize constant values directly.
926 auto constant = context.evaluateConstant(expr);
927 if (auto value = context.materializeConstant(constant, *expr.type, loc))
928 return value;
929
930 return std::visit(
931 [&](auto &subroutine) { return visitCall(expr, subroutine); },
932 expr.subroutine);
933 }
934
935 /// Handle subroutine calls.
936 Value visitCall(const slang::ast::CallExpression &expr,
937 const slang::ast::SubroutineSymbol *subroutine) {
938 auto *lowering = context.declareFunction(*subroutine);
939 if (!lowering)
940 return {};
941
942 // Convert the call arguments. Input arguments are converted to an rvalue.
943 // All other arguments are converted to lvalues and passed into the function
944 // by reference.
945 SmallVector<Value> arguments;
946 for (auto [callArg, declArg] :
947 llvm::zip(expr.arguments(), subroutine->getArguments())) {
948
949 // Unpack the `<expr> = EmptyArgument` pattern emitted by Slang for output
950 // and inout arguments.
951 auto *expr = callArg;
952 if (const auto *assign = expr->as_if<slang::ast::AssignmentExpression>())
953 expr = &assign->left();
954
955 Value value;
956 if (declArg->direction == slang::ast::ArgumentDirection::In)
957 value = context.convertRvalueExpression(*expr);
958 else
959 value = context.convertLvalueExpression(*expr);
960 if (!value)
961 return {};
962 arguments.push_back(value);
963 }
964
965 // Create the call.
966 auto callOp =
967 mlir::func::CallOp::create(builder, loc, lowering->op, arguments);
968
969 // For calls to void functions we need to have a value to return from this
970 // function. Create a dummy `unrealized_conversion_cast`, which will get
971 // deleted again later on.
972 if (callOp.getNumResults() == 0)
973 return mlir::UnrealizedConversionCastOp::create(
974 builder, loc, moore::VoidType::get(context.getContext()),
975 ValueRange{})
976 .getResult(0);
977
978 return callOp.getResult(0);
979 }
980
981 /// Handle system calls.
982 Value visitCall(const slang::ast::CallExpression &expr,
983 const slang::ast::CallExpression::SystemCallInfo &info) {
984 const auto &subroutine = *info.subroutine;
985
986 // $rose, $fell, $stable, $changed, and $past are only valid in
987 // the context of properties and assertions. Those are treated in the
988 // LTLDialect; treat them there instead.
989 bool isAssertionCall =
990 llvm::StringSwitch<bool>(subroutine.name)
991 .Cases("$rose", "$fell", "$stable", "$past", true)
992 .Default(false);
993
994 if (isAssertionCall)
995 return context.convertAssertionCallExpression(expr, info, loc);
996
997 auto args = expr.arguments();
998
999 FailureOr<Value> result;
1000 Value value;
1001
1002 // $sformatf() and $sformat look like system tasks, but we handle string
1003 // formatting differently from expression evaluation, so handle them
1004 // separately.
1005 // According to IEEE 1800-2023 Section 21.3.3 "Formatting data to a
1006 // string" $sformatf works just like the string formatting but returns
1007 // a StringType.
1008 if (!subroutine.name.compare("$sformatf")) {
1009 // Create the FormatString
1010 auto fmtValue = context.convertFormatString(
1011 expr.arguments(), loc, moore::IntFormat::Decimal, false);
1012 if (failed(fmtValue))
1013 return {};
1014 return fmtValue.value();
1015 }
1016
1017 switch (args.size()) {
1018 case (0):
1019 result = context.convertSystemCallArity0(subroutine, loc);
1020 break;
1021
1022 case (1):
1023 value = context.convertRvalueExpression(*args[0]);
1024 if (!value)
1025 return {};
1026 result = context.convertSystemCallArity1(subroutine, loc, value);
1027 break;
1028
1029 default:
1030 mlir::emitError(loc)
1031 << "system call with more than 1 argument is not supported";
1032 break;
1033 }
1034
1035 if (failed(result))
1036 return {};
1037 if (*result)
1038 return *result;
1039 mlir::emitError(loc) << "unsupported system call `" << subroutine.name
1040 << "`";
1041 return {};
1042 }
1043
1044 /// Handle string literals.
1045 Value visit(const slang::ast::StringLiteral &expr) {
1046 auto type = context.convertType(*expr.type);
1047 return moore::StringConstantOp::create(builder, loc, type, expr.getValue());
1048 }
1049
1050 /// Handle real literals.
1051 Value visit(const slang::ast::RealLiteral &expr) {
1052 return moore::RealLiteralOp::create(
1053 builder, loc, builder.getF64FloatAttr(expr.getValue()));
1054 }
1055
1056 /// Helper function to convert RValues at creation of a new Struct, Array or
1057 /// Int.
1058 FailureOr<SmallVector<Value>>
1059 convertElements(const slang::ast::AssignmentPatternExpressionBase &expr,
1060 std::variant<Type, ArrayRef<Type>> expectedTypes,
1061 unsigned replCount) {
1062 const auto &elts = expr.elements();
1063 const size_t elementCount = elts.size();
1064
1065 // Inspect the variant.
1066 const bool hasBroadcast =
1067 std::holds_alternative<Type>(expectedTypes) &&
1068 static_cast<bool>(std::get<Type>(expectedTypes)); // non-null Type
1069
1070 const bool hasPerElem =
1071 std::holds_alternative<ArrayRef<Type>>(expectedTypes) &&
1072 !std::get<ArrayRef<Type>>(expectedTypes).empty();
1073
1074 // If per-element types are provided, enforce arity.
1075 if (hasPerElem) {
1076 auto types = std::get<ArrayRef<Type>>(expectedTypes);
1077 if (types.size() != elementCount) {
1078 mlir::emitError(loc)
1079 << "assignment pattern arity mismatch: expected " << types.size()
1080 << " elements, got " << elementCount;
1081 return failure();
1082 }
1083 }
1084
1085 SmallVector<Value> converted;
1086 converted.reserve(elementCount * std::max(1u, replCount));
1087
1088 // Convert each element heuristically, no type is expected
1089 if (!hasBroadcast && !hasPerElem) {
1090 // No expected type info.
1091 for (const auto *elementExpr : elts) {
1092 Value v = context.convertRvalueExpression(*elementExpr);
1093 if (!v)
1094 return failure();
1095 converted.push_back(v);
1096 }
1097 } else if (hasBroadcast) {
1098 // Same expected type for all elements.
1099 Type want = std::get<Type>(expectedTypes);
1100 for (const auto *elementExpr : elts) {
1101 Value v = want ? context.convertRvalueExpression(*elementExpr, want)
1102 : context.convertRvalueExpression(*elementExpr);
1103 if (!v)
1104 return failure();
1105 converted.push_back(v);
1106 }
1107 } else { // hasPerElem, individual type is expected for each element
1108 auto types = std::get<ArrayRef<Type>>(expectedTypes);
1109 for (size_t i = 0; i < elementCount; ++i) {
1110 Type want = types[i];
1111 const auto *elementExpr = elts[i];
1112 Value v = want ? context.convertRvalueExpression(*elementExpr, want)
1113 : context.convertRvalueExpression(*elementExpr);
1114 if (!v)
1115 return failure();
1116 converted.push_back(v);
1117 }
1118 }
1119
1120 for (unsigned i = 1; i < replCount; ++i)
1121 converted.append(converted.begin(), converted.begin() + elementCount);
1122
1123 return converted;
1124 }
1125
1126 /// Handle assignment patterns.
1127 Value visitAssignmentPattern(
1128 const slang::ast::AssignmentPatternExpressionBase &expr,
1129 unsigned replCount = 1) {
1130 auto type = context.convertType(*expr.type);
1131 const auto &elts = expr.elements();
1132
1133 // Handle integers.
1134 if (auto intType = dyn_cast<moore::IntType>(type)) {
1135 auto elements = convertElements(expr, {}, replCount);
1136
1137 if (failed(elements))
1138 return {};
1139
1140 assert(intType.getWidth() == elements->size());
1141 std::reverse(elements->begin(), elements->end());
1142 return moore::ConcatOp::create(builder, loc, intType, *elements);
1143 }
1144
1145 // Handle packed structs.
1146 if (auto structType = dyn_cast<moore::StructType>(type)) {
1147 SmallVector<Type> expectedTy;
1148 expectedTy.reserve(structType.getMembers().size());
1149 for (auto member : structType.getMembers())
1150 expectedTy.push_back(member.type);
1151
1152 FailureOr<SmallVector<Value>> elements;
1153 if (expectedTy.size() == elts.size())
1154 elements = convertElements(expr, expectedTy, replCount);
1155 else
1156 elements = convertElements(expr, {}, replCount);
1157
1158 if (failed(elements))
1159 return {};
1160
1161 assert(structType.getMembers().size() == elements->size());
1162 return moore::StructCreateOp::create(builder, loc, structType, *elements);
1163 }
1164
1165 // Handle unpacked structs.
1166 if (auto structType = dyn_cast<moore::UnpackedStructType>(type)) {
1167 SmallVector<Type> expectedTy;
1168 expectedTy.reserve(structType.getMembers().size());
1169 for (auto member : structType.getMembers())
1170 expectedTy.push_back(member.type);
1171
1172 FailureOr<SmallVector<Value>> elements;
1173 if (expectedTy.size() == elts.size())
1174 elements = convertElements(expr, expectedTy, replCount);
1175 else
1176 elements = convertElements(expr, {}, replCount);
1177
1178 if (failed(elements))
1179 return {};
1180
1181 assert(structType.getMembers().size() == elements->size());
1182
1183 return moore::StructCreateOp::create(builder, loc, structType, *elements);
1184 }
1185
1186 // Handle packed arrays.
1187 if (auto arrayType = dyn_cast<moore::ArrayType>(type)) {
1188 auto elements =
1189 convertElements(expr, arrayType.getElementType(), replCount);
1190
1191 if (failed(elements))
1192 return {};
1193
1194 assert(arrayType.getSize() == elements->size());
1195 return moore::ArrayCreateOp::create(builder, loc, arrayType, *elements);
1196 }
1197
1198 // Handle unpacked arrays.
1199 if (auto arrayType = dyn_cast<moore::UnpackedArrayType>(type)) {
1200 auto elements =
1201 convertElements(expr, arrayType.getElementType(), replCount);
1202
1203 if (failed(elements))
1204 return {};
1205
1206 assert(arrayType.getSize() == elements->size());
1207 return moore::ArrayCreateOp::create(builder, loc, arrayType, *elements);
1208 }
1209
1210 mlir::emitError(loc) << "unsupported assignment pattern with type " << type;
1211 return {};
1212 }
1213
1214 Value visit(const slang::ast::SimpleAssignmentPatternExpression &expr) {
1215 return visitAssignmentPattern(expr);
1216 }
1217
1218 Value visit(const slang::ast::StructuredAssignmentPatternExpression &expr) {
1219 return visitAssignmentPattern(expr);
1220 }
1221
1222 Value visit(const slang::ast::ReplicatedAssignmentPatternExpression &expr) {
1223 auto count =
1224 context.evaluateConstant(expr.count()).integer().as<unsigned>();
1225 assert(count && "Slang guarantees constant non-zero replication count");
1226 return visitAssignmentPattern(expr, *count);
1227 }
1228
1229 Value visit(const slang::ast::StreamingConcatenationExpression &expr) {
1230 SmallVector<Value> operands;
1231 for (auto stream : expr.streams()) {
1232 auto operandLoc = context.convertLocation(stream.operand->sourceRange);
1233 if (!stream.constantWithWidth.has_value() && stream.withExpr) {
1234 mlir::emitError(operandLoc)
1235 << "Moore only support streaming "
1236 "concatenation with fixed size 'with expression'";
1237 return {};
1238 }
1239 Value value;
1240 if (stream.constantWithWidth.has_value()) {
1241 value = context.convertRvalueExpression(*stream.withExpr);
1242 auto type = cast<moore::UnpackedType>(value.getType());
1243 auto intType = moore::IntType::get(
1244 context.getContext(), type.getBitSize().value(), type.getDomain());
1245 // Do not care if it's signed, because we will not do expansion.
1246 value = context.materializeConversion(intType, value, false, loc);
1247 } else {
1248 value = context.convertRvalueExpression(*stream.operand);
1249 }
1250
1251 value = context.convertToSimpleBitVector(value);
1252 if (!value)
1253 return {};
1254 operands.push_back(value);
1255 }
1256 Value value;
1257
1258 if (operands.size() == 1) {
1259 // There must be at least one element, otherwise slang will report an
1260 // error.
1261 value = operands.front();
1262 } else {
1263 value = moore::ConcatOp::create(builder, loc, operands).getResult();
1264 }
1265
1266 if (expr.getSliceSize() == 0) {
1267 return value;
1268 }
1269
1270 auto type = cast<moore::IntType>(value.getType());
1271 SmallVector<Value> slicedOperands;
1272 auto iterMax = type.getWidth() / expr.getSliceSize();
1273 auto remainSize = type.getWidth() % expr.getSliceSize();
1274
1275 for (size_t i = 0; i < iterMax; i++) {
1276 auto extractResultType = moore::IntType::get(
1277 context.getContext(), expr.getSliceSize(), type.getDomain());
1278
1279 auto extracted = moore::ExtractOp::create(builder, loc, extractResultType,
1280 value, i * expr.getSliceSize());
1281 slicedOperands.push_back(extracted);
1282 }
1283 // Handle other wire
1284 if (remainSize) {
1285 auto extractResultType = moore::IntType::get(
1286 context.getContext(), remainSize, type.getDomain());
1287
1288 auto extracted =
1289 moore::ExtractOp::create(builder, loc, extractResultType, value,
1290 iterMax * expr.getSliceSize());
1291 slicedOperands.push_back(extracted);
1292 }
1293
1294 return moore::ConcatOp::create(builder, loc, slicedOperands);
1295 }
1296
1297 Value visit(const slang::ast::AssertionInstanceExpression &expr) {
1298 return context.convertAssertionExpression(expr.body, loc);
1299 }
1300
1301 /// Emit an error for all other expressions.
1302 template <typename T>
1303 Value visit(T &&node) {
1304 mlir::emitError(loc, "unsupported expression: ")
1305 << slang::ast::toString(node.kind);
1306 return {};
1307 }
1308
1309 Value visitInvalid(const slang::ast::Expression &expr) {
1310 mlir::emitError(loc, "invalid expression");
1311 return {};
1312 }
1313};
1314} // namespace
1315
1316//===----------------------------------------------------------------------===//
1317// Lvalue Conversion
1318//===----------------------------------------------------------------------===//
1319
1320namespace {
1321struct LvalueExprVisitor : public ExprVisitor {
1322 LvalueExprVisitor(Context &context, Location loc)
1323 : ExprVisitor(context, loc, /*isLvalue=*/true) {}
1324 using ExprVisitor::visit;
1325
1326 // Handle named values, such as references to declared variables.
1327 Value visit(const slang::ast::NamedValueExpression &expr) {
1328 if (auto value = context.valueSymbols.lookup(&expr.symbol))
1329 return value;
1330 auto d = mlir::emitError(loc, "unknown name `") << expr.symbol.name << "`";
1331 d.attachNote(context.convertLocation(expr.symbol.location))
1332 << "no lvalue generated for " << slang::ast::toString(expr.symbol.kind);
1333 return {};
1334 }
1335
1336 // Handle hierarchical values, such as `Top.sub.var = x`.
1337 Value visit(const slang::ast::HierarchicalValueExpression &expr) {
1338 if (auto value = context.valueSymbols.lookup(&expr.symbol))
1339 return value;
1340
1341 // Emit an error for those hierarchical values not recorded in the
1342 // `valueSymbols`.
1343 auto d = mlir::emitError(loc, "unknown hierarchical name `")
1344 << expr.symbol.name << "`";
1345 d.attachNote(context.convertLocation(expr.symbol.location))
1346 << "no lvalue generated for " << slang::ast::toString(expr.symbol.kind);
1347 return {};
1348 }
1349
1350 Value visit(const slang::ast::StreamingConcatenationExpression &expr) {
1351 SmallVector<Value> operands;
1352 for (auto stream : expr.streams()) {
1353 auto operandLoc = context.convertLocation(stream.operand->sourceRange);
1354 if (!stream.constantWithWidth.has_value() && stream.withExpr) {
1355 mlir::emitError(operandLoc)
1356 << "Moore only support streaming "
1357 "concatenation with fixed size 'with expression'";
1358 return {};
1359 }
1360 Value value;
1361 if (stream.constantWithWidth.has_value()) {
1362 value = context.convertLvalueExpression(*stream.withExpr);
1363 auto type = cast<moore::UnpackedType>(
1364 cast<moore::RefType>(value.getType()).getNestedType());
1365 auto intType = moore::RefType::get(moore::IntType::get(
1366 context.getContext(), type.getBitSize().value(), type.getDomain()));
1367 // Do not care if it's signed, because we will not do expansion.
1368 value = context.materializeConversion(intType, value, false, loc);
1369 } else {
1370 value = context.convertLvalueExpression(*stream.operand);
1371 }
1372
1373 if (!value)
1374 return {};
1375 operands.push_back(value);
1376 }
1377 Value value;
1378 if (operands.size() == 1) {
1379 // There must be at least one element, otherwise slang will report an
1380 // error.
1381 value = operands.front();
1382 } else {
1383 value = moore::ConcatRefOp::create(builder, loc, operands).getResult();
1384 }
1385
1386 if (expr.getSliceSize() == 0) {
1387 return value;
1388 }
1389
1390 auto type = cast<moore::IntType>(
1391 cast<moore::RefType>(value.getType()).getNestedType());
1392 SmallVector<Value> slicedOperands;
1393 auto widthSum = type.getWidth();
1394 auto domain = type.getDomain();
1395 auto iterMax = widthSum / expr.getSliceSize();
1396 auto remainSize = widthSum % expr.getSliceSize();
1397
1398 for (size_t i = 0; i < iterMax; i++) {
1399 auto extractResultType = moore::RefType::get(moore::IntType::get(
1400 context.getContext(), expr.getSliceSize(), domain));
1401
1402 auto extracted = moore::ExtractRefOp::create(
1403 builder, loc, extractResultType, value, i * expr.getSliceSize());
1404 slicedOperands.push_back(extracted);
1405 }
1406 // Handle other wire
1407 if (remainSize) {
1408 auto extractResultType = moore::RefType::get(
1409 moore::IntType::get(context.getContext(), remainSize, domain));
1410
1411 auto extracted =
1412 moore::ExtractRefOp::create(builder, loc, extractResultType, value,
1413 iterMax * expr.getSliceSize());
1414 slicedOperands.push_back(extracted);
1415 }
1416
1417 return moore::ConcatRefOp::create(builder, loc, slicedOperands);
1418 }
1419
1420 /// Emit an error for all other expressions.
1421 template <typename T>
1422 Value visit(T &&node) {
1423 return context.convertRvalueExpression(node);
1424 }
1425
1426 Value visitInvalid(const slang::ast::Expression &expr) {
1427 mlir::emitError(loc, "invalid expression");
1428 return {};
1429 }
1430};
1431} // namespace
1432
1433//===----------------------------------------------------------------------===//
1434// Entry Points
1435//===----------------------------------------------------------------------===//
1436
1437Value Context::convertRvalueExpression(const slang::ast::Expression &expr,
1438 Type requiredType) {
1439 auto loc = convertLocation(expr.sourceRange);
1440 auto value = expr.visit(RvalueExprVisitor(*this, loc));
1441 if (value && requiredType)
1442 value =
1443 materializeConversion(requiredType, value, expr.type->isSigned(), loc);
1444 return value;
1445}
1446
1447Value Context::convertLvalueExpression(const slang::ast::Expression &expr) {
1448 auto loc = convertLocation(expr.sourceRange);
1449 return expr.visit(LvalueExprVisitor(*this, loc));
1450}
1451// NOLINTEND(misc-no-recursion)
1452
1453/// Helper function to convert a value to its "truthy" boolean value.
1454Value Context::convertToBool(Value value) {
1455 if (!value)
1456 return {};
1457 if (auto type = dyn_cast_or_null<moore::IntType>(value.getType()))
1458 if (type.getBitSize() == 1)
1459 return value;
1460 if (auto type = dyn_cast_or_null<moore::UnpackedType>(value.getType()))
1461 return moore::BoolCastOp::create(builder, value.getLoc(), value);
1462 mlir::emitError(value.getLoc(), "expression of type ")
1463 << value.getType() << " cannot be cast to a boolean";
1464 return {};
1465}
1466
1467/// Materialize a Slang integer literal as a constant op.
1468Value Context::materializeSVInt(const slang::SVInt &svint,
1469 const slang::ast::Type &astType, Location loc) {
1470 auto type = convertType(astType);
1471 if (!type)
1472 return {};
1473
1474 bool typeIsFourValued = false;
1475 if (auto unpackedType = dyn_cast<moore::UnpackedType>(type))
1476 typeIsFourValued = unpackedType.getDomain() == moore::Domain::FourValued;
1477
1478 auto fvint = convertSVIntToFVInt(svint);
1479 auto intType = moore::IntType::get(getContext(), fvint.getBitWidth(),
1480 fvint.hasUnknown() || typeIsFourValued
1483 auto result = moore::ConstantOp::create(builder, loc, intType, fvint);
1484 return materializeConversion(type, result, astType.isSigned(), loc);
1485}
1486
1488 const slang::ConstantValue &constant,
1489 const slang::ast::FixedSizeUnpackedArrayType &astType, Location loc) {
1490
1491 auto type = convertType(astType);
1492 if (!type)
1493 return {};
1494
1495 // Check whether underlying type is an integer, if so, get bit width
1496 unsigned bitWidth;
1497 if (astType.elementType.isIntegral())
1498 bitWidth = astType.elementType.getBitWidth();
1499 else
1500 return {};
1501
1502 bool typeIsFourValued = false;
1503
1504 // Check whether the underlying type is four-valued
1505 if (auto unpackedType = dyn_cast<moore::UnpackedType>(type))
1506 typeIsFourValued = unpackedType.getDomain() == moore::Domain::FourValued;
1507 else
1508 return {};
1509
1510 auto domain =
1512
1513 // Construct the integer type this is an unpacked array of; if possible keep
1514 // it two-valued, unless any entry is four-valued or the underlying type is
1515 // four-valued
1516 auto intType = moore::IntType::get(getContext(), bitWidth, domain);
1517 // Construct the full array type from intType
1518 auto arrType = moore::UnpackedArrayType::get(
1519 getContext(), constant.elements().size(), intType);
1520
1521 llvm::SmallVector<mlir::Value> elemVals;
1522 moore::ConstantOp constOp;
1523
1524 mlir::OpBuilder::InsertionGuard guard(builder);
1525
1526 // Add one ConstantOp for every element in the array
1527 for (auto elem : constant.elements()) {
1528 FVInt fvInt = convertSVIntToFVInt(elem.integer());
1529 constOp = moore::ConstantOp::create(builder, loc, intType, fvInt);
1530 elemVals.push_back(constOp.getResult());
1531 }
1532
1533 // Take the result of each ConstantOp and concatenate them into an array (of
1534 // constant values).
1535 auto arrayOp = moore::ArrayCreateOp::create(builder, loc, arrType, elemVals);
1536
1537 return arrayOp.getResult();
1538}
1539
1540Value Context::materializeConstant(const slang::ConstantValue &constant,
1541 const slang::ast::Type &type, Location loc) {
1542
1543 if (auto *arr = type.as_if<slang::ast::FixedSizeUnpackedArrayType>())
1544 return materializeFixedSizeUnpackedArrayType(constant, *arr, loc);
1545 if (constant.isInteger())
1546 return materializeSVInt(constant.integer(), type, loc);
1547
1548 return {};
1549}
1550
1551slang::ConstantValue
1552Context::evaluateConstant(const slang::ast::Expression &expr) {
1553 using slang::ast::EvalFlags;
1554 slang::ast::EvalContext evalContext(
1555 slang::ast::ASTContext(compilation.getRoot(),
1556 slang::ast::LookupLocation::max),
1557 EvalFlags::CacheResults | EvalFlags::SpecparamsAllowed);
1558 return expr.eval(evalContext);
1559}
1560
1561/// Helper function to convert a value to its "truthy" boolean value and
1562/// convert it to the given domain.
1563Value Context::convertToBool(Value value, Domain domain) {
1564 value = convertToBool(value);
1565 if (!value)
1566 return {};
1567 auto type = moore::IntType::get(getContext(), 1, domain);
1568 return materializeConversion(type, value, false, value.getLoc());
1569}
1570
1572 if (!value)
1573 return {};
1574 if (isa<moore::IntType>(value.getType()))
1575 return value;
1576
1577 // Some operations in Slang's AST, for example bitwise or `|`, don't cast
1578 // packed struct/array operands to simple bit vectors but directly operate
1579 // on the struct/array. Since the corresponding IR ops operate only on
1580 // simple bit vectors, insert a conversion in this case.
1581 if (auto packed = dyn_cast<moore::PackedType>(value.getType()))
1582 if (auto sbvType = packed.getSimpleBitVector())
1583 return materializeConversion(sbvType, value, false, value.getLoc());
1584
1585 mlir::emitError(value.getLoc()) << "expression of type " << value.getType()
1586 << " cannot be cast to a simple bit vector";
1587 return {};
1588}
1589
1590/// Create the necessary operations to convert from a `PackedType` to the
1591/// corresponding simple bit vector `IntType`. This will apply special handling
1592/// to time values, which requires scaling by the local timescale.
1593static Value materializePackedToSBVConversion(Context &context, Value value,
1594 Location loc) {
1595 if (isa<moore::IntType>(value.getType()))
1596 return value;
1597
1598 auto &builder = context.builder;
1599 auto packedType = cast<moore::PackedType>(value.getType());
1600 auto intType = packedType.getSimpleBitVector();
1601 assert(intType);
1602
1603 // If we are converting from a time to an integer, divide the integer by the
1604 // timescale.
1605 if (isa<moore::TimeType>(packedType) &&
1607 value = builder.createOrFold<moore::TimeToLogicOp>(loc, value);
1608 auto scale = moore::ConstantOp::create(builder, loc, intType,
1610 return builder.createOrFold<moore::DivUOp>(loc, value, scale);
1611 }
1612
1613 // If this is an aggregate type, make sure that it does not contain any
1614 // `TimeType` fields. These require special conversion to ensure that the
1615 // local timescale is in effect.
1616 if (packedType.containsTimeType()) {
1617 mlir::emitError(loc) << "unsupported conversion: " << packedType
1618 << " cannot be converted to " << intType
1619 << "; contains a time type";
1620 return {};
1621 }
1622
1623 // Otherwise create a simple `PackedToSBVOp` for the conversion.
1624 return builder.createOrFold<moore::PackedToSBVOp>(loc, value);
1625}
1626
1627/// Create the necessary operations to convert from a simple bit vector
1628/// `IntType` to an equivalent `PackedType`. This will apply special handling to
1629/// time values, which requires scaling by the local timescale.
1631 moore::PackedType packedType,
1632 Value value, Location loc) {
1633 if (value.getType() == packedType)
1634 return value;
1635
1636 auto &builder = context.builder;
1637 auto intType = cast<moore::IntType>(value.getType());
1638 assert(intType && intType == packedType.getSimpleBitVector());
1639
1640 // If we are converting from an integer to a time, multiply the integer by the
1641 // timescale.
1642 if (isa<moore::TimeType>(packedType) &&
1644 auto scale = moore::ConstantOp::create(builder, loc, intType,
1646 value = builder.createOrFold<moore::MulOp>(loc, value, scale);
1647 return builder.createOrFold<moore::LogicToTimeOp>(loc, value);
1648 }
1649
1650 // If this is an aggregate type, make sure that it does not contain any
1651 // `TimeType` fields. These require special conversion to ensure that the
1652 // local timescale is in effect.
1653 if (packedType.containsTimeType()) {
1654 mlir::emitError(loc) << "unsupported conversion: " << intType
1655 << " cannot be converted to " << packedType
1656 << "; contains a time type";
1657 return {};
1658 }
1659
1660 // Otherwise create a simple `PackedToSBVOp` for the conversion.
1661 return builder.createOrFold<moore::SBVToPackedOp>(loc, packedType, value);
1662}
1663
1664Value Context::materializeConversion(Type type, Value value, bool isSigned,
1665 Location loc) {
1666 // Nothing to do if the types are already equal.
1667 if (type == value.getType())
1668 return value;
1669
1670 // Handle packed types which can be converted to a simple bit vector. This
1671 // allows us to perform resizing and domain casting on that bit vector.
1672 auto dstPacked = dyn_cast<moore::PackedType>(type);
1673 auto srcPacked = dyn_cast<moore::PackedType>(value.getType());
1674 auto dstInt = dstPacked ? dstPacked.getSimpleBitVector() : moore::IntType();
1675 auto srcInt = srcPacked ? srcPacked.getSimpleBitVector() : moore::IntType();
1676
1677 if (dstInt && srcInt) {
1678 // Convert the value to a simple bit vector if it isn't one already.
1679 value = materializePackedToSBVConversion(*this, value, loc);
1680 if (!value)
1681 return {};
1682
1683 // Create truncation or sign/zero extension ops depending on the source and
1684 // destination width.
1685 auto resizedType = moore::IntType::get(
1686 value.getContext(), dstInt.getWidth(), srcPacked.getDomain());
1687 if (dstInt.getWidth() < srcInt.getWidth()) {
1688 value = builder.createOrFold<moore::TruncOp>(loc, resizedType, value);
1689 } else if (dstInt.getWidth() > srcInt.getWidth()) {
1690 if (isSigned)
1691 value = builder.createOrFold<moore::SExtOp>(loc, resizedType, value);
1692 else
1693 value = builder.createOrFold<moore::ZExtOp>(loc, resizedType, value);
1694 }
1695
1696 // Convert the domain if needed.
1697 if (dstInt.getDomain() != srcInt.getDomain()) {
1698 if (dstInt.getDomain() == moore::Domain::TwoValued)
1699 value = builder.createOrFold<moore::LogicToIntOp>(loc, value);
1700 else if (dstInt.getDomain() == moore::Domain::FourValued)
1701 value = builder.createOrFold<moore::IntToLogicOp>(loc, value);
1702 }
1703
1704 // Convert the value from a simple bit vector back to the packed type.
1705 value = materializeSBVToPackedConversion(*this, dstPacked, value, loc);
1706 if (!value)
1707 return {};
1708
1709 assert(value.getType() == type);
1710 return value;
1711 }
1712
1713 // Convert from FormatStringType to StringType
1714 if (isa<moore::StringType>(type) &&
1715 isa<moore::FormatStringType>(value.getType())) {
1716 return builder.createOrFold<moore::FormatStringToStringOp>(loc, value);
1717 }
1718
1719 // Convert from StringType to FormatStringType
1720 if (isa<moore::FormatStringType>(type) &&
1721 isa<moore::StringType>(value.getType())) {
1722 return builder.createOrFold<moore::FormatStringOp>(loc, value);
1723 }
1724
1725 // TODO: Handle other conversions with dedicated ops.
1726 if (value.getType() != type)
1727 value = moore::ConversionOp::create(builder, loc, type, value);
1728 return value;
1729}
1730
1731FailureOr<Value>
1732Context::convertSystemCallArity0(const slang::ast::SystemSubroutine &subroutine,
1733 Location loc) {
1734
1735 auto systemCallRes =
1736 llvm::StringSwitch<std::function<FailureOr<Value>()>>(subroutine.name)
1737 .Case("$urandom",
1738 [&]() -> Value {
1739 return moore::UrandomBIOp::create(builder, loc, nullptr);
1740 })
1741 .Case("$random",
1742 [&]() -> Value {
1743 return moore::RandomBIOp::create(builder, loc, nullptr);
1744 })
1745 .Case(
1746 "$time",
1747 [&]() -> Value { return moore::TimeBIOp::create(builder, loc); })
1748 .Case(
1749 "$stime",
1750 [&]() -> Value { return moore::TimeBIOp::create(builder, loc); })
1751 .Case(
1752 "$realtime",
1753 [&]() -> Value { return moore::TimeBIOp::create(builder, loc); })
1754 .Default([&]() -> Value { return {}; });
1755 return systemCallRes();
1756}
1757
1758FailureOr<Value>
1759Context::convertSystemCallArity1(const slang::ast::SystemSubroutine &subroutine,
1760 Location loc, Value value) {
1761 auto systemCallRes =
1762 llvm::StringSwitch<std::function<FailureOr<Value>()>>(subroutine.name)
1763 // Signed and unsigned system functions.
1764 .Case("$signed", [&]() { return value; })
1765 .Case("$unsigned", [&]() { return value; })
1766
1767 // Math functions in SystemVerilog.
1768 .Case("$clog2",
1769 [&]() -> FailureOr<Value> {
1770 value = convertToSimpleBitVector(value);
1771 if (!value)
1772 return failure();
1773 return (Value)moore::Clog2BIOp::create(builder, loc, value);
1774 })
1775 .Case("$ln",
1776 [&]() -> Value {
1777 return moore::LnBIOp::create(builder, loc, value);
1778 })
1779 .Case("$log10",
1780 [&]() -> Value {
1781 return moore::Log10BIOp::create(builder, loc, value);
1782 })
1783 .Case("$sin",
1784 [&]() -> Value {
1785 return moore::SinBIOp::create(builder, loc, value);
1786 })
1787 .Case("$cos",
1788 [&]() -> Value {
1789 return moore::CosBIOp::create(builder, loc, value);
1790 })
1791 .Case("$tan",
1792 [&]() -> Value {
1793 return moore::TanBIOp::create(builder, loc, value);
1794 })
1795 .Case("$exp",
1796 [&]() -> Value {
1797 return moore::ExpBIOp::create(builder, loc, value);
1798 })
1799 .Case("$sqrt",
1800 [&]() -> Value {
1801 return moore::SqrtBIOp::create(builder, loc, value);
1802 })
1803 .Case("$floor",
1804 [&]() -> Value {
1805 return moore::FloorBIOp::create(builder, loc, value);
1806 })
1807 .Case("$ceil",
1808 [&]() -> Value {
1809 return moore::CeilBIOp::create(builder, loc, value);
1810 })
1811 .Case("$asin",
1812 [&]() -> Value {
1813 return moore::AsinBIOp::create(builder, loc, value);
1814 })
1815 .Case("$acos",
1816 [&]() -> Value {
1817 return moore::AcosBIOp::create(builder, loc, value);
1818 })
1819 .Case("$atan",
1820 [&]() -> Value {
1821 return moore::AtanBIOp::create(builder, loc, value);
1822 })
1823 .Case("$sinh",
1824 [&]() -> Value {
1825 return moore::SinhBIOp::create(builder, loc, value);
1826 })
1827 .Case("$cosh",
1828 [&]() -> Value {
1829 return moore::CoshBIOp::create(builder, loc, value);
1830 })
1831 .Case("$tanh",
1832 [&]() -> Value {
1833 return moore::TanhBIOp::create(builder, loc, value);
1834 })
1835 .Case("$asinh",
1836 [&]() -> Value {
1837 return moore::AsinhBIOp::create(builder, loc, value);
1838 })
1839 .Case("$acosh",
1840 [&]() -> Value {
1841 return moore::AcoshBIOp::create(builder, loc, value);
1842 })
1843 .Case("$atanh",
1844 [&]() -> Value {
1845 return moore::AtanhBIOp::create(builder, loc, value);
1846 })
1847 .Case("$urandom",
1848 [&]() -> Value {
1849 return moore::UrandomBIOp::create(builder, loc, value);
1850 })
1851 .Case("$random",
1852 [&]() -> Value {
1853 return moore::RandomBIOp::create(builder, loc, value);
1854 })
1855 .Case("$realtobits",
1856 [&]() -> Value {
1857 return moore::RealtobitsBIOp::create(builder, loc, value);
1858 })
1859 .Case("$bitstoreal",
1860 [&]() -> Value {
1861 return moore::BitstorealBIOp::create(builder, loc, value);
1862 })
1863 .Case("$shortrealtobits",
1864 [&]() -> Value {
1865 return moore::ShortrealtobitsBIOp::create(builder, loc,
1866 value);
1867 })
1868 .Case("$bitstoshortreal",
1869 [&]() -> Value {
1870 return moore::BitstoshortrealBIOp::create(builder, loc,
1871 value);
1872 })
1873 .Default([&]() -> Value { return {}; });
1874 return systemCallRes();
1875}
assert(baseType &&"element must be base type")
static Value materializeSBVToPackedConversion(Context &context, moore::PackedType packedType, Value value, Location loc)
Create the necessary operations to convert from a simple bit vector IntType to an equivalent PackedTy...
static uint64_t getTimeScaleInFemtoseconds(Context &context)
Get the currently active timescale as an integer number of femtoseconds.
static Value materializePackedToSBVConversion(Context &context, Value value, Location loc)
Create the necessary operations to convert from a PackedType to the corresponding simple bit vector I...
static Value getSelectIndex(Context &context, Location loc, Value index, const slang::ConstantRange &range)
Map an index into an array, with bounds range, to a bit offset of the underlying bit storage.
static FVInt convertSVIntToFVInt(const slang::SVInt &svint)
Convert a Slang SVInt to a CIRCT FVInt.
Four-valued arbitrary precision integers.
Definition FVInt.h:37
A packed SystemVerilog type.
Definition MooreTypes.h:151
bool containsTimeType() const
Check if this is a TimeType, or an aggregate that contains a nested TimeType.
IntType getSimpleBitVector() const
Get the simple bit vector type equivalent to this packed type.
void info(Twine message)
Definition LSPUtils.cpp:20
Domain
The number of values each bit of a type can assume.
Definition MooreTypes.h:48
@ FourValued
Four-valued types such as logic or integer.
@ TwoValued
Two-valued types such as bit or int.
bool isIntType(Type type, unsigned width)
Check if a type is an IntType type of the given width.
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.
Value materializeFixedSizeUnpackedArrayType(const slang::ConstantValue &constant, const slang::ast::FixedSizeUnpackedArrayType &astType, Location loc)
Helper function to materialize an unpacked array of SVInts as an SSA value.
Value convertAssertionCallExpression(const slang::ast::CallExpression &expr, const slang::ast::CallExpression::SystemCallInfo &info, Location loc)
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:178
Value materializeSVInt(const slang::SVInt &svint, const slang::ast::Type &type, Location loc)
Helper function to materialize an SVInt as an SSA value.
slang::TimeScale timeScale
The time scale currently in effect.
Value convertToBool(Value value)
Helper function to convert a value to its "truthy" boolean value.
FailureOr< Value > convertFormatString(std::span< const slang::ast::Expression *const > arguments, Location loc, moore::IntFormat defaultFormat=moore::IntFormat::Decimal, bool appendNewline=false)
Convert a list of string literal arguments with formatting specifiers and arguments to be interpolate...
Value convertRvalueExpression(const slang::ast::Expression &expr, Type requiredType={})
FailureOr< Value > convertSystemCallArity0(const slang::ast::SystemSubroutine &subroutine, Location loc)
Convert system function calls only have arity-0.
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.
Value convertAssertionExpression(const slang::ast::AssertionExpr &expr, Location loc)
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.