|
8 | 8 | #include "slang/ast/LSPUtilities.h" |
9 | 9 |
|
10 | 10 | #include "slang/ast/ASTVisitor.h" |
| 11 | +#include "slang/ast/EvalContext.h" |
| 12 | +#include "slang/ast/TypeProvider.h" |
11 | 13 |
|
12 | 14 | namespace slang::ast { |
13 | 15 |
|
@@ -62,17 +64,13 @@ void LSPUtilities::stringifyLSP(const Expression& expr, EvalContext& evalContext |
62 | 64 | } |
63 | 65 | } |
64 | 66 |
|
65 | | -std::optional<DriverBitRange> LSPUtilities::getBounds(const Expression& prefixExpression, |
66 | | - EvalContext& evalContext, |
67 | | - const Type& rootType) { |
| 67 | +static std::optional<DriverBitRange> computeBounds(SmallVector<const Expression*>& path, |
| 68 | + size_t skip, EvalContext& evalContext, |
| 69 | + const Type& rootType) { |
68 | 70 | auto type = &rootType.getCanonicalType(); |
69 | 71 | DriverBitRange result{0, type->getSelectableWidth() - 1}; |
70 | 72 |
|
71 | | - SmallVector<const Expression*> path; |
72 | | - visitComponents(prefixExpression, /* includeRoot */ false, |
73 | | - [&](const Expression& expr) { path.push_back(&expr); }); |
74 | | - |
75 | | - for (size_t i = path.size(); i > 0; i--) { |
| 73 | + for (size_t i = path.size() - skip; i > 0; i--) { |
76 | 74 | uint64_t start, width; |
77 | 75 | auto& elem = *path[i - 1]; |
78 | 76 | if (elem.kind == ExpressionKind::MemberAccess) { |
@@ -112,29 +110,14 @@ std::optional<DriverBitRange> LSPUtilities::getBounds(const Expression& prefixEx |
112 | 110 | return result; |
113 | 111 | } |
114 | 112 |
|
115 | | -static Expression* translateSelector(BumpAllocator& alloc, Expression& value, |
116 | | - const Expression& sourceSelector) { |
117 | | - // TODO: need to translate selector indices because types may be only |
118 | | - // equivalent and not matching. |
119 | | - switch (sourceSelector.kind) { |
120 | | - case ExpressionKind::ElementSelect: { |
121 | | - auto& es = sourceSelector.as<ElementSelectExpression>(); |
122 | | - return alloc.emplace<ElementSelectExpression>(*es.type, value, es.selector(), |
123 | | - es.sourceRange); |
124 | | - } |
125 | | - case ExpressionKind::RangeSelect: { |
126 | | - auto& rs = sourceSelector.as<RangeSelectExpression>(); |
127 | | - return alloc.emplace<RangeSelectExpression>(rs.getSelectionKind(), *rs.type, value, |
128 | | - rs.left(), rs.right(), rs.sourceRange); |
129 | | - } |
130 | | - case ExpressionKind::MemberAccess: { |
131 | | - auto& ma = sourceSelector.as<MemberAccessExpression>(); |
132 | | - return alloc.emplace<MemberAccessExpression>(*ma.type, value, ma.member, |
133 | | - ma.sourceRange); |
134 | | - } |
135 | | - default: |
136 | | - SLANG_UNREACHABLE; |
137 | | - } |
| 113 | +std::optional<DriverBitRange> LSPUtilities::getBounds(const Expression& prefixExpression, |
| 114 | + EvalContext& evalContext, |
| 115 | + const Type& rootType) { |
| 116 | + SmallVector<const Expression*> path; |
| 117 | + visitComponents(prefixExpression, /* includeRoot */ false, |
| 118 | + [&](const Expression& expr) { path.push_back(&expr); }); |
| 119 | + |
| 120 | + return computeBounds(path, 0, evalContext, rootType); |
138 | 121 | } |
139 | 122 |
|
140 | 123 | static bool expandRefPortConn(BumpAllocator& alloc, EvalContext& evalContext, const Expression& lsp, |
@@ -180,10 +163,61 @@ static bool expandRefPortConn(BumpAllocator& alloc, EvalContext& evalContext, co |
180 | 163 | // glue the uncovered portion of the LSP onto the external connection. |
181 | 164 | // The const_cast here is okay because we never mutate anything during analysis. |
182 | 165 | auto newExpr = const_cast<Expression*>(&externalConn); |
183 | | - for (size_t i = elemsToRemove; i < lspPath.size(); i++) { |
184 | | - auto elem = lspPath[lspPath.size() - 1 - i]; |
185 | | - newExpr = translateSelector(alloc, *newExpr, *elem); |
| 166 | + |
| 167 | + // First, replicate all of the selects for unpacked types. The only way that |
| 168 | + // types can mismatch here is for fixed size arrays, which can have differing |
| 169 | + // ranges, so translate those along the way. |
| 170 | + for (; elemsToRemove < lspPath.size(); elemsToRemove++) { |
| 171 | + auto& ct = newExpr->type->getCanonicalType(); |
| 172 | + if (ct.isIntegral()) |
| 173 | + break; |
| 174 | + |
| 175 | + auto elem = lspPath[lspPath.size() - 1 - elemsToRemove]; |
| 176 | + if (elem->kind == ExpressionKind::MemberAccess) { |
| 177 | + auto& ma = elem->as<MemberAccessExpression>(); |
| 178 | + newExpr = alloc.emplace<MemberAccessExpression>(*ma.type, *newExpr, ma.member, |
| 179 | + ma.sourceRange); |
| 180 | + continue; |
| 181 | + } |
| 182 | + |
| 183 | + auto targetDim = ct.getFixedRange(); |
| 184 | + auto translateIndex = [&](int32_t index) { |
| 185 | + if (targetDim.isLittleEndian()) |
| 186 | + return targetDim.upper() - index; |
| 187 | + else |
| 188 | + return targetDim.lower() + index; |
| 189 | + }; |
| 190 | + |
| 191 | + auto selection = elem->evalSelector(evalContext, /* enforceBounds */ true); |
| 192 | + SLANG_ASSERT(selection); |
| 193 | + |
| 194 | + selection->left = translateIndex(selection->left); |
| 195 | + selection->right = translateIndex(selection->right); |
| 196 | + |
| 197 | + if (elem->kind == ExpressionKind::ElementSelect) { |
| 198 | + newExpr = &ElementSelectExpression::fromConstant(alloc, *newExpr, |
| 199 | + selection->lower(), |
| 200 | + evalContext.astCtx); |
| 201 | + } |
| 202 | + else { |
| 203 | + newExpr = &RangeSelectExpression::fromConstant(alloc, *newExpr, *selection, |
| 204 | + evalContext.astCtx); |
| 205 | + } |
186 | 206 | } |
| 207 | + |
| 208 | + // For remaining integral path components, compute the bounds and then |
| 209 | + // recreate a corresponding select tree that achieves those same bounds. |
| 210 | + if (elemsToRemove < lspPath.size()) { |
| 211 | + auto bounds = computeBounds(lspPath, elemsToRemove, evalContext, *newExpr->type); |
| 212 | + SLANG_ASSERT(bounds); |
| 213 | + |
| 214 | + // Note that this can't overflow here because it's a packed type |
| 215 | + // so the total width is bounded. |
| 216 | + ConstantRange range{int32_t(bounds->second), int32_t(bounds->first)}; |
| 217 | + newExpr = &Expression::buildPackedSelectTree(alloc, *newExpr, range, |
| 218 | + evalContext.astCtx); |
| 219 | + } |
| 220 | + |
187 | 221 | LSPUtilities::visitLSPs(*newExpr, evalContext, callback, isLValue); |
188 | 222 | } |
189 | 223 | return true; |
@@ -268,8 +302,7 @@ void LSPUtilities::expandIndirectLSP(BumpAllocator& alloc, EvalContext& evalCont |
268 | 302 |
|
269 | 303 | if (!anyRefPorts) { |
270 | 304 | // No ref ports or modports involved, so just invoke the callback directly. |
271 | | - // TODO: |
272 | | - // callback(symbol, lsp, isLValue); |
| 305 | + callback(symbol, lsp, isLValue); |
273 | 306 | } |
274 | 307 | } |
275 | 308 |
|
|
0 commit comments