//===- FormulaSerialization.cpp ---------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/Analysis/FlowSensitive/FormulaSerialization.h" #include "clang/Analysis/FlowSensitive/Arena.h" #include "clang/Analysis/FlowSensitive/Formula.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include namespace clang::dataflow { // Returns the leading indicator of operation formulas. `AtomRef` and `Literal` // are handled differently. static char compactSigil(Formula::Kind K) { switch (K) { case Formula::AtomRef: case Formula::Literal: // No sigil. return '\0'; case Formula::Not: return '!'; case Formula::And: return '&'; case Formula::Or: return '|'; case Formula::Implies: return '>'; case Formula::Equal: return '='; } llvm_unreachable("unhandled formula kind"); } void serializeFormula(const Formula &F, llvm::raw_ostream &OS) { switch (Formula::numOperands(F.kind())) { case 0: switch (F.kind()) { case Formula::AtomRef: OS << F.getAtom(); break; case Formula::Literal: OS << (F.literal() ? 'T' : 'F'); break; default: llvm_unreachable("unhandled formula kind"); } break; case 1: OS << compactSigil(F.kind()); serializeFormula(*F.operands()[0], OS); break; case 2: OS << compactSigil(F.kind()); serializeFormula(*F.operands()[0], OS); serializeFormula(*F.operands()[1], OS); break; default: llvm_unreachable("unhandled formula arity"); } } static llvm::Expected parsePrefix(llvm::StringRef &Str, Arena &A, llvm::DenseMap &AtomMap) { if (Str.empty()) return llvm::createStringError(llvm::inconvertibleErrorCode(), "unexpected end of input"); char Prefix = Str[0]; Str = Str.drop_front(); switch (Prefix) { case 'T': return &A.makeLiteral(true); case 'F': return &A.makeLiteral(false); case 'V': { unsigned AtomID; if (Str.consumeInteger(10, AtomID)) return llvm::createStringError(llvm::inconvertibleErrorCode(), "expected atom id"); auto [It, Inserted] = AtomMap.try_emplace(AtomID, Atom()); if (Inserted) It->second = A.makeAtom(); return &A.makeAtomRef(It->second); } case '!': { auto OperandOrErr = parsePrefix(Str, A, AtomMap); if (!OperandOrErr) return OperandOrErr.takeError(); return &A.makeNot(**OperandOrErr); } case '&': case '|': case '>': case '=': { auto LeftOrErr = parsePrefix(Str, A, AtomMap); if (!LeftOrErr) return LeftOrErr.takeError(); auto RightOrErr = parsePrefix(Str, A, AtomMap); if (!RightOrErr) return RightOrErr.takeError(); const Formula &LHS = **LeftOrErr; const Formula &RHS = **RightOrErr; switch (Prefix) { case '&': return &A.makeAnd(LHS, RHS); case '|': return &A.makeOr(LHS, RHS); case '>': return &A.makeImplies(LHS, RHS); case '=': return &A.makeEquals(LHS, RHS); default: llvm_unreachable("unexpected binary op"); } } default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "unexpected prefix character: %c", Prefix); } } llvm::Expected parseFormula(llvm::StringRef Str, Arena &A, llvm::DenseMap &AtomMap) { size_t OriginalSize = Str.size(); llvm::Expected F = parsePrefix(Str, A, AtomMap); if (!F) return F.takeError(); if (!Str.empty()) return llvm::createStringError(llvm::inconvertibleErrorCode(), ("unexpected suffix of length: " + llvm::Twine(Str.size() - OriginalSize)) .str()); return F; } } // namespace clang::dataflow