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 auto one = moore::ConstantOp::create(
467 builder, loc, cast<moore::IntType>(preValue.getType()), 1);
468 auto postValue =
469 isInc ? moore::AddOp::create(builder, loc, preValue, one).getResult()
470 : moore::SubOp::create(builder, loc, preValue, one).getResult();
471 moore::BlockingAssignOp::create(builder, loc, arg, postValue);
472 if (isPost)
473 return preValue;
474 return postValue;
475 }
476
477 // Handle unary operators.
478 Value visit(const slang::ast::UnaryExpression &expr) {
479 using slang::ast::UnaryOperator;
480 Value arg;
481 if (expr.op == UnaryOperator::Preincrement ||
482 expr.op == UnaryOperator::Predecrement ||
483 expr.op == UnaryOperator::Postincrement ||
484 expr.op == UnaryOperator::Postdecrement)
485 arg = context.convertLvalueExpression(expr.operand());
486 else
487 arg = context.convertRvalueExpression(expr.operand());
488 if (!arg)
489 return {};
490
491 switch (expr.op) {
492 // `+a` is simply `a`, but converted to a simple bit vector type since
493 // this is technically an arithmetic operation.
494 case UnaryOperator::Plus:
495 return context.convertToSimpleBitVector(arg);
496
497 case UnaryOperator::Minus:
498 arg = context.convertToSimpleBitVector(arg);
499 if (!arg)
500 return {};
501 return moore::NegOp::create(builder, loc, arg);
502
503 case UnaryOperator::BitwiseNot:
504 arg = context.convertToSimpleBitVector(arg);
505 if (!arg)
506 return {};
507 return moore::NotOp::create(builder, loc, arg);
508
509 case UnaryOperator::BitwiseAnd:
510 return createReduction<moore::ReduceAndOp>(arg, false);
511 case UnaryOperator::BitwiseOr:
512 return createReduction<moore::ReduceOrOp>(arg, false);
513 case UnaryOperator::BitwiseXor:
514 return createReduction<moore::ReduceXorOp>(arg, false);
515 case UnaryOperator::BitwiseNand:
516 return createReduction<moore::ReduceAndOp>(arg, true);
517 case UnaryOperator::BitwiseNor:
518 return createReduction<moore::ReduceOrOp>(arg, true);
519 case UnaryOperator::BitwiseXnor:
520 return createReduction<moore::ReduceXorOp>(arg, true);
521
522 case UnaryOperator::LogicalNot:
523 arg = context.convertToBool(arg);
524 if (!arg)
525 return {};
526 return moore::NotOp::create(builder, loc, arg);
527
528 case UnaryOperator::Preincrement:
529 return createIncrement(arg, true, false);
530 case UnaryOperator::Predecrement:
531 return createIncrement(arg, false, false);
532 case UnaryOperator::Postincrement:
533 return createIncrement(arg, true, true);
534 case UnaryOperator::Postdecrement:
535 return createIncrement(arg, false, true);
536 }
537
538 mlir::emitError(loc, "unsupported unary operator");
539 return {};
540 }
541
542 // Helper function to convert two arguments to a simple bit vector type and
543 // pass them into a binary op.
544 template <class ConcreteOp>
545 Value createBinary(Value lhs, Value rhs) {
546 lhs = context.convertToSimpleBitVector(lhs);
547 if (!lhs)
548 return {};
549 rhs = context.convertToSimpleBitVector(rhs);
550 if (!rhs)
551 return {};
552 return ConcreteOp::create(builder, loc, lhs, rhs);
553 }
554
555 // Handle binary operators.
556 Value visit(const slang::ast::BinaryExpression &expr) {
557 auto lhs = context.convertRvalueExpression(expr.left());
558 if (!lhs)
559 return {};
560 auto rhs = context.convertRvalueExpression(expr.right());
561 if (!rhs)
562 return {};
563
564 // Determine the domain of the result.
565 Domain domain = Domain::TwoValued;
566 if (expr.type->isFourState() || expr.left().type->isFourState() ||
567 expr.right().type->isFourState())
568 domain = Domain::FourValued;
569
570 using slang::ast::BinaryOperator;
571 switch (expr.op) {
572 case BinaryOperator::Add:
573 return createBinary<moore::AddOp>(lhs, rhs);
574 case BinaryOperator::Subtract:
575 return createBinary<moore::SubOp>(lhs, rhs);
576 case BinaryOperator::Multiply:
577 return createBinary<moore::MulOp>(lhs, rhs);
578 case BinaryOperator::Divide:
579 if (expr.type->isSigned())
580 return createBinary<moore::DivSOp>(lhs, rhs);
581 else
582 return createBinary<moore::DivUOp>(lhs, rhs);
583 case BinaryOperator::Mod:
584 if (expr.type->isSigned())
585 return createBinary<moore::ModSOp>(lhs, rhs);
586 else
587 return createBinary<moore::ModUOp>(lhs, rhs);
588 case BinaryOperator::Power: {
589 // Slang casts the LHS and result of the `**` operator to a four-valued
590 // type, since the operator can return X even for two-valued inputs. To
591 // maintain uniform types across operands and results, cast the RHS to
592 // that four-valued type as well.
593 auto rhsCast = context.materializeConversion(
594 lhs.getType(), rhs, expr.right().type->isSigned(), rhs.getLoc());
595 if (expr.type->isSigned())
596 return createBinary<moore::PowSOp>(lhs, rhsCast);
597 else
598 return createBinary<moore::PowUOp>(lhs, rhsCast);
599 }
600
601 case BinaryOperator::BinaryAnd:
602 return createBinary<moore::AndOp>(lhs, rhs);
603 case BinaryOperator::BinaryOr:
604 return createBinary<moore::OrOp>(lhs, rhs);
605 case BinaryOperator::BinaryXor:
606 return createBinary<moore::XorOp>(lhs, rhs);
607 case BinaryOperator::BinaryXnor: {
608 auto result = createBinary<moore::XorOp>(lhs, rhs);
609 if (!result)
610 return {};
611 return moore::NotOp::create(builder, loc, result);
612 }
613
614 case BinaryOperator::Equality:
615 if (isa<moore::UnpackedArrayType>(lhs.getType()))
616 return moore::UArrayCmpOp::create(
617 builder, loc, moore::UArrayCmpPredicate::eq, lhs, rhs);
618 else if (isa<moore::StringType>(lhs.getType()))
619 return moore::StringCmpOp::create(
620 builder, loc, moore::StringCmpPredicate::eq, lhs, rhs);
621 else
622 return createBinary<moore::EqOp>(lhs, rhs);
623 case BinaryOperator::Inequality:
624 if (isa<moore::UnpackedArrayType>(lhs.getType()))
625 return moore::UArrayCmpOp::create(
626 builder, loc, moore::UArrayCmpPredicate::ne, lhs, rhs);
627 else if (isa<moore::StringType>(lhs.getType()))
628 return moore::StringCmpOp::create(
629 builder, loc, moore::StringCmpPredicate::ne, lhs, rhs);
630 else
631 return createBinary<moore::NeOp>(lhs, rhs);
632 case BinaryOperator::CaseEquality:
633 return createBinary<moore::CaseEqOp>(lhs, rhs);
634 case BinaryOperator::CaseInequality:
635 return createBinary<moore::CaseNeOp>(lhs, rhs);
636 case BinaryOperator::WildcardEquality:
637 return createBinary<moore::WildcardEqOp>(lhs, rhs);
638 case BinaryOperator::WildcardInequality:
639 return createBinary<moore::WildcardNeOp>(lhs, rhs);
640
641 case BinaryOperator::GreaterThanEqual:
642 if (expr.left().type->isSigned())
643 return createBinary<moore::SgeOp>(lhs, rhs);
644 else if (isa<moore::StringType>(lhs.getType()))
645 return moore::StringCmpOp::create(
646 builder, loc, moore::StringCmpPredicate::ge, lhs, rhs);
647 else
648 return createBinary<moore::UgeOp>(lhs, rhs);
649 case BinaryOperator::GreaterThan:
650 if (expr.left().type->isSigned())
651 return createBinary<moore::SgtOp>(lhs, rhs);
652 else if (isa<moore::StringType>(lhs.getType()))
653 return moore::StringCmpOp::create(
654 builder, loc, moore::StringCmpPredicate::gt, lhs, rhs);
655 else
656 return createBinary<moore::UgtOp>(lhs, rhs);
657 case BinaryOperator::LessThanEqual:
658 if (expr.left().type->isSigned())
659 return createBinary<moore::SleOp>(lhs, rhs);
660 else if (isa<moore::StringType>(lhs.getType()))
661 return moore::StringCmpOp::create(
662 builder, loc, moore::StringCmpPredicate::le, lhs, rhs);
663 else
664 return createBinary<moore::UleOp>(lhs, rhs);
665 case BinaryOperator::LessThan:
666 if (expr.left().type->isSigned())
667 return createBinary<moore::SltOp>(lhs, rhs);
668 else if (isa<moore::StringType>(lhs.getType()))
669 return moore::StringCmpOp::create(
670 builder, loc, moore::StringCmpPredicate::lt, lhs, rhs);
671 else
672 return createBinary<moore::UltOp>(lhs, rhs);
673
674 // See IEEE 1800-2017 § 11.4.7 "Logical operators".
675 case BinaryOperator::LogicalAnd: {
676 // TODO: This should short-circuit. Put the RHS code into a separate
677 // block.
678 lhs = context.convertToBool(lhs, domain);
679 if (!lhs)
680 return {};
681 rhs = context.convertToBool(rhs, domain);
682 if (!rhs)
683 return {};
684 return moore::AndOp::create(builder, loc, lhs, rhs);
685 }
686 case BinaryOperator::LogicalOr: {
687 // TODO: This should short-circuit. Put the RHS code into a separate
688 // block.
689 lhs = context.convertToBool(lhs, domain);
690 if (!lhs)
691 return {};
692 rhs = context.convertToBool(rhs, domain);
693 if (!rhs)
694 return {};
695 return moore::OrOp::create(builder, loc, lhs, rhs);
696 }
697 case BinaryOperator::LogicalImplication: {
698 // `(lhs -> rhs)` equivalent to `(!lhs || rhs)`.
699 lhs = context.convertToBool(lhs, domain);
700 if (!lhs)
701 return {};
702 rhs = context.convertToBool(rhs, domain);
703 if (!rhs)
704 return {};
705 auto notLHS = moore::NotOp::create(builder, loc, lhs);
706 return moore::OrOp::create(builder, loc, notLHS, rhs);
707 }
708 case BinaryOperator::LogicalEquivalence: {
709 // `(lhs <-> rhs)` equivalent to `(lhs && rhs) || (!lhs && !rhs)`.
710 lhs = context.convertToBool(lhs, domain);
711 if (!lhs)
712 return {};
713 rhs = context.convertToBool(rhs, domain);
714 if (!rhs)
715 return {};
716 auto notLHS = moore::NotOp::create(builder, loc, lhs);
717 auto notRHS = moore::NotOp::create(builder, loc, rhs);
718 auto both = moore::AndOp::create(builder, loc, lhs, rhs);
719 auto notBoth = moore::AndOp::create(builder, loc, notLHS, notRHS);
720 return moore::OrOp::create(builder, loc, both, notBoth);
721 }
722
723 case BinaryOperator::LogicalShiftLeft:
724 return createBinary<moore::ShlOp>(lhs, rhs);
725 case BinaryOperator::LogicalShiftRight:
726 return createBinary<moore::ShrOp>(lhs, rhs);
727 case BinaryOperator::ArithmeticShiftLeft:
728 return createBinary<moore::ShlOp>(lhs, rhs);
729 case BinaryOperator::ArithmeticShiftRight: {
730 // The `>>>` operator is an arithmetic right shift if the LHS operand is
731 // signed, or a logical right shift if the operand is unsigned.
732 lhs = context.convertToSimpleBitVector(lhs);
733 rhs = context.convertToSimpleBitVector(rhs);
734 if (!lhs || !rhs)
735 return {};
736 if (expr.type->isSigned())
737 return moore::AShrOp::create(builder, loc, lhs, rhs);
738 return moore::ShrOp::create(builder, loc, lhs, rhs);
739 }
740 }
741
742 mlir::emitError(loc, "unsupported binary operator");
743 return {};
744 }
745
746 // Handle `'0`, `'1`, `'x`, and `'z` literals.
747 Value visit(const slang::ast::UnbasedUnsizedIntegerLiteral &expr) {
748 return context.materializeSVInt(expr.getValue(), *expr.type, loc);
749 }
750
751 // Handle integer literals.
752 Value visit(const slang::ast::IntegerLiteral &expr) {
753 return context.materializeSVInt(expr.getValue(), *expr.type, loc);
754 }
755
756 // Handle time literals.
757 Value visit(const slang::ast::TimeLiteral &expr) {
758 // The time literal is expressed in the current time scale. Determine the
759 // conversion factor to convert the literal from the current time scale into
760 // femtoseconds, and round the scaled value to femtoseconds.
761 double scale = getTimeScaleInFemtoseconds(context);
762 double value = std::round(expr.getValue() * scale);
763 assert(value >= 0.0);
764
765 // Check that the value does not exceed what we can represent in the IR.
766 // Casting the maximum uint64 value to double changes its value from
767 // 18446744073709551615 to 18446744073709551616, which makes the comparison
768 // overestimate the largest number we can represent. To avoid this, round
769 // the maximum value down to the closest number that only has the front 53
770 // bits set. This matches the mantissa of a double, plus the implicit
771 // leading 1, ensuring that we can accurately represent the limit.
772 static constexpr uint64_t limit =
773 (std::numeric_limits<uint64_t>::max() >> 11) << 11;
774 if (value > limit) {
775 mlir::emitError(loc) << "time value is larger than " << limit << " fs";
776 return {};
777 }
778
779 return moore::ConstantTimeOp::create(builder, loc,
780 static_cast<uint64_t>(value));
781 }
782
783 // Handle replications.
784 Value visit(const slang::ast::ReplicationExpression &expr) {
785 auto type = context.convertType(*expr.type);
786 auto value = context.convertRvalueExpression(expr.concat());
787 if (!value)
788 return {};
789 return moore::ReplicateOp::create(builder, loc, type, value);
790 }
791
792 // Handle set membership operator.
793 Value visit(const slang::ast::InsideExpression &expr) {
794 auto lhs = context.convertToSimpleBitVector(
795 context.convertRvalueExpression(expr.left()));
796 if (!lhs)
797 return {};
798 // All conditions for determining whether it is inside.
799 SmallVector<Value> conditions;
800
801 // Traverse open range list.
802 for (const auto *listExpr : expr.rangeList()) {
803 Value cond;
804 // The open range list on the right-hand side of the inside operator is a
805 // comma-separated list of expressions or ranges.
806 if (const auto *openRange =
807 listExpr->as_if<slang::ast::ValueRangeExpression>()) {
808 // Handle ranges.
809 auto lowBound = context.convertToSimpleBitVector(
810 context.convertRvalueExpression(openRange->left()));
811 auto highBound = context.convertToSimpleBitVector(
812 context.convertRvalueExpression(openRange->right()));
813 if (!lowBound || !highBound)
814 return {};
815 Value leftValue, rightValue;
816 // Determine if the expression on the left-hand side is inclusively
817 // within the range.
818 if (openRange->left().type->isSigned() ||
819 expr.left().type->isSigned()) {
820 leftValue = moore::SgeOp::create(builder, loc, lhs, lowBound);
821 } else {
822 leftValue = moore::UgeOp::create(builder, loc, lhs, lowBound);
823 }
824 if (openRange->right().type->isSigned() ||
825 expr.left().type->isSigned()) {
826 rightValue = moore::SleOp::create(builder, loc, lhs, highBound);
827 } else {
828 rightValue = moore::UleOp::create(builder, loc, lhs, highBound);
829 }
830 cond = moore::AndOp::create(builder, loc, leftValue, rightValue);
831 } else {
832 // Handle expressions.
833 if (!listExpr->type->isIntegral()) {
834 if (listExpr->type->isUnpackedArray()) {
835 mlir::emitError(
836 loc, "unpacked arrays in 'inside' expressions not supported");
837 return {};
838 }
839 mlir::emitError(
840 loc, "only simple bit vectors supported in 'inside' expressions");
841 return {};
842 }
843
844 auto value = context.convertToSimpleBitVector(
845 context.convertRvalueExpression(*listExpr));
846 if (!value)
847 return {};
848 cond = moore::WildcardEqOp::create(builder, loc, lhs, value);
849 }
850 conditions.push_back(cond);
851 }
852
853 // Calculate the final result by `or` op.
854 auto result = conditions.back();
855 conditions.pop_back();
856 while (!conditions.empty()) {
857 result = moore::OrOp::create(builder, loc, conditions.back(), result);
858 conditions.pop_back();
859 }
860 return result;
861 }
862
863 // Handle conditional operator `?:`.
864 Value visit(const slang::ast::ConditionalExpression &expr) {
865 auto type = context.convertType(*expr.type);
866
867 // Handle condition.
868 if (expr.conditions.size() > 1) {
869 mlir::emitError(loc)
870 << "unsupported conditional expression with more than one condition";
871 return {};
872 }
873 const auto &cond = expr.conditions[0];
874 if (cond.pattern) {
875 mlir::emitError(loc) << "unsupported conditional expression with pattern";
876 return {};
877 }
878 auto value =
879 context.convertToBool(context.convertRvalueExpression(*cond.expr));
880 if (!value)
881 return {};
882 auto conditionalOp =
883 moore::ConditionalOp::create(builder, loc, type, value);
884
885 // Create blocks for true region and false region.
886 auto &trueBlock = conditionalOp.getTrueRegion().emplaceBlock();
887 auto &falseBlock = conditionalOp.getFalseRegion().emplaceBlock();
888
889 OpBuilder::InsertionGuard g(builder);
890
891 // Handle left expression.
892 builder.setInsertionPointToStart(&trueBlock);
893 auto trueValue = context.convertRvalueExpression(expr.left(), type);
894 if (!trueValue)
895 return {};
896 moore::YieldOp::create(builder, loc, trueValue);
897
898 // Handle right expression.
899 builder.setInsertionPointToStart(&falseBlock);
900 auto falseValue = context.convertRvalueExpression(expr.right(), type);
901 if (!falseValue)
902 return {};
903 moore::YieldOp::create(builder, loc, falseValue);
904
905 return conditionalOp.getResult();
906 }
907
908 /// Handle calls.
909 Value visit(const slang::ast::CallExpression &expr) {
910 // Class method calls are currently not supported.
911 if (expr.thisClass()) {
912 mlir::emitError(loc, "unsupported class method call");
913 return {};
914 }
915
916 // Try to materialize constant values directly.
917 auto constant = context.evaluateConstant(expr);
918 if (auto value = context.materializeConstant(constant, *expr.type, loc))
919 return value;
920
921 return std::visit(
922 [&](auto &subroutine) { return visitCall(expr, subroutine); },
923 expr.subroutine);
924 }
925
926 /// Handle subroutine calls.
927 Value visitCall(const slang::ast::CallExpression &expr,
928 const slang::ast::SubroutineSymbol *subroutine) {
929 auto *lowering = context.declareFunction(*subroutine);
930 if (!lowering)
931 return {};
932
933 // Convert the call arguments. Input arguments are converted to an rvalue.
934 // All other arguments are converted to lvalues and passed into the function
935 // by reference.
936 SmallVector<Value> arguments;
937 for (auto [callArg, declArg] :
938 llvm::zip(expr.arguments(), subroutine->getArguments())) {
939
940 // Unpack the `<expr> = EmptyArgument` pattern emitted by Slang for output
941 // and inout arguments.
942 auto *expr = callArg;
943 if (const auto *assign = expr->as_if<slang::ast::AssignmentExpression>())
944 expr = &assign->left();
945
946 Value value;
947 if (declArg->direction == slang::ast::ArgumentDirection::In)
948 value = context.convertRvalueExpression(*expr);
949 else
950 value = context.convertLvalueExpression(*expr);
951 if (!value)
952 return {};
953 arguments.push_back(value);
954 }
955
956 // Create the call.
957 auto callOp =
958 mlir::func::CallOp::create(builder, loc, lowering->op, arguments);
959
960 // For calls to void functions we need to have a value to return from this
961 // function. Create a dummy `unrealized_conversion_cast`, which will get
962 // deleted again later on.
963 if (callOp.getNumResults() == 0)
964 return mlir::UnrealizedConversionCastOp::create(
965 builder, loc, moore::VoidType::get(context.getContext()),
966 ValueRange{})
967 .getResult(0);
968
969 return callOp.getResult(0);
970 }
971
972 /// Handle system calls.
973 Value visitCall(const slang::ast::CallExpression &expr,
974 const slang::ast::CallExpression::SystemCallInfo &info) {
975 const auto &subroutine = *info.subroutine;
976 auto args = expr.arguments();
977
978 if (args.size() == 1) {
979 auto value = context.convertRvalueExpression(*args[0]);
980 if (!value)
981 return {};
982 auto result = context.convertSystemCallArity1(subroutine, loc, value);
983 if (failed(result))
984 return {};
985 if (*result)
986 return *result;
987 }
988
989 mlir::emitError(loc) << "unsupported system call `" << subroutine.name
990 << "`";
991 return {};
992 }
993
994 /// Handle string literals.
995 Value visit(const slang::ast::StringLiteral &expr) {
996 auto type = context.convertType(*expr.type);
997 return moore::StringConstantOp::create(builder, loc, type, expr.getValue());
998 }
999
1000 /// Handle real literals.
1001 Value visit(const slang::ast::RealLiteral &expr) {
1002 return moore::RealLiteralOp::create(
1003 builder, loc, builder.getF64FloatAttr(expr.getValue()));
1004 }
1005
1006 /// Handle assignment patterns.
1007 Value visitAssignmentPattern(
1008 const slang::ast::AssignmentPatternExpressionBase &expr,
1009 unsigned replCount = 1) {
1010 auto type = context.convertType(*expr.type);
1011
1012 // Convert the individual elements first.
1013 auto elementCount = expr.elements().size();
1014 SmallVector<Value> elements;
1015 elements.reserve(replCount * elementCount);
1016 for (auto elementExpr : expr.elements()) {
1017 auto value = context.convertRvalueExpression(*elementExpr);
1018 if (!value)
1019 return {};
1020 elements.push_back(value);
1021 }
1022 for (unsigned replIdx = 1; replIdx < replCount; ++replIdx)
1023 for (unsigned elementIdx = 0; elementIdx < elementCount; ++elementIdx)
1024 elements.push_back(elements[elementIdx]);
1025
1026 // Handle integers.
1027 if (auto intType = dyn_cast<moore::IntType>(type)) {
1028 assert(intType.getWidth() == elements.size());
1029 std::reverse(elements.begin(), elements.end());
1030 return moore::ConcatOp::create(builder, loc, intType, elements);
1031 }
1032
1033 // Handle packed structs.
1034 if (auto structType = dyn_cast<moore::StructType>(type)) {
1035 assert(structType.getMembers().size() == elements.size());
1036 return moore::StructCreateOp::create(builder, loc, structType, elements);
1037 }
1038
1039 // Handle unpacked structs.
1040 if (auto structType = dyn_cast<moore::UnpackedStructType>(type)) {
1041 assert(structType.getMembers().size() == elements.size());
1042 return moore::StructCreateOp::create(builder, loc, structType, elements);
1043 }
1044
1045 // Handle packed arrays.
1046 if (auto arrayType = dyn_cast<moore::ArrayType>(type)) {
1047 assert(arrayType.getSize() == elements.size());
1048 return moore::ArrayCreateOp::create(builder, loc, arrayType, elements);
1049 }
1050
1051 // Handle unpacked arrays.
1052 if (auto arrayType = dyn_cast<moore::UnpackedArrayType>(type)) {
1053 assert(arrayType.getSize() == elements.size());
1054 return moore::ArrayCreateOp::create(builder, loc, arrayType, elements);
1055 }
1056
1057 mlir::emitError(loc) << "unsupported assignment pattern with type " << type;
1058 return {};
1059 }
1060
1061 Value visit(const slang::ast::SimpleAssignmentPatternExpression &expr) {
1062 return visitAssignmentPattern(expr);
1063 }
1064
1065 Value visit(const slang::ast::StructuredAssignmentPatternExpression &expr) {
1066 return visitAssignmentPattern(expr);
1067 }
1068
1069 Value visit(const slang::ast::ReplicatedAssignmentPatternExpression &expr) {
1070 auto count =
1071 context.evaluateConstant(expr.count()).integer().as<unsigned>();
1072 assert(count && "Slang guarantees constant non-zero replication count");
1073 return visitAssignmentPattern(expr, *count);
1074 }
1075
1076 Value visit(const slang::ast::StreamingConcatenationExpression &expr) {
1077 SmallVector<Value> operands;
1078 for (auto stream : expr.streams()) {
1079 auto operandLoc = context.convertLocation(stream.operand->sourceRange);
1080 if (!stream.constantWithWidth.has_value() && stream.withExpr) {
1081 mlir::emitError(operandLoc)
1082 << "Moore only support streaming "
1083 "concatenation with fixed size 'with expression'";
1084 return {};
1085 }
1086 Value value;
1087 if (stream.constantWithWidth.has_value()) {
1088 value = context.convertRvalueExpression(*stream.withExpr);
1089 auto type = cast<moore::UnpackedType>(value.getType());
1090 auto intType = moore::IntType::get(
1091 context.getContext(), type.getBitSize().value(), type.getDomain());
1092 // Do not care if it's signed, because we will not do expansion.
1093 value = context.materializeConversion(intType, value, false, loc);
1094 } else {
1095 value = context.convertRvalueExpression(*stream.operand);
1096 }
1097
1098 value = context.convertToSimpleBitVector(value);
1099 if (!value)
1100 return {};
1101 operands.push_back(value);
1102 }
1103 Value value;
1104
1105 if (operands.size() == 1) {
1106 // There must be at least one element, otherwise slang will report an
1107 // error.
1108 value = operands.front();
1109 } else {
1110 value = moore::ConcatOp::create(builder, loc, operands).getResult();
1111 }
1112
1113 if (expr.getSliceSize() == 0) {
1114 return value;
1115 }
1116
1117 auto type = cast<moore::IntType>(value.getType());
1118 SmallVector<Value> slicedOperands;
1119 auto iterMax = type.getWidth() / expr.getSliceSize();
1120 auto remainSize = type.getWidth() % expr.getSliceSize();
1121
1122 for (size_t i = 0; i < iterMax; i++) {
1123 auto extractResultType = moore::IntType::get(
1124 context.getContext(), expr.getSliceSize(), type.getDomain());
1125
1126 auto extracted = moore::ExtractOp::create(builder, loc, extractResultType,
1127 value, i * expr.getSliceSize());
1128 slicedOperands.push_back(extracted);
1129 }
1130 // Handle other wire
1131 if (remainSize) {
1132 auto extractResultType = moore::IntType::get(
1133 context.getContext(), remainSize, type.getDomain());
1134
1135 auto extracted =
1136 moore::ExtractOp::create(builder, loc, extractResultType, value,
1137 iterMax * expr.getSliceSize());
1138 slicedOperands.push_back(extracted);
1139 }
1140
1141 return moore::ConcatOp::create(builder, loc, slicedOperands);
1142 }
1143
1144 Value visit(const slang::ast::AssertionInstanceExpression &expr) {
1145 return context.convertAssertionExpression(expr.body, loc);
1146 }
1147
1148 /// Emit an error for all other expressions.
1149 template <typename T>
1150 Value visit(T &&node) {
1151 mlir::emitError(loc, "unsupported expression: ")
1152 << slang::ast::toString(node.kind);
1153 return {};
1154 }
1155
1156 Value visitInvalid(const slang::ast::Expression &expr) {
1157 mlir::emitError(loc, "invalid expression");
1158 return {};
1159 }
1160};
1161} // namespace
1162
1163//===----------------------------------------------------------------------===//
1164// Lvalue Conversion
1165//===----------------------------------------------------------------------===//
1166
1167namespace {
1168struct LvalueExprVisitor : public ExprVisitor {
1169 LvalueExprVisitor(Context &context, Location loc)
1170 : ExprVisitor(context, loc, /*isLvalue=*/true) {}
1171 using ExprVisitor::visit;
1172
1173 // Handle named values, such as references to declared variables.
1174 Value visit(const slang::ast::NamedValueExpression &expr) {
1175 if (auto value = context.valueSymbols.lookup(&expr.symbol))
1176 return value;
1177 auto d = mlir::emitError(loc, "unknown name `") << expr.symbol.name << "`";
1178 d.attachNote(context.convertLocation(expr.symbol.location))
1179 << "no lvalue generated for " << slang::ast::toString(expr.symbol.kind);
1180 return {};
1181 }
1182
1183 // Handle hierarchical values, such as `Top.sub.var = x`.
1184 Value visit(const slang::ast::HierarchicalValueExpression &expr) {
1185 if (auto value = context.valueSymbols.lookup(&expr.symbol))
1186 return value;
1187
1188 // Emit an error for those hierarchical values not recorded in the
1189 // `valueSymbols`.
1190 auto d = mlir::emitError(loc, "unknown hierarchical name `")
1191 << expr.symbol.name << "`";
1192 d.attachNote(context.convertLocation(expr.symbol.location))
1193 << "no lvalue generated for " << slang::ast::toString(expr.symbol.kind);
1194 return {};
1195 }
1196
1197 Value visit(const slang::ast::StreamingConcatenationExpression &expr) {
1198 SmallVector<Value> operands;
1199 for (auto stream : expr.streams()) {
1200 auto operandLoc = context.convertLocation(stream.operand->sourceRange);
1201 if (!stream.constantWithWidth.has_value() && stream.withExpr) {
1202 mlir::emitError(operandLoc)
1203 << "Moore only support streaming "
1204 "concatenation with fixed size 'with expression'";
1205 return {};
1206 }
1207 Value value;
1208 if (stream.constantWithWidth.has_value()) {
1209 value = context.convertLvalueExpression(*stream.withExpr);
1210 auto type = cast<moore::UnpackedType>(
1211 cast<moore::RefType>(value.getType()).getNestedType());
1212 auto intType = moore::RefType::get(moore::IntType::get(
1213 context.getContext(), type.getBitSize().value(), type.getDomain()));
1214 // Do not care if it's signed, because we will not do expansion.
1215 value = context.materializeConversion(intType, value, false, loc);
1216 } else {
1217 value = context.convertLvalueExpression(*stream.operand);
1218 }
1219
1220 if (!value)
1221 return {};
1222 operands.push_back(value);
1223 }
1224 Value value;
1225 if (operands.size() == 1) {
1226 // There must be at least one element, otherwise slang will report an
1227 // error.
1228 value = operands.front();
1229 } else {
1230 value = moore::ConcatRefOp::create(builder, loc, operands).getResult();
1231 }
1232
1233 if (expr.getSliceSize() == 0) {
1234 return value;
1235 }
1236
1237 auto type = cast<moore::IntType>(
1238 cast<moore::RefType>(value.getType()).getNestedType());
1239 SmallVector<Value> slicedOperands;
1240 auto widthSum = type.getWidth();
1241 auto domain = type.getDomain();
1242 auto iterMax = widthSum / expr.getSliceSize();
1243 auto remainSize = widthSum % expr.getSliceSize();
1244
1245 for (size_t i = 0; i < iterMax; i++) {
1246 auto extractResultType = moore::RefType::get(moore::IntType::get(
1247 context.getContext(), expr.getSliceSize(), domain));
1248
1249 auto extracted = moore::ExtractRefOp::create(
1250 builder, loc, extractResultType, value, i * expr.getSliceSize());
1251 slicedOperands.push_back(extracted);
1252 }
1253 // Handle other wire
1254 if (remainSize) {
1255 auto extractResultType = moore::RefType::get(
1256 moore::IntType::get(context.getContext(), remainSize, domain));
1257
1258 auto extracted =
1259 moore::ExtractRefOp::create(builder, loc, extractResultType, value,
1260 iterMax * expr.getSliceSize());
1261 slicedOperands.push_back(extracted);
1262 }
1263
1264 return moore::ConcatRefOp::create(builder, loc, slicedOperands);
1265 }
1266
1267 /// Emit an error for all other expressions.
1268 template <typename T>
1269 Value visit(T &&node) {
1270 return context.convertRvalueExpression(node);
1271 }
1272
1273 Value visitInvalid(const slang::ast::Expression &expr) {
1274 mlir::emitError(loc, "invalid expression");
1275 return {};
1276 }
1277};
1278} // namespace
1279
1280//===----------------------------------------------------------------------===//
1281// Entry Points
1282//===----------------------------------------------------------------------===//
1283
1284Value Context::convertRvalueExpression(const slang::ast::Expression &expr,
1285 Type requiredType) {
1286 auto loc = convertLocation(expr.sourceRange);
1287 auto value = expr.visit(RvalueExprVisitor(*this, loc));
1288 if (value && requiredType)
1289 value =
1290 materializeConversion(requiredType, value, expr.type->isSigned(), loc);
1291 return value;
1292}
1293
1294Value Context::convertLvalueExpression(const slang::ast::Expression &expr) {
1295 auto loc = convertLocation(expr.sourceRange);
1296 return expr.visit(LvalueExprVisitor(*this, loc));
1297}
1298// NOLINTEND(misc-no-recursion)
1299
1300/// Helper function to convert a value to its "truthy" boolean value.
1301Value Context::convertToBool(Value value) {
1302 if (!value)
1303 return {};
1304 if (auto type = dyn_cast_or_null<moore::IntType>(value.getType()))
1305 if (type.getBitSize() == 1)
1306 return value;
1307 if (auto type = dyn_cast_or_null<moore::UnpackedType>(value.getType()))
1308 return moore::BoolCastOp::create(builder, value.getLoc(), value);
1309 mlir::emitError(value.getLoc(), "expression of type ")
1310 << value.getType() << " cannot be cast to a boolean";
1311 return {};
1312}
1313
1314/// Materialize a Slang integer literal as a constant op.
1315Value Context::materializeSVInt(const slang::SVInt &svint,
1316 const slang::ast::Type &astType, Location loc) {
1317 auto type = convertType(astType);
1318 if (!type)
1319 return {};
1320
1321 bool typeIsFourValued = false;
1322 if (auto unpackedType = dyn_cast<moore::UnpackedType>(type))
1323 typeIsFourValued = unpackedType.getDomain() == moore::Domain::FourValued;
1324
1325 auto fvint = convertSVIntToFVInt(svint);
1326 auto intType = moore::IntType::get(getContext(), fvint.getBitWidth(),
1327 fvint.hasUnknown() || typeIsFourValued
1330 auto result = moore::ConstantOp::create(builder, loc, intType, fvint);
1331 return materializeConversion(type, result, astType.isSigned(), loc);
1332}
1333
1334Value Context::materializeConstant(const slang::ConstantValue &constant,
1335 const slang::ast::Type &type, Location loc) {
1336 if (constant.isInteger())
1337 return materializeSVInt(constant.integer(), type, loc);
1338 return {};
1339}
1340
1341slang::ConstantValue
1342Context::evaluateConstant(const slang::ast::Expression &expr) {
1343 using slang::ast::EvalFlags;
1344 slang::ast::EvalContext evalContext(
1345 slang::ast::ASTContext(compilation.getRoot(),
1346 slang::ast::LookupLocation::max),
1347 EvalFlags::CacheResults | EvalFlags::SpecparamsAllowed);
1348 return expr.eval(evalContext);
1349}
1350
1351/// Helper function to convert a value to its "truthy" boolean value and
1352/// convert it to the given domain.
1353Value Context::convertToBool(Value value, Domain domain) {
1354 value = convertToBool(value);
1355 if (!value)
1356 return {};
1357 auto type = moore::IntType::get(getContext(), 1, domain);
1358 return materializeConversion(type, value, false, value.getLoc());
1359}
1360
1362 if (!value)
1363 return {};
1364 if (isa<moore::IntType>(value.getType()))
1365 return value;
1366
1367 // Some operations in Slang's AST, for example bitwise or `|`, don't cast
1368 // packed struct/array operands to simple bit vectors but directly operate
1369 // on the struct/array. Since the corresponding IR ops operate only on
1370 // simple bit vectors, insert a conversion in this case.
1371 if (auto packed = dyn_cast<moore::PackedType>(value.getType()))
1372 if (auto sbvType = packed.getSimpleBitVector())
1373 return materializeConversion(sbvType, value, false, value.getLoc());
1374
1375 mlir::emitError(value.getLoc()) << "expression of type " << value.getType()
1376 << " cannot be cast to a simple bit vector";
1377 return {};
1378}
1379
1380/// Create the necessary operations to convert from a `PackedType` to the
1381/// corresponding simple bit vector `IntType`. This will apply special handling
1382/// to time values, which requires scaling by the local timescale.
1383static Value materializePackedToSBVConversion(Context &context, Value value,
1384 Location loc) {
1385 if (isa<moore::IntType>(value.getType()))
1386 return value;
1387
1388 auto &builder = context.builder;
1389 auto packedType = cast<moore::PackedType>(value.getType());
1390 auto intType = packedType.getSimpleBitVector();
1391 assert(intType);
1392
1393 // If we are converting from a time to an integer, divide the integer by the
1394 // timescale.
1395 if (isa<moore::TimeType>(packedType) &&
1397 value = builder.createOrFold<moore::TimeToLogicOp>(loc, value);
1398 auto scale = moore::ConstantOp::create(builder, loc, intType,
1400 return builder.createOrFold<moore::DivUOp>(loc, value, scale);
1401 }
1402
1403 // If this is an aggregate type, make sure that it does not contain any
1404 // `TimeType` fields. These require special conversion to ensure that the
1405 // local timescale is in effect.
1406 if (packedType.containsTimeType()) {
1407 mlir::emitError(loc) << "unsupported conversion: " << packedType
1408 << " cannot be converted to " << intType
1409 << "; contains a time type";
1410 return {};
1411 }
1412
1413 // Otherwise create a simple `PackedToSBVOp` for the conversion.
1414 return builder.createOrFold<moore::PackedToSBVOp>(loc, value);
1415}
1416
1417/// Create the necessary operations to convert from a simple bit vector
1418/// `IntType` to an equivalent `PackedType`. This will apply special handling to
1419/// time values, which requires scaling by the local timescale.
1421 moore::PackedType packedType,
1422 Value value, Location loc) {
1423 if (value.getType() == packedType)
1424 return value;
1425
1426 auto &builder = context.builder;
1427 auto intType = cast<moore::IntType>(value.getType());
1428 assert(intType && intType == packedType.getSimpleBitVector());
1429
1430 // If we are converting from an integer to a time, multiply the integer by the
1431 // timescale.
1432 if (isa<moore::TimeType>(packedType) &&
1434 auto scale = moore::ConstantOp::create(builder, loc, intType,
1436 value = builder.createOrFold<moore::MulOp>(loc, value, scale);
1437 return builder.createOrFold<moore::LogicToTimeOp>(loc, value);
1438 }
1439
1440 // If this is an aggregate type, make sure that it does not contain any
1441 // `TimeType` fields. These require special conversion to ensure that the
1442 // local timescale is in effect.
1443 if (packedType.containsTimeType()) {
1444 mlir::emitError(loc) << "unsupported conversion: " << intType
1445 << " cannot be converted to " << packedType
1446 << "; contains a time type";
1447 return {};
1448 }
1449
1450 // Otherwise create a simple `PackedToSBVOp` for the conversion.
1451 return builder.createOrFold<moore::SBVToPackedOp>(loc, packedType, value);
1452}
1453
1454Value Context::materializeConversion(Type type, Value value, bool isSigned,
1455 Location loc) {
1456 // Nothing to do if the types are already equal.
1457 if (type == value.getType())
1458 return value;
1459
1460 // Handle packed types which can be converted to a simple bit vector. This
1461 // allows us to perform resizing and domain casting on that bit vector.
1462 auto dstPacked = dyn_cast<moore::PackedType>(type);
1463 auto srcPacked = dyn_cast<moore::PackedType>(value.getType());
1464 auto dstInt = dstPacked ? dstPacked.getSimpleBitVector() : moore::IntType();
1465 auto srcInt = srcPacked ? srcPacked.getSimpleBitVector() : moore::IntType();
1466
1467 if (dstInt && srcInt) {
1468 // Convert the value to a simple bit vector if it isn't one already.
1469 value = materializePackedToSBVConversion(*this, value, loc);
1470 if (!value)
1471 return {};
1472
1473 // Create truncation or sign/zero extension ops depending on the source and
1474 // destination width.
1475 auto resizedType = moore::IntType::get(
1476 value.getContext(), dstInt.getWidth(), srcPacked.getDomain());
1477 if (dstInt.getWidth() < srcInt.getWidth()) {
1478 value = builder.createOrFold<moore::TruncOp>(loc, resizedType, value);
1479 } else if (dstInt.getWidth() > srcInt.getWidth()) {
1480 if (isSigned)
1481 value = builder.createOrFold<moore::SExtOp>(loc, resizedType, value);
1482 else
1483 value = builder.createOrFold<moore::ZExtOp>(loc, resizedType, value);
1484 }
1485
1486 // Convert the domain if needed.
1487 if (dstInt.getDomain() != srcInt.getDomain()) {
1488 if (dstInt.getDomain() == moore::Domain::TwoValued)
1489 value = builder.createOrFold<moore::LogicToIntOp>(loc, value);
1490 else if (dstInt.getDomain() == moore::Domain::FourValued)
1491 value = builder.createOrFold<moore::IntToLogicOp>(loc, value);
1492 }
1493
1494 // Convert the value from a simple bit vector back to the packed type.
1495 value = materializeSBVToPackedConversion(*this, dstPacked, value, loc);
1496 if (!value)
1497 return {};
1498
1499 assert(value.getType() == type);
1500 return value;
1501 }
1502
1503 // TODO: Handle other conversions with dedicated ops.
1504 if (value.getType() != type)
1505 value = moore::ConversionOp::create(builder, loc, type, value);
1506 return value;
1507}
1508
1509FailureOr<Value>
1510Context::convertSystemCallArity1(const slang::ast::SystemSubroutine &subroutine,
1511 Location loc, Value value) {
1512 auto systemCallRes =
1513 llvm::StringSwitch<std::function<FailureOr<Value>()>>(subroutine.name)
1514 // Signed and unsigned system functions.
1515 .Case("$signed", [&]() { return value; })
1516 .Case("$unsigned", [&]() { return value; })
1517
1518 // Math functions in SystemVerilog.
1519 .Case("$clog2",
1520 [&]() -> FailureOr<Value> {
1521 value = convertToSimpleBitVector(value);
1522 if (!value)
1523 return failure();
1524 return (Value)moore::Clog2BIOp::create(builder, loc, value);
1525 })
1526 .Case("$ln",
1527 [&]() -> Value {
1528 return moore::LnBIOp::create(builder, loc, value);
1529 })
1530 .Case("$log10",
1531 [&]() -> Value {
1532 return moore::Log10BIOp::create(builder, loc, value);
1533 })
1534 .Case("$sin",
1535 [&]() -> Value {
1536 return moore::SinBIOp::create(builder, loc, value);
1537 })
1538 .Case("$cos",
1539 [&]() -> Value {
1540 return moore::CosBIOp::create(builder, loc, value);
1541 })
1542 .Case("$tan",
1543 [&]() -> Value {
1544 return moore::TanBIOp::create(builder, loc, value);
1545 })
1546 .Case("$exp",
1547 [&]() -> Value {
1548 return moore::ExpBIOp::create(builder, loc, value);
1549 })
1550 .Case("$sqrt",
1551 [&]() -> Value {
1552 return moore::SqrtBIOp::create(builder, loc, value);
1553 })
1554 .Case("$floor",
1555 [&]() -> Value {
1556 return moore::FloorBIOp::create(builder, loc, value);
1557 })
1558 .Case("$ceil",
1559 [&]() -> Value {
1560 return moore::CeilBIOp::create(builder, loc, value);
1561 })
1562 .Case("$asin",
1563 [&]() -> Value {
1564 return moore::AsinBIOp::create(builder, loc, value);
1565 })
1566 .Case("$acos",
1567 [&]() -> Value {
1568 return moore::AcosBIOp::create(builder, loc, value);
1569 })
1570 .Case("$atan",
1571 [&]() -> Value {
1572 return moore::AtanBIOp::create(builder, loc, value);
1573 })
1574 .Case("$sinh",
1575 [&]() -> Value {
1576 return moore::SinhBIOp::create(builder, loc, value);
1577 })
1578 .Case("$cosh",
1579 [&]() -> Value {
1580 return moore::CoshBIOp::create(builder, loc, value);
1581 })
1582 .Case("$tanh",
1583 [&]() -> Value {
1584 return moore::TanhBIOp::create(builder, loc, value);
1585 })
1586 .Case("$asinh",
1587 [&]() -> Value {
1588 return moore::AsinhBIOp::create(builder, loc, value);
1589 })
1590 .Case("$acosh",
1591 [&]() -> Value {
1592 return moore::AcoshBIOp::create(builder, loc, value);
1593 })
1594 .Case("$atanh",
1595 [&]() -> Value {
1596 return moore::AtanhBIOp::create(builder, loc, value);
1597 })
1598 .Default([&]() -> Value { return {}; });
1599 return systemCallRes();
1600}
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:141
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.
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:176
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.
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.
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.