clang 23.0.0git
FormulaSerialization.cpp
Go to the documentation of this file.
1//===- FormulaSerialization.cpp ---------------------------------*- 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
12#include "clang/Basic/LLVM.h"
13#include "llvm/ADT/DenseMap.h"
14#include "llvm/ADT/StringRef.h"
15#include "llvm/Support/Error.h"
16#include "llvm/Support/ErrorHandling.h"
17#include <cassert>
18#include <cstddef>
19#include <stack>
20#include <vector>
21
22namespace clang::dataflow {
23
24// Returns the leading indicator of operation formulas. `AtomRef` and `Literal`
25// are handled differently.
27 switch (K) {
30 // No sigil.
31 return '\0';
32 case Formula::Not:
33 return '!';
34 case Formula::And:
35 return '&';
36 case Formula::Or:
37 return '|';
39 return '>';
40 case Formula::Equal:
41 return '=';
42 }
43 llvm_unreachable("unhandled formula kind");
44}
45
46// Avoids recursion to avoid stack overflows from very large formulas.
47void serializeFormula(const Formula &F, llvm::raw_ostream &OS) {
48 std::stack<const Formula *> WorkList;
49 WorkList.push(&F);
50 while (!WorkList.empty()) {
51 const Formula *Current = WorkList.top();
52 WorkList.pop();
53 switch (Formula::numOperands(Current->kind())) {
54 case 0:
55 switch (Current->kind()) {
57 OS << Current->getAtom();
58 break;
60 OS << (Current->literal() ? 'T' : 'F');
61 break;
62 default:
63 llvm_unreachable("unhandled formula kind");
64 }
65 break;
66 case 1:
67 OS << compactSigil(Current->kind());
68 WorkList.push(Current->operands()[0]);
69 break;
70 case 2:
71 OS << compactSigil(Current->kind());
72 WorkList.push(Current->operands()[1]);
73 WorkList.push(Current->operands()[0]);
74 break;
75 default:
76 llvm_unreachable("unhandled formula arity");
77 }
78 }
79}
80
81struct Operation {
85 std::vector<const Formula *> Operands;
86};
87
88// Avoids recursion to avoid stack overflows from very large formulas.
90parseFormulaInternal(llvm::StringRef &Str, Arena &A,
91 llvm::DenseMap<unsigned, Atom> &AtomMap) {
92 std::stack<Operation> ActiveOperations;
93
94 while (true) {
95 if (ActiveOperations.empty() ||
96 ActiveOperations.top().ExpectedNumOperands >
97 ActiveOperations.top().Operands.size()) {
98 if (Str.empty()) {
99 return llvm::createStringError(llvm::inconvertibleErrorCode(),
100 "unexpected end of input");
101 }
102 char Prefix = Str[0];
103 Str = Str.drop_front();
104
105 switch (Prefix) {
106 // Terminals
107 case 'T':
108 case 'F':
109 case 'V': {
110 const Formula *TerminalFormula;
111 switch (Prefix) {
112 case 'T':
113 TerminalFormula = &A.makeLiteral(true);
114 break;
115 case 'F':
116 TerminalFormula = &A.makeLiteral(false);
117 break;
118 case 'V': {
119 unsigned AtomID;
120 if (Str.consumeInteger(10, AtomID))
121 return llvm::createStringError(llvm::inconvertibleErrorCode(),
122 "expected atom id");
123 auto [It, Inserted] = AtomMap.try_emplace(AtomID, Atom());
124 if (Inserted)
125 It->second = A.makeAtom();
126 TerminalFormula = &A.makeAtomRef(It->second);
127 break;
128 }
129 default:
130 llvm_unreachable("unexpected terminal character");
131 }
132 if (ActiveOperations.empty()) {
133 return TerminalFormula;
134 }
135 Operation *Op = &ActiveOperations.top();
136 Op->Operands.push_back(TerminalFormula);
137 } break;
138 case '!':
139 ActiveOperations.emplace(Formula::Kind::Not);
140 break;
141 case '&':
142 ActiveOperations.emplace(Formula::Kind::And);
143 break;
144 case '|':
145 ActiveOperations.emplace(Formula::Kind::Or);
146 break;
147 case '>':
148 ActiveOperations.emplace(Formula::Kind::Implies);
149 break;
150 case '=':
151 ActiveOperations.emplace(Formula::Kind::Equal);
152 break;
153 default:
154 return llvm::createStringError(llvm::inconvertibleErrorCode(),
155 "unexpected prefix character: %c",
156 Prefix);
157 }
158 } else if (!ActiveOperations.empty() &&
159 ActiveOperations.top().ExpectedNumOperands ==
160 ActiveOperations.top().Operands.size()) {
161 Operation *Op = &ActiveOperations.top();
162 const Formula *OpFormula = nullptr;
163 switch (Op->Kind) {
165 OpFormula = &A.makeNot(*Op->Operands[0]);
166 break;
168 OpFormula = &A.makeAnd(*Op->Operands[0], *Op->Operands[1]);
169 break;
171 OpFormula = &A.makeOr(*Op->Operands[0], *Op->Operands[1]);
172 break;
174 OpFormula = &A.makeImplies(*Op->Operands[0], *Op->Operands[1]);
175 break;
177 OpFormula = &A.makeEquals(*Op->Operands[0], *Op->Operands[1]);
178 break;
179 default:
180 return llvm::createStringError(llvm::inconvertibleErrorCode(),
181 "only unary and binary operations are "
182 "expected, but got Formula::Kind %u",
183 Op->Kind);
184 }
185 ActiveOperations.pop();
186 if (ActiveOperations.empty())
187 return OpFormula;
188 Op = &ActiveOperations.top();
189 Op->Operands.push_back(OpFormula);
190 } else {
191 llvm_unreachable(
192 "we should never have added more operands than expected");
193 }
194 }
195}
196
198parseFormula(llvm::StringRef Str, Arena &A,
199 llvm::DenseMap<unsigned, Atom> &AtomMap) {
200 size_t OriginalSize = Str.size();
202 if (!F)
203 return F.takeError();
204 if (!Str.empty())
205 return llvm::createStringError(llvm::inconvertibleErrorCode(),
206 ("unexpected suffix of length: " +
207 llvm::Twine(Str.size() - OriginalSize))
208 .str());
209 return F;
210}
211
212} // namespace clang::dataflow
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
The Arena owns the objects that model data within an analysis.
Definition Arena.h:21
const Formula & makeEquals(const Formula &LHS, const Formula &RHS)
Returns a formula for LHS <=> RHS.
Definition Arena.cpp:91
const Formula & makeAtomRef(Atom A)
Returns a formula for the variable A.
Definition Arena.cpp:34
Atom makeAtom()
Returns a new atomic boolean variable, distinct from any other.
Definition Arena.h:118
const Formula & makeNot(const Formula &Val)
Returns a formula for the negation of Val.
Definition Arena.cpp:67
const Formula & makeOr(const Formula &LHS, const Formula &RHS)
Returns a formula for the disjunction of LHS and RHS.
Definition Arena.cpp:54
const Formula & makeAnd(const Formula &LHS, const Formula &RHS)
Returns a formula for the conjunction of LHS and RHS.
Definition Arena.cpp:41
const Formula & makeImplies(const Formula &LHS, const Formula &RHS)
Returns a formula for LHS => RHS.
Definition Arena.cpp:78
const Formula & makeLiteral(bool Value)
Returns a formula for a literal true/false.
Definition Arena.h:111
ArrayRef< const Formula * > operands() const
Definition Formula.h:82
Atom getAtom() const
Definition Formula.h:68
@ Equal
True if LHS is false or RHS is true.
Definition Formula.h:64
@ Implies
True if either LHS or RHS is true.
Definition Formula.h:63
@ AtomRef
A reference to an atomic boolean variable.
Definition Formula.h:54
@ Literal
Constant true or false.
Definition Formula.h:56
@ Or
True if LHS and RHS are both true.
Definition Formula.h:62
@ And
True if its only operand is false.
Definition Formula.h:61
bool literal() const
Definition Formula.h:73
Kind kind() const
Definition Formula.h:66
static unsigned numOperands(Kind K)
Count of operands (sub-formulas) associated with Formulas of kind K.
Definition Formula.h:99
Dataflow Directional Tag Classes.
Definition AdornedCFG.h:29
Atom
Identifies an atomic boolean variable such as "V1".
Definition Formula.h:34
static llvm::Expected< const Formula * > parseFormulaInternal(llvm::StringRef &Str, Arena &A, llvm::DenseMap< unsigned, Atom > &AtomMap)
void serializeFormula(const Formula &F, llvm::raw_ostream &OS)
Prints F to OS in a compact format, optimized for easy parsing (deserialization) rather than human us...
llvm::Expected< const Formula * > parseFormula(llvm::StringRef Str, Arena &A, llvm::DenseMap< unsigned, Atom > &AtomMap)
Parses Str to build a serialized Formula.
static char compactSigil(Formula::Kind K)
std::vector< const Formula * > Operands