Loading [MathJax]/extensions/tex2jax.js
CIRCT 22.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Expressions.cpp
Go to the documentation of this file.
1//===- Expressions.cpp - Slang expression conversion ----------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "slang/ast/SystemSubroutine.h"
11#include "slang/syntax/AllSyntax.h"
12
13using namespace circt;
14using namespace ImportVerilog;
15using moore::Domain;
16
17/// Convert a Slang `SVInt` to a CIRCT `FVInt`.
18static FVInt convertSVIntToFVInt(const slang::SVInt &svint) {
19 if (svint.hasUnknown()) {
20 unsigned numWords = svint.getNumWords() / 2;
21 auto value = ArrayRef<uint64_t>(svint.getRawPtr(), numWords);
22 auto unknown = ArrayRef<uint64_t>(svint.getRawPtr() + numWords, numWords);
23 return FVInt(APInt(svint.getBitWidth(), value),
24 APInt(svint.getBitWidth(), unknown));
25 }
26 auto value = ArrayRef<uint64_t>(svint.getRawPtr(), svint.getNumWords());
27 return FVInt(APInt(svint.getBitWidth(), value));
28}
29
30/// Map an index into an array, with bounds `range`, to a bit offset of the
31/// underlying bit storage. This is a dynamic version of
32/// `slang::ConstantRange::translateIndex`.
33static Value getSelectIndex(OpBuilder &builder, Location loc, Value index,
34 const slang::ConstantRange &range) {
35 auto indexType = cast<moore::UnpackedType>(index.getType());
36 auto bw = std::max(llvm::Log2_32_Ceil(std::max(std::abs(range.lower()),
37 std::abs(range.upper()))),
38 indexType.getBitSize().value());
39 auto intType =
40 moore::IntType::get(index.getContext(), bw, indexType.getDomain());
41
42 if (range.isLittleEndian()) {
43 if (range.lower() == 0)
44 return index;
45
46 Value newIndex =
47 builder.createOrFold<moore::ConversionOp>(loc, intType, index);
48 Value offset = builder.create<moore::ConstantOp>(
49 loc, intType, range.lower(), /*isSigned = */ range.lower() < 0);
50 return builder.createOrFold<moore::SubOp>(loc, newIndex, offset);
51 }
52
53 if (range.upper() == 0)
54 return builder.createOrFold<moore::NegOp>(loc, index);
55
56 Value newIndex =
57 builder.createOrFold<moore::ConversionOp>(loc, intType, index);
58 Value offset = builder.create<moore::ConstantOp>(
59 loc, intType, range.upper(), /* isSigned = */ range.upper() < 0);
60 return builder.createOrFold<moore::SubOp>(loc, offset, newIndex);
61}
62
63namespace {
64/// A visitor handling expressions that can be lowered as lvalue and rvalue.
65struct ExprVisitor {
66 Context &context;
67 Location loc;
68 OpBuilder &builder;
69 bool isLvalue;
70
71 ExprVisitor(Context &context, Location loc, bool isLvalue)
72 : context(context), loc(loc), builder(context.builder),
73 isLvalue(isLvalue) {}
74
75 /// Convert an expression either as an lvalue or rvalue, depending on whether
76 /// this is an lvalue or rvalue visitor. This is useful for projections such
77 /// as `a[i]`, where you want `a` as an lvalue if you want `a[i]` as an
78 /// lvalue, or `a` as an rvalue if you want `a[i]` as an rvalue.
79 Value convertLvalueOrRvalueExpression(const slang::ast::Expression &expr) {
80 if (isLvalue)
81 return context.convertLvalueExpression(expr);
82 return context.convertRvalueExpression(expr);
83 }
84
85 /// Handle single bit selections.
86 Value visit(const slang::ast::ElementSelectExpression &expr) {
87 auto type = context.convertType(*expr.type);
88 auto value = convertLvalueOrRvalueExpression(expr.value());
89 if (!type || !value)
90 return {};
91 auto resultType =
92 isLvalue ? moore::RefType::get(cast<moore::UnpackedType>(type)) : type;
93 auto range = expr.value().type->getFixedRange();
94 if (auto *constValue = expr.selector().constant) {
95 assert(!constValue->hasUnknown());
96 assert(constValue->size() <= 32);
97
98 auto lowBit = constValue->integer().as<uint32_t>().value();
99 if (isLvalue)
100 return builder.create<moore::ExtractRefOp>(
101 loc, resultType, value, range.translateIndex(lowBit));
102 else
103 return builder.create<moore::ExtractOp>(loc, resultType, value,
104 range.translateIndex(lowBit));
105 }
106 auto lowBit = context.convertRvalueExpression(expr.selector());
107 if (!lowBit)
108 return {};
109 lowBit = getSelectIndex(builder, loc, lowBit, range);
110 if (isLvalue)
111 return builder.create<moore::DynExtractRefOp>(loc, resultType, value,
112 lowBit);
113 else
114 return builder.create<moore::DynExtractOp>(loc, resultType, value,
115 lowBit);
116 }
117
118 /// Handle range bit selections.
119 Value visit(const slang::ast::RangeSelectExpression &expr) {
120 auto type = context.convertType(*expr.type);
121 auto value = convertLvalueOrRvalueExpression(expr.value());
122 if (!type || !value)
123 return {};
124
125 std::optional<int32_t> constLeft;
126 std::optional<int32_t> constRight;
127 if (auto *constant = expr.left().constant)
128 constLeft = constant->integer().as<int32_t>();
129 if (auto *constant = expr.right().constant)
130 constRight = constant->integer().as<int32_t>();
131
132 // We need to determine the right bound of the range. This is the address of
133 // the least significant bit of the underlying bit storage, which is the
134 // offset we want to pass to the extract op.
135 //
136 // The arrays [6:2] and [2:6] both have 5 bits worth of underlying storage.
137 // The left and right bound of the range only determine the addressing
138 // scheme of the storage bits:
139 //
140 // Storage bits: 4 3 2 1 0 <-- extract op works on storage bits
141 // [6:2] indices: 6 5 4 3 2 ("little endian" in Slang terms)
142 // [2:6] indices: 2 3 4 5 6 ("big endian" in Slang terms)
143 //
144 // Before we can extract, we need to map the range select left and right
145 // bounds from these indices to actual bit positions in the storage.
146
147 Value offsetDyn;
148 int32_t offsetConst = 0;
149 auto range = expr.value().type->getFixedRange();
150
151 using slang::ast::RangeSelectionKind;
152 if (expr.getSelectionKind() == RangeSelectionKind::Simple) {
153 // For a constant range [a:b], we want the offset of the lowest storage
154 // bit from which we are starting the extract. For a range [5:3] this is
155 // bit index 3; for a range [3:5] this is bit index 5. Both of these are
156 // later translated map to bit offset 1 (see bit indices above).
157 assert(constRight && "constness checked in slang");
158 offsetConst = *constRight;
159 } else {
160 // For an indexed range [a+:b] or [a-:b], determining the lowest storage
161 // bit is a bit more complicated. We start out with the base index `a`.
162 // This is the lower *index* of the range, but not the lower *storage bit
163 // position*.
164 //
165 // The range [a+:b] expands to [a+b-1:a] for a [6:2] range, or [a:a+b-1]
166 // for a [2:6] range. The range [a-:b] expands to [a:a-b+1] for a [6:2]
167 // range, or [a-b+1:a] for a [2:6] range.
168 if (constLeft) {
169 offsetConst = *constLeft;
170 } else {
171 offsetDyn = context.convertRvalueExpression(expr.left());
172 if (!offsetDyn)
173 return {};
174 }
175
176 // For a [a-:b] select on [2:6] and a [a+:b] select on [6:2], the range
177 // expands to [a-b+1:a] and [a+b-1:a]. In this case, the right bound which
178 // corresponds to the lower *storage bit offset*, is just `a` and there's
179 // no further tweaking to do.
180 int32_t offsetAdd = 0;
181
182 // For a [a-:b] select on [6:2], the range expands to [a:a-b+1]. We
183 // therefore have to take the `a` from above and adjust it by `-b+1` to
184 // arrive at the right bound.
185 if (expr.getSelectionKind() == RangeSelectionKind::IndexedDown &&
186 range.isLittleEndian()) {
187 assert(constRight && "constness checked in slang");
188 offsetAdd = 1 - *constRight;
189 }
190
191 // For a [a+:b] select on [2:6], the range expands to [a:a+b-1]. We
192 // therefore have to take the `a` from above and adjust it by `+b-1` to
193 // arrive at the right bound.
194 if (expr.getSelectionKind() == RangeSelectionKind::IndexedUp &&
195 !range.isLittleEndian()) {
196 assert(constRight && "constness checked in slang");
197 offsetAdd = *constRight - 1;
198 }
199
200 // Adjust the offset such that it matches the right bound of the range.
201 if (offsetAdd != 0) {
202 if (offsetDyn)
203 offsetDyn = builder.create<moore::AddOp>(
204 loc, offsetDyn,
205 builder.create<moore::ConstantOp>(
206 loc, cast<moore::IntType>(offsetDyn.getType()), offsetAdd,
207 /*isSigned=*/offsetAdd < 0));
208 else
209 offsetConst += offsetAdd;
210 }
211 }
212
213 // Create a dynamic or constant extract. Use `getSelectIndex` and
214 // `ConstantRange::translateIndex` to map from the bit indices provided by
215 // the user to the actual storage bit position. Since `offset*` corresponds
216 // to the right bound of the range, which provides the index of the least
217 // significant selected storage bit, we get the bit offset at which we want
218 // to start extracting.
219 auto resultType =
220 isLvalue ? moore::RefType::get(cast<moore::UnpackedType>(type)) : type;
221
222 if (offsetDyn) {
223 offsetDyn = getSelectIndex(builder, loc, offsetDyn, range);
224 if (isLvalue) {
225 return builder.create<moore::DynExtractRefOp>(loc, resultType, value,
226 offsetDyn);
227 } else {
228 return builder.create<moore::DynExtractOp>(loc, resultType, value,
229 offsetDyn);
230 }
231 } else {
232 offsetConst = range.translateIndex(offsetConst);
233 if (isLvalue) {
234 return builder.create<moore::ExtractRefOp>(loc, resultType, value,
235 offsetConst);
236 } else {
237 return builder.create<moore::ExtractOp>(loc, resultType, value,
238 offsetConst);
239 }
240 }
241 }
242
243 /// Handle concatenations.
244 Value visit(const slang::ast::ConcatenationExpression &expr) {
245 SmallVector<Value> operands;
246 for (auto *operand : expr.operands()) {
247 // Handle empty replications like `{0{...}}` which may occur within
248 // concatenations. Slang assigns them a `void` type which we can check for
249 // here.
250 if (operand->type->isVoid())
251 continue;
252 auto value = convertLvalueOrRvalueExpression(*operand);
253 if (!value)
254 return {};
255 if (!isLvalue)
256 value = context.convertToSimpleBitVector(value);
257 operands.push_back(value);
258 }
259 if (isLvalue)
260 return builder.create<moore::ConcatRefOp>(loc, operands);
261 else
262 return builder.create<moore::ConcatOp>(loc, operands);
263 }
264
265 /// Handle member accesses.
266 Value visit(const slang::ast::MemberAccessExpression &expr) {
267 auto type = context.convertType(*expr.type);
268 auto valueType = expr.value().type;
269 auto value = convertLvalueOrRvalueExpression(expr.value());
270 if (!type || !value)
271 return {};
272
273 auto resultType =
274 isLvalue ? moore::RefType::get(cast<moore::UnpackedType>(type)) : type;
275 auto memberName = builder.getStringAttr(expr.member.name);
276
277 // Handle structs.
278 if (valueType->isStruct()) {
279 if (isLvalue)
280 return builder.create<moore::StructExtractRefOp>(loc, resultType,
281 memberName, value);
282 else
283 return builder.create<moore::StructExtractOp>(loc, resultType,
284 memberName, value);
285 }
286
287 // Handle unions.
288 if (valueType->isPackedUnion() || valueType->isUnpackedUnion()) {
289 if (isLvalue)
290 return builder.create<moore::UnionExtractRefOp>(loc, resultType,
291 memberName, value);
292 else
293 return builder.create<moore::UnionExtractOp>(loc, type, memberName,
294 value);
295 }
296
297 mlir::emitError(loc, "expression of type ")
298 << value.getType() << " has no member fields";
299 return {};
300 }
301};
302} // namespace
303
304//===----------------------------------------------------------------------===//
305// Rvalue Conversion
306//===----------------------------------------------------------------------===//
307
308// NOLINTBEGIN(misc-no-recursion)
309namespace {
310struct RvalueExprVisitor : public ExprVisitor {
311 RvalueExprVisitor(Context &context, Location loc)
312 : ExprVisitor(context, loc, /*isLvalue=*/false) {}
313 using ExprVisitor::visit;
314
315 // Handle references to the left-hand side of a parent assignment.
316 Value visit(const slang::ast::LValueReferenceExpression &expr) {
317 assert(!context.lvalueStack.empty() && "parent assignments push lvalue");
318 auto lvalue = context.lvalueStack.back();
319 return builder.create<moore::ReadOp>(loc, lvalue);
320 }
321
322 // Handle named values, such as references to declared variables.
323 Value visit(const slang::ast::NamedValueExpression &expr) {
324 if (auto value = context.valueSymbols.lookup(&expr.symbol)) {
325 if (isa<moore::RefType>(value.getType())) {
326 auto readOp = builder.create<moore::ReadOp>(loc, value);
327 if (context.rvalueReadCallback)
328 context.rvalueReadCallback(readOp);
329 value = readOp.getResult();
330 }
331 return value;
332 }
333
334 // Try to materialize constant values directly.
335 auto constant = context.evaluateConstant(expr);
336 if (auto value = context.materializeConstant(constant, *expr.type, loc))
337 return value;
338
339 // Otherwise some other part of ImportVerilog should have added an MLIR
340 // value for this expression's symbol to the `context.valueSymbols` table.
341 auto d = mlir::emitError(loc, "unknown name `") << expr.symbol.name << "`";
342 d.attachNote(context.convertLocation(expr.symbol.location))
343 << "no rvalue generated for " << slang::ast::toString(expr.symbol.kind);
344 return {};
345 }
346
347 // Handle hierarchical values, such as `x = Top.sub.var`.
348 Value visit(const slang::ast::HierarchicalValueExpression &expr) {
349 auto hierLoc = context.convertLocation(expr.symbol.location);
350 if (auto value = context.valueSymbols.lookup(&expr.symbol)) {
351 if (isa<moore::RefType>(value.getType())) {
352 auto readOp = builder.create<moore::ReadOp>(hierLoc, value);
353 if (context.rvalueReadCallback)
354 context.rvalueReadCallback(readOp);
355 value = readOp.getResult();
356 }
357 return value;
358 }
359
360 // Emit an error for those hierarchical values not recorded in the
361 // `valueSymbols`.
362 auto d = mlir::emitError(loc, "unknown hierarchical name `")
363 << expr.symbol.name << "`";
364 d.attachNote(hierLoc) << "no rvalue generated for "
365 << slang::ast::toString(expr.symbol.kind);
366 return {};
367 }
368
369 // Handle type conversions (explicit and implicit).
370 Value visit(const slang::ast::ConversionExpression &expr) {
371 auto type = context.convertType(*expr.type);
372 if (!type)
373 return {};
374 return context.convertRvalueExpression(expr.operand(), type);
375 }
376
377 // Handle blocking and non-blocking assignments.
378 Value visit(const slang::ast::AssignmentExpression &expr) {
379 auto lhs = context.convertLvalueExpression(expr.left());
380 if (!lhs)
381 return {};
382
383 context.lvalueStack.push_back(lhs);
384 auto rhs = context.convertRvalueExpression(
385 expr.right(), cast<moore::RefType>(lhs.getType()).getNestedType());
386 context.lvalueStack.pop_back();
387 if (!rhs)
388 return {};
389
390 if (expr.timingControl) {
391 auto loc = context.convertLocation(expr.timingControl->sourceRange);
392 mlir::emitError(loc, "delayed assignments not supported");
393 return {};
394 }
395
396 if (expr.isNonBlocking())
397 builder.create<moore::NonBlockingAssignOp>(loc, lhs, rhs);
398 else
399 builder.create<moore::BlockingAssignOp>(loc, lhs, rhs);
400 return rhs;
401 }
402
403 // Helper function to convert an argument to a simple bit vector type, pass it
404 // to a reduction op, and optionally invert the result.
405 template <class ConcreteOp>
406 Value createReduction(Value arg, bool invert) {
407 arg = context.convertToSimpleBitVector(arg);
408 if (!arg)
409 return {};
410 Value result = builder.create<ConcreteOp>(loc, arg);
411 if (invert)
412 result = builder.create<moore::NotOp>(loc, result);
413 return result;
414 }
415
416 // Helper function to create pre and post increments and decrements.
417 Value createIncrement(Value arg, bool isInc, bool isPost) {
418 auto preValue = builder.create<moore::ReadOp>(loc, arg);
419 auto one = builder.create<moore::ConstantOp>(
420 loc, cast<moore::IntType>(preValue.getType()), 1);
421 auto postValue =
422 isInc ? builder.create<moore::AddOp>(loc, preValue, one).getResult()
423 : builder.create<moore::SubOp>(loc, preValue, one).getResult();
424 builder.create<moore::BlockingAssignOp>(loc, arg, postValue);
425 if (isPost)
426 return preValue;
427 return postValue;
428 }
429
430 // Handle unary operators.
431 Value visit(const slang::ast::UnaryExpression &expr) {
432 using slang::ast::UnaryOperator;
433 Value arg;
434 if (expr.op == UnaryOperator::Preincrement ||
435 expr.op == UnaryOperator::Predecrement ||
436 expr.op == UnaryOperator::Postincrement ||
437 expr.op == UnaryOperator::Postdecrement)
438 arg = context.convertLvalueExpression(expr.operand());
439 else
440 arg = context.convertRvalueExpression(expr.operand());
441 if (!arg)
442 return {};
443
444 switch (expr.op) {
445 // `+a` is simply `a`, but converted to a simple bit vector type since
446 // this is technically an arithmetic operation.
447 case UnaryOperator::Plus:
448 return context.convertToSimpleBitVector(arg);
449
450 case UnaryOperator::Minus:
451 arg = context.convertToSimpleBitVector(arg);
452 if (!arg)
453 return {};
454 return builder.create<moore::NegOp>(loc, arg);
455
456 case UnaryOperator::BitwiseNot:
457 arg = context.convertToSimpleBitVector(arg);
458 if (!arg)
459 return {};
460 return builder.create<moore::NotOp>(loc, arg);
461
462 case UnaryOperator::BitwiseAnd:
463 return createReduction<moore::ReduceAndOp>(arg, false);
464 case UnaryOperator::BitwiseOr:
465 return createReduction<moore::ReduceOrOp>(arg, false);
466 case UnaryOperator::BitwiseXor:
467 return createReduction<moore::ReduceXorOp>(arg, false);
468 case UnaryOperator::BitwiseNand:
469 return createReduction<moore::ReduceAndOp>(arg, true);
470 case UnaryOperator::BitwiseNor:
471 return createReduction<moore::ReduceOrOp>(arg, true);
472 case UnaryOperator::BitwiseXnor:
473 return createReduction<moore::ReduceXorOp>(arg, true);
474
475 case UnaryOperator::LogicalNot:
476 arg = context.convertToBool(arg);
477 if (!arg)
478 return {};
479 return builder.create<moore::NotOp>(loc, arg);
480
481 case UnaryOperator::Preincrement:
482 return createIncrement(arg, true, false);
483 case UnaryOperator::Predecrement:
484 return createIncrement(arg, false, false);
485 case UnaryOperator::Postincrement:
486 return createIncrement(arg, true, true);
487 case UnaryOperator::Postdecrement:
488 return createIncrement(arg, false, true);
489 }
490
491 mlir::emitError(loc, "unsupported unary operator");
492 return {};
493 }
494
495 // Helper function to convert two arguments to a simple bit vector type and
496 // pass them into a binary op.
497 template <class ConcreteOp>
498 Value createBinary(Value lhs, Value rhs) {
499 lhs = context.convertToSimpleBitVector(lhs);
500 if (!lhs)
501 return {};
502 rhs = context.convertToSimpleBitVector(rhs);
503 if (!rhs)
504 return {};
505 return builder.create<ConcreteOp>(loc, lhs, rhs);
506 }
507
508 // Handle binary operators.
509 Value visit(const slang::ast::BinaryExpression &expr) {
510 auto lhs = context.convertRvalueExpression(expr.left());
511 if (!lhs)
512 return {};
513 auto rhs = context.convertRvalueExpression(expr.right());
514 if (!rhs)
515 return {};
516
517 // Determine the domain of the result.
518 Domain domain = Domain::TwoValued;
519 if (expr.type->isFourState() || expr.left().type->isFourState() ||
520 expr.right().type->isFourState())
521 domain = Domain::FourValued;
522
523 using slang::ast::BinaryOperator;
524 switch (expr.op) {
525 case BinaryOperator::Add:
526 return createBinary<moore::AddOp>(lhs, rhs);
527 case BinaryOperator::Subtract:
528 return createBinary<moore::SubOp>(lhs, rhs);
529 case BinaryOperator::Multiply:
530 return createBinary<moore::MulOp>(lhs, rhs);
531 case BinaryOperator::Divide:
532 if (expr.type->isSigned())
533 return createBinary<moore::DivSOp>(lhs, rhs);
534 else
535 return createBinary<moore::DivUOp>(lhs, rhs);
536 case BinaryOperator::Mod:
537 if (expr.type->isSigned())
538 return createBinary<moore::ModSOp>(lhs, rhs);
539 else
540 return createBinary<moore::ModUOp>(lhs, rhs);
541 case BinaryOperator::Power: {
542 // Slang casts the LHS and result of the `**` operator to a four-valued
543 // type, since the operator can return X even for two-valued inputs. To
544 // maintain uniform types across operands and results, cast the RHS to
545 // that four-valued type as well.
546 auto rhsCast =
547 builder.create<moore::ConversionOp>(loc, lhs.getType(), rhs);
548 if (expr.type->isSigned())
549 return createBinary<moore::PowSOp>(lhs, rhsCast);
550 else
551 return createBinary<moore::PowUOp>(lhs, rhsCast);
552 }
553
554 case BinaryOperator::BinaryAnd:
555 return createBinary<moore::AndOp>(lhs, rhs);
556 case BinaryOperator::BinaryOr:
557 return createBinary<moore::OrOp>(lhs, rhs);
558 case BinaryOperator::BinaryXor:
559 return createBinary<moore::XorOp>(lhs, rhs);
560 case BinaryOperator::BinaryXnor: {
561 auto result = createBinary<moore::XorOp>(lhs, rhs);
562 if (!result)
563 return {};
564 return builder.create<moore::NotOp>(loc, result);
565 }
566
567 case BinaryOperator::Equality:
568 if (isa<moore::UnpackedArrayType>(lhs.getType()))
569 return builder.create<moore::UArrayCmpOp>(
570 loc, moore::UArrayCmpPredicate::eq, lhs, rhs);
571 else if (isa<moore::StringType>(lhs.getType()))
572 return builder.create<moore::StringCmpOp>(
573 loc, moore::StringCmpPredicate::eq, lhs, rhs);
574 else
575 return createBinary<moore::EqOp>(lhs, rhs);
576 case BinaryOperator::Inequality:
577 if (isa<moore::UnpackedArrayType>(lhs.getType()))
578 return builder.create<moore::UArrayCmpOp>(
579 loc, moore::UArrayCmpPredicate::ne, lhs, rhs);
580 else if (isa<moore::StringType>(lhs.getType()))
581 return builder.create<moore::StringCmpOp>(
582 loc, moore::StringCmpPredicate::ne, lhs, rhs);
583 else
584 return createBinary<moore::NeOp>(lhs, rhs);
585 case BinaryOperator::CaseEquality:
586 return createBinary<moore::CaseEqOp>(lhs, rhs);
587 case BinaryOperator::CaseInequality:
588 return createBinary<moore::CaseNeOp>(lhs, rhs);
589 case BinaryOperator::WildcardEquality:
590 return createBinary<moore::WildcardEqOp>(lhs, rhs);
591 case BinaryOperator::WildcardInequality:
592 return createBinary<moore::WildcardNeOp>(lhs, rhs);
593
594 case BinaryOperator::GreaterThanEqual:
595 if (expr.left().type->isSigned())
596 return createBinary<moore::SgeOp>(lhs, rhs);
597 else if (isa<moore::StringType>(lhs.getType()))
598 return builder.create<moore::StringCmpOp>(
599 loc, moore::StringCmpPredicate::ge, lhs, rhs);
600 else
601 return createBinary<moore::UgeOp>(lhs, rhs);
602 case BinaryOperator::GreaterThan:
603 if (expr.left().type->isSigned())
604 return createBinary<moore::SgtOp>(lhs, rhs);
605 else if (isa<moore::StringType>(lhs.getType()))
606 return builder.create<moore::StringCmpOp>(
607 loc, moore::StringCmpPredicate::gt, lhs, rhs);
608 else
609 return createBinary<moore::UgtOp>(lhs, rhs);
610 case BinaryOperator::LessThanEqual:
611 if (expr.left().type->isSigned())
612 return createBinary<moore::SleOp>(lhs, rhs);
613 else if (isa<moore::StringType>(lhs.getType()))
614 return builder.create<moore::StringCmpOp>(
615 loc, moore::StringCmpPredicate::le, lhs, rhs);
616 else
617 return createBinary<moore::UleOp>(lhs, rhs);
618 case BinaryOperator::LessThan:
619 if (expr.left().type->isSigned())
620 return createBinary<moore::SltOp>(lhs, rhs);
621 else if (isa<moore::StringType>(lhs.getType()))
622 return builder.create<moore::StringCmpOp>(
623 loc, moore::StringCmpPredicate::lt, lhs, rhs);
624 else
625 return createBinary<moore::UltOp>(lhs, rhs);
626
627 // See IEEE 1800-2017 § 11.4.7 "Logical operators".
628 case BinaryOperator::LogicalAnd: {
629 // TODO: This should short-circuit. Put the RHS code into a separate
630 // block.
631 lhs = context.convertToBool(lhs, domain);
632 if (!lhs)
633 return {};
634 rhs = context.convertToBool(rhs, domain);
635 if (!rhs)
636 return {};
637 return builder.create<moore::AndOp>(loc, lhs, rhs);
638 }
639 case BinaryOperator::LogicalOr: {
640 // TODO: This should short-circuit. Put the RHS code into a separate
641 // block.
642 lhs = context.convertToBool(lhs, domain);
643 if (!lhs)
644 return {};
645 rhs = context.convertToBool(rhs, domain);
646 if (!rhs)
647 return {};
648 return builder.create<moore::OrOp>(loc, lhs, rhs);
649 }
650 case BinaryOperator::LogicalImplication: {
651 // `(lhs -> rhs)` equivalent to `(!lhs || rhs)`.
652 lhs = context.convertToBool(lhs, domain);
653 if (!lhs)
654 return {};
655 rhs = context.convertToBool(rhs, domain);
656 if (!rhs)
657 return {};
658 auto notLHS = builder.create<moore::NotOp>(loc, lhs);
659 return builder.create<moore::OrOp>(loc, notLHS, rhs);
660 }
661 case BinaryOperator::LogicalEquivalence: {
662 // `(lhs <-> rhs)` equivalent to `(lhs && rhs) || (!lhs && !rhs)`.
663 lhs = context.convertToBool(lhs, domain);
664 if (!lhs)
665 return {};
666 rhs = context.convertToBool(rhs, domain);
667 if (!rhs)
668 return {};
669 auto notLHS = builder.create<moore::NotOp>(loc, lhs);
670 auto notRHS = builder.create<moore::NotOp>(loc, rhs);
671 auto both = builder.create<moore::AndOp>(loc, lhs, rhs);
672 auto notBoth = builder.create<moore::AndOp>(loc, notLHS, notRHS);
673 return builder.create<moore::OrOp>(loc, both, notBoth);
674 }
675
676 case BinaryOperator::LogicalShiftLeft:
677 return createBinary<moore::ShlOp>(lhs, rhs);
678 case BinaryOperator::LogicalShiftRight:
679 return createBinary<moore::ShrOp>(lhs, rhs);
680 case BinaryOperator::ArithmeticShiftLeft:
681 return createBinary<moore::ShlOp>(lhs, rhs);
682 case BinaryOperator::ArithmeticShiftRight: {
683 // The `>>>` operator is an arithmetic right shift if the LHS operand is
684 // signed, or a logical right shift if the operand is unsigned.
685 lhs = context.convertToSimpleBitVector(lhs);
686 rhs = context.convertToSimpleBitVector(rhs);
687 if (!lhs || !rhs)
688 return {};
689 if (expr.type->isSigned())
690 return builder.create<moore::AShrOp>(loc, lhs, rhs);
691 return builder.create<moore::ShrOp>(loc, lhs, rhs);
692 }
693 }
694
695 mlir::emitError(loc, "unsupported binary operator");
696 return {};
697 }
698
699 // Handle `'0`, `'1`, `'x`, and `'z` literals.
700 Value visit(const slang::ast::UnbasedUnsizedIntegerLiteral &expr) {
701 return context.materializeSVInt(expr.getValue(), *expr.type, loc);
702 }
703
704 // Handle integer literals.
705 Value visit(const slang::ast::IntegerLiteral &expr) {
706 return context.materializeSVInt(expr.getValue(), *expr.type, loc);
707 }
708
709 // Handle replications.
710 Value visit(const slang::ast::ReplicationExpression &expr) {
711 auto type = context.convertType(*expr.type);
712 auto value = context.convertRvalueExpression(expr.concat());
713 if (!value)
714 return {};
715 return builder.create<moore::ReplicateOp>(loc, type, value);
716 }
717
718 // Handle set membership operator.
719 Value visit(const slang::ast::InsideExpression &expr) {
720 auto lhs = context.convertToSimpleBitVector(
721 context.convertRvalueExpression(expr.left()));
722 if (!lhs)
723 return {};
724 // All conditions for determining whether it is inside.
725 SmallVector<Value> conditions;
726
727 // Traverse open range list.
728 for (const auto *listExpr : expr.rangeList()) {
729 Value cond;
730 // The open range list on the right-hand side of the inside operator is a
731 // comma-separated list of expressions or ranges.
732 if (const auto *openRange =
733 listExpr->as_if<slang::ast::OpenRangeExpression>()) {
734 // Handle ranges.
735 auto lowBound = context.convertToSimpleBitVector(
736 context.convertRvalueExpression(openRange->left()));
737 auto highBound = context.convertToSimpleBitVector(
738 context.convertRvalueExpression(openRange->right()));
739 if (!lowBound || !highBound)
740 return {};
741 Value leftValue, rightValue;
742 // Determine if the expression on the left-hand side is inclusively
743 // within the range.
744 if (openRange->left().type->isSigned() ||
745 expr.left().type->isSigned()) {
746 leftValue = builder.create<moore::SgeOp>(loc, lhs, lowBound);
747 } else {
748 leftValue = builder.create<moore::UgeOp>(loc, lhs, lowBound);
749 }
750 if (openRange->right().type->isSigned() ||
751 expr.left().type->isSigned()) {
752 rightValue = builder.create<moore::SleOp>(loc, lhs, highBound);
753 } else {
754 rightValue = builder.create<moore::UleOp>(loc, lhs, highBound);
755 }
756 cond = builder.create<moore::AndOp>(loc, leftValue, rightValue);
757 } else {
758 // Handle expressions.
759 if (!listExpr->type->isIntegral()) {
760 if (listExpr->type->isUnpackedArray()) {
761 mlir::emitError(
762 loc, "unpacked arrays in 'inside' expressions not supported");
763 return {};
764 }
765 mlir::emitError(
766 loc, "only simple bit vectors supported in 'inside' expressions");
767 return {};
768 }
769
770 auto value = context.convertToSimpleBitVector(
771 context.convertRvalueExpression(*listExpr));
772 if (!value)
773 return {};
774 cond = builder.create<moore::WildcardEqOp>(loc, lhs, value);
775 }
776 conditions.push_back(cond);
777 }
778
779 // Calculate the final result by `or` op.
780 auto result = conditions.back();
781 conditions.pop_back();
782 while (!conditions.empty()) {
783 result = builder.create<moore::OrOp>(loc, conditions.back(), result);
784 conditions.pop_back();
785 }
786 return result;
787 }
788
789 // Handle conditional operator `?:`.
790 Value visit(const slang::ast::ConditionalExpression &expr) {
791 auto type = context.convertType(*expr.type);
792
793 // Handle condition.
794 if (expr.conditions.size() > 1) {
795 mlir::emitError(loc)
796 << "unsupported conditional expression with more than one condition";
797 return {};
798 }
799 const auto &cond = expr.conditions[0];
800 if (cond.pattern) {
801 mlir::emitError(loc) << "unsupported conditional expression with pattern";
802 return {};
803 }
804 auto value =
805 context.convertToBool(context.convertRvalueExpression(*cond.expr));
806 if (!value)
807 return {};
808 auto conditionalOp = builder.create<moore::ConditionalOp>(loc, type, value);
809
810 // Create blocks for true region and false region.
811 auto &trueBlock = conditionalOp.getTrueRegion().emplaceBlock();
812 auto &falseBlock = conditionalOp.getFalseRegion().emplaceBlock();
813
814 OpBuilder::InsertionGuard g(builder);
815
816 // Handle left expression.
817 builder.setInsertionPointToStart(&trueBlock);
818 auto trueValue = context.convertRvalueExpression(expr.left(), type);
819 if (!trueValue)
820 return {};
821 builder.create<moore::YieldOp>(loc, trueValue);
822
823 // Handle right expression.
824 builder.setInsertionPointToStart(&falseBlock);
825 auto falseValue = context.convertRvalueExpression(expr.right(), type);
826 if (!falseValue)
827 return {};
828 builder.create<moore::YieldOp>(loc, falseValue);
829
830 return conditionalOp.getResult();
831 }
832
833 /// Handle calls.
834 Value visit(const slang::ast::CallExpression &expr) {
835 // Class method calls are currently not supported.
836 if (expr.thisClass()) {
837 mlir::emitError(loc, "unsupported class method call");
838 return {};
839 }
840
841 // Try to materialize constant values directly.
842 auto constant = context.evaluateConstant(expr);
843 if (auto value = context.materializeConstant(constant, *expr.type, loc))
844 return value;
845
846 return std::visit(
847 [&](auto &subroutine) { return visitCall(expr, subroutine); },
848 expr.subroutine);
849 }
850
851 /// Handle subroutine calls.
852 Value visitCall(const slang::ast::CallExpression &expr,
853 const slang::ast::SubroutineSymbol *subroutine) {
854 auto *lowering = context.declareFunction(*subroutine);
855 if (!lowering)
856 return {};
857
858 // Convert the call arguments. Input arguments are converted to an rvalue.
859 // All other arguments are converted to lvalues and passed into the function
860 // by reference.
861 SmallVector<Value> arguments;
862 for (auto [callArg, declArg] :
863 llvm::zip(expr.arguments(), subroutine->getArguments())) {
864
865 // Unpack the `<expr> = EmptyArgument` pattern emitted by Slang for output
866 // and inout arguments.
867 auto *expr = callArg;
868 if (const auto *assign = expr->as_if<slang::ast::AssignmentExpression>())
869 expr = &assign->left();
870
871 Value value;
872 if (declArg->direction == slang::ast::ArgumentDirection::In)
873 value = context.convertRvalueExpression(*expr);
874 else
875 value = context.convertLvalueExpression(*expr);
876 if (!value)
877 return {};
878 arguments.push_back(value);
879 }
880
881 // Create the call.
882 auto callOp =
883 builder.create<mlir::func::CallOp>(loc, lowering->op, arguments);
884
885 // For calls to void functions we need to have a value to return from this
886 // function. Create a dummy `unrealized_conversion_cast`, which will get
887 // deleted again later on.
888 if (callOp.getNumResults() == 0)
889 return builder
890 .create<mlir::UnrealizedConversionCastOp>(
891 loc, moore::VoidType::get(context.getContext()), ValueRange{})
892 .getResult(0);
893
894 return callOp.getResult(0);
895 }
896
897 /// Handle system calls.
898 Value visitCall(const slang::ast::CallExpression &expr,
899 const slang::ast::CallExpression::SystemCallInfo &info) {
900 const auto &subroutine = *info.subroutine;
901 auto args = expr.arguments();
902
903 if (args.size() == 1) {
904 auto value = context.convertRvalueExpression(*args[0]);
905 if (!value)
906 return {};
907 auto result = context.convertSystemCallArity1(subroutine, loc, value);
908 if (failed(result))
909 return {};
910 if (*result)
911 return *result;
912 }
913
914 mlir::emitError(loc) << "unsupported system call `" << subroutine.name
915 << "`";
916 return {};
917 }
918
919 /// Handle string literals.
920 Value visit(const slang::ast::StringLiteral &expr) {
921 auto type = context.convertType(*expr.type);
922 return builder.create<moore::StringConstantOp>(loc, type, expr.getValue());
923 }
924
925 /// Handle real literals.
926 Value visit(const slang::ast::RealLiteral &expr) {
927 return builder.create<moore::RealLiteralOp>(
928 loc, builder.getF64FloatAttr(expr.getValue()));
929 }
930
931 /// Handle assignment patterns.
932 Value visitAssignmentPattern(
933 const slang::ast::AssignmentPatternExpressionBase &expr,
934 unsigned replCount = 1) {
935 auto type = context.convertType(*expr.type);
936
937 // Convert the individual elements first.
938 auto elementCount = expr.elements().size();
939 SmallVector<Value> elements;
940 elements.reserve(replCount * elementCount);
941 for (auto elementExpr : expr.elements()) {
942 auto value = context.convertRvalueExpression(*elementExpr);
943 if (!value)
944 return {};
945 elements.push_back(value);
946 }
947 for (unsigned replIdx = 1; replIdx < replCount; ++replIdx)
948 for (unsigned elementIdx = 0; elementIdx < elementCount; ++elementIdx)
949 elements.push_back(elements[elementIdx]);
950
951 // Handle integers.
952 if (auto intType = dyn_cast<moore::IntType>(type)) {
953 assert(intType.getWidth() == elements.size());
954 std::reverse(elements.begin(), elements.end());
955 return builder.create<moore::ConcatOp>(loc, intType, elements);
956 }
957
958 // Handle packed structs.
959 if (auto structType = dyn_cast<moore::StructType>(type)) {
960 assert(structType.getMembers().size() == elements.size());
961 return builder.create<moore::StructCreateOp>(loc, structType, elements);
962 }
963
964 // Handle unpacked structs.
965 if (auto structType = dyn_cast<moore::UnpackedStructType>(type)) {
966 assert(structType.getMembers().size() == elements.size());
967 return builder.create<moore::StructCreateOp>(loc, structType, elements);
968 }
969
970 // Handle packed arrays.
971 if (auto arrayType = dyn_cast<moore::ArrayType>(type)) {
972 assert(arrayType.getSize() == elements.size());
973 return builder.create<moore::ArrayCreateOp>(loc, arrayType, elements);
974 }
975
976 // Handle unpacked arrays.
977 if (auto arrayType = dyn_cast<moore::UnpackedArrayType>(type)) {
978 assert(arrayType.getSize() == elements.size());
979 return builder.create<moore::ArrayCreateOp>(loc, arrayType, elements);
980 }
981
982 mlir::emitError(loc) << "unsupported assignment pattern with type " << type;
983 return {};
984 }
985
986 Value visit(const slang::ast::SimpleAssignmentPatternExpression &expr) {
987 return visitAssignmentPattern(expr);
988 }
989
990 Value visit(const slang::ast::StructuredAssignmentPatternExpression &expr) {
991 return visitAssignmentPattern(expr);
992 }
993
994 Value visit(const slang::ast::ReplicatedAssignmentPatternExpression &expr) {
995 auto count =
996 context.evaluateConstant(expr.count()).integer().as<unsigned>();
997 assert(count && "Slang guarantees constant non-zero replication count");
998 return visitAssignmentPattern(expr, *count);
999 }
1000
1001 Value visit(const slang::ast::StreamingConcatenationExpression &expr) {
1002 SmallVector<Value> operands;
1003 for (auto stream : expr.streams()) {
1004 auto operandLoc = context.convertLocation(stream.operand->sourceRange);
1005 if (!stream.constantWithWidth.has_value() && stream.withExpr) {
1006 mlir::emitError(operandLoc)
1007 << "Moore only support streaming "
1008 "concatenation with fixed size 'with expression'";
1009 return {};
1010 }
1011 Value value;
1012 if (stream.constantWithWidth.has_value()) {
1013 value = context.convertRvalueExpression(*stream.withExpr);
1014 auto type = cast<moore::UnpackedType>(value.getType());
1015 auto intType = moore::IntType::get(
1016 context.getContext(), type.getBitSize().value(), type.getDomain());
1017 // Do not care if it's signed, because we will not do expansion.
1018 value = context.materializeConversion(intType, value, false, loc);
1019 } else {
1020 value = context.convertRvalueExpression(*stream.operand);
1021 }
1022
1023 value = context.convertToSimpleBitVector(value);
1024 if (!value)
1025 return {};
1026 operands.push_back(value);
1027 }
1028 Value value;
1029
1030 if (operands.size() == 1) {
1031 // There must be at least one element, otherwise slang will report an
1032 // error.
1033 value = operands.front();
1034 } else {
1035 value = builder.create<moore::ConcatOp>(loc, operands).getResult();
1036 }
1037
1038 if (expr.sliceSize == 0) {
1039 return value;
1040 }
1041
1042 auto type = cast<moore::IntType>(value.getType());
1043 SmallVector<Value> slicedOperands;
1044 auto iterMax = type.getWidth() / expr.sliceSize;
1045 auto remainSize = type.getWidth() % expr.sliceSize;
1046
1047 for (size_t i = 0; i < iterMax; i++) {
1048 auto extractResultType = moore::IntType::get(
1049 context.getContext(), expr.sliceSize, type.getDomain());
1050
1051 auto extracted = builder.create<moore::ExtractOp>(
1052 loc, extractResultType, value, i * expr.sliceSize);
1053 slicedOperands.push_back(extracted);
1054 }
1055 // Handle other wire
1056 if (remainSize) {
1057 auto extractResultType = moore::IntType::get(
1058 context.getContext(), remainSize, type.getDomain());
1059
1060 auto extracted = builder.create<moore::ExtractOp>(
1061 loc, extractResultType, value, iterMax * expr.sliceSize);
1062 slicedOperands.push_back(extracted);
1063 }
1064
1065 return builder.create<moore::ConcatOp>(loc, slicedOperands);
1066 }
1067
1068 Value visit(const slang::ast::AssertionInstanceExpression &expr) {
1069 return context.convertAssertionExpression(expr.body, loc);
1070 }
1071
1072 /// Emit an error for all other expressions.
1073 template <typename T>
1074 Value visit(T &&node) {
1075 mlir::emitError(loc, "unsupported expression: ")
1076 << slang::ast::toString(node.kind);
1077 return {};
1078 }
1079
1080 Value visitInvalid(const slang::ast::Expression &expr) {
1081 mlir::emitError(loc, "invalid expression");
1082 return {};
1083 }
1084};
1085} // namespace
1086
1087//===----------------------------------------------------------------------===//
1088// Lvalue Conversion
1089//===----------------------------------------------------------------------===//
1090
1091namespace {
1092struct LvalueExprVisitor : public ExprVisitor {
1093 LvalueExprVisitor(Context &context, Location loc)
1094 : ExprVisitor(context, loc, /*isLvalue=*/true) {}
1095 using ExprVisitor::visit;
1096
1097 // Handle named values, such as references to declared variables.
1098 Value visit(const slang::ast::NamedValueExpression &expr) {
1099 if (auto value = context.valueSymbols.lookup(&expr.symbol))
1100 return value;
1101 auto d = mlir::emitError(loc, "unknown name `") << expr.symbol.name << "`";
1102 d.attachNote(context.convertLocation(expr.symbol.location))
1103 << "no lvalue generated for " << slang::ast::toString(expr.symbol.kind);
1104 return {};
1105 }
1106
1107 // Handle hierarchical values, such as `Top.sub.var = x`.
1108 Value visit(const slang::ast::HierarchicalValueExpression &expr) {
1109 if (auto value = context.valueSymbols.lookup(&expr.symbol))
1110 return value;
1111
1112 // Emit an error for those hierarchical values not recorded in the
1113 // `valueSymbols`.
1114 auto d = mlir::emitError(loc, "unknown hierarchical name `")
1115 << expr.symbol.name << "`";
1116 d.attachNote(context.convertLocation(expr.symbol.location))
1117 << "no lvalue generated for " << slang::ast::toString(expr.symbol.kind);
1118 return {};
1119 }
1120
1121 Value visit(const slang::ast::StreamingConcatenationExpression &expr) {
1122 SmallVector<Value> operands;
1123 for (auto stream : expr.streams()) {
1124 auto operandLoc = context.convertLocation(stream.operand->sourceRange);
1125 if (!stream.constantWithWidth.has_value() && stream.withExpr) {
1126 mlir::emitError(operandLoc)
1127 << "Moore only support streaming "
1128 "concatenation with fixed size 'with expression'";
1129 return {};
1130 }
1131 Value value;
1132 if (stream.constantWithWidth.has_value()) {
1133 value = context.convertLvalueExpression(*stream.withExpr);
1134 auto type = cast<moore::UnpackedType>(
1135 cast<moore::RefType>(value.getType()).getNestedType());
1136 auto intType = moore::RefType::get(moore::IntType::get(
1137 context.getContext(), type.getBitSize().value(), type.getDomain()));
1138 // Do not care if it's signed, because we will not do expansion.
1139 value = context.materializeConversion(intType, value, false, loc);
1140 } else {
1141 value = context.convertLvalueExpression(*stream.operand);
1142 }
1143
1144 if (!value)
1145 return {};
1146 operands.push_back(value);
1147 }
1148 Value value;
1149 if (operands.size() == 1) {
1150 // There must be at least one element, otherwise slang will report an
1151 // error.
1152 value = operands.front();
1153 } else {
1154 value = builder.create<moore::ConcatRefOp>(loc, operands).getResult();
1155 }
1156
1157 if (expr.sliceSize == 0) {
1158 return value;
1159 }
1160
1161 auto type = cast<moore::IntType>(
1162 cast<moore::RefType>(value.getType()).getNestedType());
1163 SmallVector<Value> slicedOperands;
1164 auto widthSum = type.getWidth();
1165 auto domain = type.getDomain();
1166 auto iterMax = widthSum / expr.sliceSize;
1167 auto remainSize = widthSum % expr.sliceSize;
1168
1169 for (size_t i = 0; i < iterMax; i++) {
1170 auto extractResultType = moore::RefType::get(
1171 moore::IntType::get(context.getContext(), expr.sliceSize, domain));
1172
1173 auto extracted = builder.create<moore::ExtractRefOp>(
1174 loc, extractResultType, value, i * expr.sliceSize);
1175 slicedOperands.push_back(extracted);
1176 }
1177 // Handle other wire
1178 if (remainSize) {
1179 auto extractResultType = moore::RefType::get(
1180 moore::IntType::get(context.getContext(), remainSize, domain));
1181
1182 auto extracted = builder.create<moore::ExtractRefOp>(
1183 loc, extractResultType, value, iterMax * expr.sliceSize);
1184 slicedOperands.push_back(extracted);
1185 }
1186
1187 return builder.create<moore::ConcatRefOp>(loc, slicedOperands);
1188 }
1189
1190 /// Emit an error for all other expressions.
1191 template <typename T>
1192 Value visit(T &&node) {
1193 return context.convertRvalueExpression(node);
1194 }
1195
1196 Value visitInvalid(const slang::ast::Expression &expr) {
1197 mlir::emitError(loc, "invalid expression");
1198 return {};
1199 }
1200};
1201} // namespace
1202
1203//===----------------------------------------------------------------------===//
1204// Entry Points
1205//===----------------------------------------------------------------------===//
1206
1207Value Context::convertRvalueExpression(const slang::ast::Expression &expr,
1208 Type requiredType) {
1209 auto loc = convertLocation(expr.sourceRange);
1210 auto value = expr.visit(RvalueExprVisitor(*this, loc));
1211 if (value && requiredType)
1212 value =
1213 materializeConversion(requiredType, value, expr.type->isSigned(), loc);
1214 return value;
1215}
1216
1217Value Context::convertLvalueExpression(const slang::ast::Expression &expr) {
1218 auto loc = convertLocation(expr.sourceRange);
1219 return expr.visit(LvalueExprVisitor(*this, loc));
1220}
1221// NOLINTEND(misc-no-recursion)
1222
1223/// Helper function to convert a value to its "truthy" boolean value.
1224Value Context::convertToBool(Value value) {
1225 if (!value)
1226 return {};
1227 if (auto type = dyn_cast_or_null<moore::IntType>(value.getType()))
1228 if (type.getBitSize() == 1)
1229 return value;
1230 if (auto type = dyn_cast_or_null<moore::UnpackedType>(value.getType()))
1231 return builder.create<moore::BoolCastOp>(value.getLoc(), value);
1232 mlir::emitError(value.getLoc(), "expression of type ")
1233 << value.getType() << " cannot be cast to a boolean";
1234 return {};
1235}
1236
1237/// Materialize a Slang integer literal as a constant op.
1238Value Context::materializeSVInt(const slang::SVInt &svint,
1239 const slang::ast::Type &astType, Location loc) {
1240 auto type = convertType(astType);
1241 if (!type)
1242 return {};
1243
1244 bool typeIsFourValued = false;
1245 if (auto unpackedType = dyn_cast<moore::UnpackedType>(type))
1246 typeIsFourValued = unpackedType.getDomain() == moore::Domain::FourValued;
1247
1248 auto fvint = convertSVIntToFVInt(svint);
1249 auto intType = moore::IntType::get(getContext(), fvint.getBitWidth(),
1250 fvint.hasUnknown() || typeIsFourValued
1253 Value result = builder.create<moore::ConstantOp>(loc, intType, fvint);
1254 if (result.getType() != type)
1255 result = builder.create<moore::ConversionOp>(loc, type, result);
1256 return result;
1257}
1258
1259Value Context::materializeConstant(const slang::ConstantValue &constant,
1260 const slang::ast::Type &type, Location loc) {
1261 if (constant.isInteger())
1262 return materializeSVInt(constant.integer(), type, loc);
1263 return {};
1264}
1265
1266slang::ConstantValue
1267Context::evaluateConstant(const slang::ast::Expression &expr) {
1268 using slang::ast::EvalFlags;
1269 slang::ast::EvalContext evalContext(
1270 compilation, EvalFlags::CacheResults | EvalFlags::SpecparamsAllowed);
1271 return expr.eval(evalContext);
1272}
1273
1274/// Helper function to convert a value to its "truthy" boolean value and
1275/// convert it to the given domain.
1276Value Context::convertToBool(Value value, Domain domain) {
1277 value = convertToBool(value);
1278 if (!value)
1279 return {};
1280 auto type = moore::IntType::get(getContext(), 1, domain);
1281 if (value.getType() == type)
1282 return value;
1283 return builder.create<moore::ConversionOp>(value.getLoc(), type, value);
1284}
1285
1287 if (!value)
1288 return {};
1289 if (isa<moore::IntType>(value.getType()))
1290 return value;
1291
1292 // Some operations in Slang's AST, for example bitwise or `|`, don't cast
1293 // packed struct/array operands to simple bit vectors but directly operate
1294 // on the struct/array. Since the corresponding IR ops operate only on
1295 // simple bit vectors, insert a conversion in this case.
1296 if (auto packed = dyn_cast<moore::PackedType>(value.getType())) {
1297 if (auto bits = packed.getBitSize()) {
1298 auto sbvType =
1299 moore::IntType::get(value.getContext(), *bits, packed.getDomain());
1300 return builder.create<moore::ConversionOp>(value.getLoc(), sbvType,
1301 value);
1302 }
1303 }
1304
1305 mlir::emitError(value.getLoc()) << "expression of type " << value.getType()
1306 << " cannot be cast to a simple bit vector";
1307 return {};
1308}
1309
1310Value Context::materializeConversion(Type type, Value value, bool isSigned,
1311 Location loc) {
1312 if (type == value.getType())
1313 return value;
1314 auto dstPacked = dyn_cast<moore::PackedType>(type);
1315 auto srcPacked = dyn_cast<moore::PackedType>(value.getType());
1316
1317 // Resize the value if needed.
1318 if (dstPacked && srcPacked && dstPacked.getBitSize() &&
1319 srcPacked.getBitSize() &&
1320 *dstPacked.getBitSize() != *srcPacked.getBitSize()) {
1321 auto dstWidth = *dstPacked.getBitSize();
1322 auto srcWidth = *srcPacked.getBitSize();
1323
1324 // Convert the value to a simple bit vector which we can extend or truncate.
1325 auto srcWidthType = moore::IntType::get(value.getContext(), srcWidth,
1326 srcPacked.getDomain());
1327 if (value.getType() != srcWidthType)
1328 value = builder.create<moore::ConversionOp>(value.getLoc(), srcWidthType,
1329 value);
1330
1331 // Create truncation or sign/zero extension ops depending on the source and
1332 // destination width.
1333 auto dstWidthType = moore::IntType::get(value.getContext(), dstWidth,
1334 srcPacked.getDomain());
1335 if (dstWidth < srcWidth) {
1336 value = builder.create<moore::TruncOp>(loc, dstWidthType, value);
1337 } else if (dstWidth > srcWidth) {
1338 if (isSigned)
1339 value = builder.create<moore::SExtOp>(loc, dstWidthType, value);
1340 else
1341 value = builder.create<moore::ZExtOp>(loc, dstWidthType, value);
1342 }
1343 }
1344
1345 if (value.getType() != type)
1346 value = builder.create<moore::ConversionOp>(loc, type, value);
1347 return value;
1348}
1349
1350FailureOr<Value>
1351Context::convertSystemCallArity1(const slang::ast::SystemSubroutine &subroutine,
1352 Location loc, Value value) {
1353 auto systemCallRes =
1354 llvm::StringSwitch<std::function<FailureOr<Value>()>>(subroutine.name)
1355 // Signed and unsigned system functions.
1356 .Case("$signed", [&]() { return value; })
1357 .Case("$unsigned", [&]() { return value; })
1358
1359 // Math functions in SystemVerilog.
1360 .Case("$clog2",
1361 [&]() -> FailureOr<Value> {
1362 value = convertToSimpleBitVector(value);
1363 if (!value)
1364 return failure();
1365 return (Value)builder.create<moore::Clog2BIOp>(loc, value);
1366 })
1367 .Case("$ln",
1368 [&]() -> Value {
1369 return builder.create<moore::LnBIOp>(loc, value);
1370 })
1371 .Case("$log10",
1372 [&]() -> Value {
1373 return builder.create<moore::Log10BIOp>(loc, value);
1374 })
1375 .Case("$sin",
1376 [&]() -> Value {
1377 return builder.create<moore::SinBIOp>(loc, value);
1378 })
1379 .Case("$cos",
1380 [&]() -> Value {
1381 return builder.create<moore::CosBIOp>(loc, value);
1382 })
1383 .Case("$tan",
1384 [&]() -> Value {
1385 return builder.create<moore::TanBIOp>(loc, value);
1386 })
1387 .Case("$exp",
1388 [&]() -> Value {
1389 return builder.create<moore::ExpBIOp>(loc, value);
1390 })
1391 .Case("$sqrt",
1392 [&]() -> Value {
1393 return builder.create<moore::SqrtBIOp>(loc, value);
1394 })
1395 .Case("$floor",
1396 [&]() -> Value {
1397 return builder.create<moore::FloorBIOp>(loc, value);
1398 })
1399 .Case("$ceil",
1400 [&]() -> Value {
1401 return builder.create<moore::CeilBIOp>(loc, value);
1402 })
1403 .Case("$asin",
1404 [&]() -> Value {
1405 return builder.create<moore::AsinBIOp>(loc, value);
1406 })
1407 .Case("$acos",
1408 [&]() -> Value {
1409 return builder.create<moore::AcosBIOp>(loc, value);
1410 })
1411 .Case("$atan",
1412 [&]() -> Value {
1413 return builder.create<moore::AtanBIOp>(loc, value);
1414 })
1415 .Case("$sinh",
1416 [&]() -> Value {
1417 return builder.create<moore::SinhBIOp>(loc, value);
1418 })
1419 .Case("$cosh",
1420 [&]() -> Value {
1421 return builder.create<moore::CoshBIOp>(loc, value);
1422 })
1423 .Case("$tanh",
1424 [&]() -> Value {
1425 return builder.create<moore::TanhBIOp>(loc, value);
1426 })
1427 .Case("$asinh",
1428 [&]() -> Value {
1429 return builder.create<moore::AsinhBIOp>(loc, value);
1430 })
1431 .Case("$acosh",
1432 [&]() -> Value {
1433 return builder.create<moore::AcoshBIOp>(loc, value);
1434 })
1435 .Case("$atanh",
1436 [&]() -> Value {
1437 return builder.create<moore::AtanhBIOp>(loc, value);
1438 })
1439 .Default([&]() -> Value { return {}; });
1440 return systemCallRes();
1441}
assert(baseType &&"element must be base type")
static Value getSelectIndex(OpBuilder &builder, 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
void info(Twine message)
Definition LSPUtils.cpp:20
Domain
The number of values each bit of a type can assume.
Definition MooreTypes.h:47
@ FourValued
Four-valued types such as logic or integer.
@ TwoValued
Two-valued types such as bit or int.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
A helper class to facilitate the conversion from a Slang AST to MLIR operations.
Value materializeConversion(Type type, Value value, bool isSigned, Location loc)
Helper function to insert the necessary operations to cast a value from one type to another.
Value convertLvalueExpression(const slang::ast::Expression &expr)
Value materializeConstant(const slang::ConstantValue &constant, const slang::ast::Type &type, Location loc)
Helper function to materialize a ConstantValue as an SSA value.
slang::ConstantValue evaluateConstant(const slang::ast::Expression &expr)
Evaluate the constant value of an expression.
slang::ast::Compilation & compilation
OpBuilder builder
The builder used to create IR operations.
std::function< void(moore::ReadOp)> rvalueReadCallback
A listener called for every variable or net being read.
Type convertType(const slang::ast::Type &type, LocationAttr loc={})
Convert a slang type into an MLIR type.
Definition Types.cpp:167
Value materializeSVInt(const slang::SVInt &svint, const slang::ast::Type &type, Location loc)
Helper function to materialize an SVInt as an SSA value.
Value convertToBool(Value value)
Helper function to convert a value to its "truthy" boolean value.
Value convertRvalueExpression(const slang::ast::Expression &expr, Type requiredType={})
Value convertToSimpleBitVector(Value value)
Helper function to convert a value to its simple bit vector representation, if it has one.
FailureOr< Value > convertSystemCallArity1(const slang::ast::SystemSubroutine &subroutine, Location loc, Value value)
Convert system function calls only have arity-1.
FunctionLowering * declareFunction(const slang::ast::SubroutineSymbol &subroutine)
Convert a function and its arguments to a function declaration in the IR.
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.