CIRCT  20.0.0git
FVInt.cpp
Go to the documentation of this file.
1 //===- FVInt.cpp - Four-valued integer --------------------------*- C++ -*-===//
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 
9 #include "circt/Support/FVInt.h"
10 #include "mlir/IR/OpImplementation.h"
11 #include "llvm/ADT/StringExtras.h"
12 
13 #define DEBUG_TYPE "fvint"
14 
15 using namespace circt;
16 
17 std::optional<FVInt> FVInt::tryFromString(StringRef str, unsigned radix) {
18  assert(radix == 2 || radix == 8 || radix == 10 || radix == 16);
19  if (str.empty())
20  return {};
21 
22  // Overestimate the number of bits that will be needed to hold all digits.
23  unsigned radixLog2 = 0;
24  for (unsigned r = radix - 1; r > 0; r >>= 1)
25  ++radixLog2;
26  bool radixIsPow2 = (radix == (1U << radixLog2));
27 
28  // Parse the string.
29  auto result = FVInt::getZero(str.size() * radixLog2);
30  while (!str.empty()) {
31  unsigned digit = llvm::toLower(str[0]);
32  str = str.drop_front();
33 
34  // Handle X and Z digits.
35  if (digit == 'x' || digit == 'z') {
36  if (!radixIsPow2)
37  return {};
38  result <<= radixLog2;
39  result.unknown.setLowBits(radixLog2);
40  if (digit == 'z')
41  result.value.setLowBits(radixLog2);
42  continue;
43  }
44 
45  // Determine the value of the current digit.
46  if (digit >= '0' && digit <= '9')
47  digit = digit - '0';
48  else if (digit >= 'a' && digit <= 'z')
49  digit = digit - 'a' + 10;
50  else
51  return {};
52  if (digit >= radix)
53  return {};
54 
55  // Add the digit to the result.
56  if (radixIsPow2) {
57  result <<= radixLog2;
58  result.value |= digit;
59  } else {
60  result.value *= radix;
61  result.value += digit;
62  }
63  }
64 
65  return result;
66 }
67 
68 bool FVInt::tryToString(SmallVectorImpl<char> &str, unsigned radix,
69  bool uppercase) const {
70  size_t strBaseLen = str.size();
71  assert(radix == 2 || radix == 8 || radix == 10 || radix == 16);
72 
73  // Determine if the radix is a power of two.
74  unsigned radixLog2 = 0;
75  for (unsigned r = radix - 1; r > 0; r >>= 1)
76  ++radixLog2;
77  bool radixIsPow2 = (radix == (1U << radixLog2));
78  unsigned radixMask = (1U << radixLog2) - 1;
79 
80  // If the number has no X or Z bits, take the easy route and print the `APInt`
81  // directly.
82  if (!hasUnknown()) {
83  value.toString(str, radix, /*Signed=*/false, /*formatAsCLiteral=*/false,
84  uppercase);
85  return true;
86  }
87 
88  // We can only print with non-power-of-two radices if there are no X or Z
89  // bits. So at this point we require radix be a power of two.
90  if (!radixIsPow2)
91  return false;
92 
93  // Otherwise chop off digits at the bottom and print them to the string. This
94  // prints the digits in reverse order, with the least significant digit as the
95  // first character.
96  APInt value = this->value;
97  APInt unknown = this->unknown;
98 
99  char chrA = uppercase ? 'A' : 'a';
100  char chrX = uppercase ? 'X' : 'x';
101  char chrZ = uppercase ? 'Z' : 'z';
102 
103  while (!value.isZero() || !unknown.isZero()) {
104  unsigned digitValue = value.getRawData()[0] & radixMask;
105  unsigned digitUnknown = unknown.getRawData()[0] & radixMask;
106  unsigned shiftAmount = std::min(radixLog2, getBitWidth());
107  value.lshrInPlace(shiftAmount);
108  unknown.lshrInPlace(shiftAmount);
109 
110  // Handle unknown bits. Since we only get to print a single X or Z character
111  // to the string, either all bits in the digit have to be X, or all have to
112  // be Z. But we cannot represent the case where X, Z and 0/1 bits are mixed.
113  if (digitUnknown != 0) {
114  if (digitUnknown != radixMask ||
115  (digitValue != 0 && digitValue != radixMask)) {
116  str.resize(strBaseLen);
117  return false;
118  }
119  str.push_back(digitValue == 0 ? chrX : chrZ);
120  continue;
121  }
122 
123  // Handle known bits.
124  if (digitValue < 10)
125  str.push_back(digitValue + '0');
126  else
127  str.push_back(digitValue - 10 + chrA);
128  }
129 
130  // Reverse the digits.
131  std::reverse(str.begin() + strBaseLen, str.end());
132  return true;
133 }
134 
135 void FVInt::print(raw_ostream &os) const {
136  SmallString<32> buffer;
137  if (!tryToString(buffer))
138  if (!tryToString(buffer, 16))
139  tryToString(buffer, 2);
140  os << buffer;
141 }
142 
143 llvm::hash_code circt::hash_value(const FVInt &a) {
145 }
146 
147 void circt::printFVInt(AsmPrinter &p, const FVInt &value) {
148  SmallString<32> buffer;
149  if (value.getBitWidth() > 1 && value.isNegative() &&
150  (-value).tryToString(buffer)) {
151  p << "-" << buffer;
152  } else if (value.tryToString(buffer)) {
153  p << buffer;
154  } else if (value.tryToString(buffer, 16)) {
155  p << "h" << buffer;
156  } else {
157  value.tryToString(buffer, 2);
158  p << "b" << buffer;
159  }
160 }
161 
162 ParseResult circt::parseFVInt(AsmParser &p, FVInt &result) {
163  // Parse the value as either a keyword (`b[01XZ]+` for binary or
164  // `h[0-9A-FXZ]+` for hexadecimal), or an integer value (for decimal).
165  FVInt value;
166  StringRef strValue;
167  auto valueLoc = p.getCurrentLocation();
168  if (succeeded(p.parseOptionalKeyword(&strValue))) {
169  // Determine the radix based on the `b` or `h` prefix.
170  unsigned base = 0;
171  if (strValue.consume_front("b")) {
172  base = 2;
173  } else if (strValue.consume_front("h")) {
174  base = 16;
175  } else {
176  return p.emitError(valueLoc) << "expected `b` or `h` prefix";
177  }
178 
179  // Parse the value.
180  auto parsedValue = FVInt::tryFromString(strValue, base);
181  if (!parsedValue) {
182  return p.emitError(valueLoc)
183  << "expected base-" << base << " four-valued integer";
184  }
185 
186  // Add a zero bit at the top to ensure the value reads as positive.
187  result = parsedValue->zext(parsedValue->getBitWidth() + 1);
188  } else {
189  APInt intValue;
190  if (p.parseInteger(intValue))
191  return failure();
192  result = std::move(intValue);
193  }
194  return success();
195 }
196 
197 unsigned DenseMapInfo<FVInt, void>::getHashValue(const FVInt &Key) {
198  return static_cast<unsigned>(hash_value(Key));
199 }
assert(baseType &&"element must be base type")
Four-valued arbitrary precision integers.
Definition: FVInt.h:37
static FVInt getZero(unsigned numBits)
Construct an FVInt with all bits set to 0.
Definition: FVInt.h:61
APInt value
Definition: FVInt.h:627
bool isNegative() const
Determine whether the integer interpreted as a signed number would be negative.
Definition: FVInt.h:181
APInt unknown
Definition: FVInt.h:628
static std::optional< FVInt > tryFromString(StringRef str, unsigned radix=10)
Convert a string into an FVInt.
Definition: FVInt.cpp:17
const APInt & getRawUnknown() const
Return the underlying APInt used to store whether a bit is unknown (X or Z).
Definition: FVInt.h:107
void print(raw_ostream &os) const
Print an FVInt to an output stream.
Definition: FVInt.cpp:135
bool hasUnknown() const
Determine if any bits are X or Z.
Definition: FVInt.h:164
FVInt zext(unsigned bitWidth) const
Zero-extend the integer to a new bit width.
Definition: FVInt.h:135
unsigned getBitWidth() const
Return the number of bits this integer has.
Definition: FVInt.h:81
const APInt & getRawValue() const
Return the underlying APInt used to store whether a bit is 0/X or 1/Z.
Definition: FVInt.h:103
bool tryToString(SmallVectorImpl< char > &str, unsigned radix=10, bool uppercase=true) const
Convert an FVInt to a string.
Definition: FVInt.cpp:68
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
inline ::llvm::hash_code hash_value(const FieldRef &fieldRef)
Get a hash code for a FieldRef.
Definition: FieldRef.h:92
void printFVInt(AsmPrinter &p, const FVInt &value)
Print a four-valued integer usign an AsmPrinter.
Definition: FVInt.cpp:147
ParseResult parseFVInt(AsmParser &p, FVInt &result)
Parse a four-valued integer using an AsmParser.
Definition: FVInt.cpp:162
size_t hash_combine(size_t h1, size_t h2)
C++'s stdlib doesn't have a hash_combine function. This is a simple one.
Definition: Utils.h:32