blob: df15a1d6eaadb878737309e07ec7b003aa1f9ba7 [file] [log] [blame]
//===- 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 <cassert>
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<const Formula *>
parsePrefix(llvm::StringRef &Str, Arena &A,
llvm::DenseMap<unsigned, Atom> &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<const Formula *>
parseFormula(llvm::StringRef Str, Arena &A,
llvm::DenseMap<unsigned, Atom> &AtomMap) {
size_t OriginalSize = Str.size();
llvm::Expected<const Formula *> 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