| // Copyright 2017-2021 The Verible Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "verilog/analysis/checkers/truncated_numeric_literal_rule.h" | |
| #include <cmath> | |
| #include <cstddef> | |
| #include <set> | |
| #include <string> | |
| #include "absl/numeric/int128.h" | |
| #include "absl/strings/numbers.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/string_view.h" | |
| #include "common/analysis/lint_rule_status.h" | |
| #include "common/analysis/matcher/bound_symbol_manager.h" | |
| #include "common/analysis/matcher/matcher.h" | |
| #include "common/text/concrete_syntax_leaf.h" | |
| #include "common/text/config_utils.h" | |
| #include "common/text/symbol.h" | |
| #include "common/text/syntax_tree_context.h" | |
| #include "common/text/token_info.h" | |
| #include "common/util/logging.h" | |
| #include "verilog/CST/numbers.h" | |
| #include "verilog/CST/verilog_matchers.h" | |
| #include "verilog/analysis/descriptions.h" | |
| #include "verilog/analysis/lint_rule_registry.h" | |
| namespace verilog { | |
| namespace analysis { | |
| using verible::down_cast; | |
| using verible::LintRuleStatus; | |
| using verible::LintViolation; | |
| using verible::SyntaxTreeContext; | |
| using verible::SyntaxTreeLeaf; | |
| using verible::SyntaxTreeNode; | |
| using verible::matcher::Matcher; | |
| VERILOG_REGISTER_LINT_RULE(TruncatedNumericLiteralRule); | |
| const LintRuleDescriptor& TruncatedNumericLiteralRule::GetDescriptor() { | |
| static const LintRuleDescriptor d{ | |
| .name = "truncated-numeric-literal", | |
| .topic = "number-literals", | |
| .desc = | |
| "Checks that numeric literals are not longer than their stated " | |
| "bit-width to avoid undesired accidental truncation.", | |
| }; | |
| return d; | |
| } | |
| static const Matcher& NumberMatcher() { | |
| static const Matcher matcher( | |
| NodekNumber(NumberHasConstantWidth().Bind("width"), | |
| NumberHasBasedLiteral().Bind("literal"))); | |
| return matcher; | |
| } | |
| // Given a binary/oct/hex digit, return how many bits it occupies | |
| static int digitBits(char digit, bool* is_lower_bound) { | |
| if (digit == 'z' || digit == 'x' || digit == '?') { | |
| *is_lower_bound = true; | |
| return 1; // Minimum number of bits assumed | |
| } | |
| *is_lower_bound = false; | |
| if (digit > '7') | |
| return 4; | |
| else if (digit > '3') | |
| return 3; | |
| else if (digit > '1') | |
| return 2; | |
| return 1; | |
| } | |
| static absl::string_view StripLeadingZeroes(absl::string_view str) { | |
| const absl::string_view::const_iterator it = | |
| std::find_if_not(str.begin(), str.end(), [](char c) { return c == '0'; }); | |
| return str.substr(it - str.begin()); | |
| } | |
| // Return count of bits the given number occupies. Sometims we can only make | |
| // a lower bound estimate, return that in "is_lower_bound". | |
| static size_t GetBitWidthOfNumber(const BasedNumber& n, bool* is_lower_bound) { | |
| const absl::string_view literal = StripLeadingZeroes(n.literal); | |
| *is_lower_bound = true; // Can only estimate for the following two | |
| if (literal.empty()) return 1; // all zeroes | |
| if (literal[0] == '`') return 1; // Not dealing with macros. | |
| *is_lower_bound = false; // Now, we strive for exact bits | |
| switch (n.base) { | |
| case 'h': | |
| return digitBits(literal[0], is_lower_bound) + 4 * (literal.length() - 1); | |
| case 'o': | |
| return digitBits(literal[0], is_lower_bound) + 3 * (literal.length() - 1); | |
| case 'b': | |
| return literal.length(); | |
| case 'd': { | |
| if (!isdigit(literal[0])) { | |
| *is_lower_bound = true; | |
| return 1; // Not dealing with ? or z | |
| } | |
| // Let's first try if we can parse it with regular means. Luckily, | |
| // absl provides compiler-independent abstraction of 128 bit numbers, | |
| // so we can parse most commonly used values accurately. | |
| absl::uint128 number; | |
| if (absl::SimpleAtoi(literal, &number)) { | |
| int bits; | |
| for (bits = 0; bits < 128 && number; ++bits) { | |
| number >>= 1; | |
| } | |
| return bits; | |
| } | |
| *is_lower_bound = true; // Heuristic below only gives us a lower bound | |
| // More than 128 bits. Best effort to establish at least a lower bound. | |
| // We parse the number as double, so that parsing can keep track of | |
| // pretty long numbers and we get log2 for 15-ish significant dec digits. | |
| // This will create false negatives, i.e. undercounting required bits, | |
| // TODO(hzeller): is there a cheap way to accurately determine bits used | |
| // without fully parsing the decimal number ? | |
| double v; | |
| if (absl::SimpleAtod(literal, &v) && !std::isinf(v)) { | |
| return std::max(129, static_cast<int>(ceil(log(v) / log(2)))); | |
| } | |
| // Uh, more than 300-ish decimal digits ? ... rough estimation it is. | |
| return ceil((literal.length() - 1) * log(10) / log(2)); | |
| } break; | |
| } | |
| return 0; // not reached. | |
| } | |
| void TruncatedNumericLiteralRule::HandleSymbol( | |
| const verible::Symbol& symbol, const SyntaxTreeContext& context) { | |
| verible::matcher::BoundSymbolManager manager; | |
| if (!NumberMatcher().Matches(symbol, &manager)) return; | |
| const auto* width_leaf = manager.GetAs<SyntaxTreeLeaf>("width"); | |
| const auto* literal_node = manager.GetAs<SyntaxTreeNode>("literal"); | |
| if (!width_leaf || !literal_node) return; | |
| const auto width_text = width_leaf->get().text(); | |
| size_t width; | |
| if (!absl::SimpleAtoi(width_text, &width)) return; | |
| const auto& base_digit_part = literal_node->children(); | |
| const auto* base_leaf = | |
| down_cast<const SyntaxTreeLeaf*>(base_digit_part[0].get()); | |
| const auto* digits_leaf = | |
| down_cast<const SyntaxTreeLeaf*>(base_digit_part[1].get()); | |
| const auto base_text = base_leaf->get().text(); | |
| const auto digits_text = digits_leaf->get().text(); | |
| const BasedNumber number(base_text, digits_text); | |
| bool is_lower_bound = false; | |
| const size_t actual_width = GetBitWidthOfNumber(number, &is_lower_bound); | |
| if (actual_width > width) { | |
| violations_.insert(LintViolation( | |
| digits_leaf->get(), | |
| absl::StrCat("Number ", width_text, base_text, digits_text, | |
| " occupies ", is_lower_bound ? "at least " : "", | |
| actual_width, " bits, truncated to ", width, " bits."), | |
| context)); | |
| // No autofix yet. In particular signed numbers might be hairy, and | |
| // numbers for which we only have a lower bound. | |
| } | |
| } | |
| LintRuleStatus TruncatedNumericLiteralRule::Report() const { | |
| return LintRuleStatus(violations_, GetDescriptor()); | |
| } | |
| } // namespace analysis | |
| } // namespace verilog |