69#define PASS_KEY "x86-lvi-load"
70#define DEBUG_TYPE PASS_KEY
72STATISTIC(NumFences,
"Number of LFENCEs inserted for LVI mitigation");
73STATISTIC(NumFunctionsConsidered,
"Number of functions analyzed");
74STATISTIC(NumFunctionsMitigated,
"Number of functions for which mitigations "
76STATISTIC(NumGadgets,
"Number of LVI gadgets detected during analysis");
84 cl::desc(
"Don't treat conditional branches as disclosure gadgets. This "
85 "may improve performance, at the cost of security."),
91 "For each function, emit a dot graph depicting potential LVI gadgets"),
96 cl::desc(
"For each function, emit a dot graph depicting potential LVI "
97 "gadgets, and do not insert any fences"),
102 cl::desc(
"For each function, emit a dot graph to stdout depicting "
103 "potential LVI gadgets, used for testing purposes only"),
107typedef int (*
OptimizeCutT)(
unsigned int *Nodes,
unsigned int NodesSize,
108 unsigned int *Edges,
int *EdgeValues,
109 int *CutEdges ,
unsigned int EdgesSize);
115 static constexpr int GadgetEdgeSentinel = -1;
116 static constexpr MachineInstr *
const ArgNodeSentinel =
nullptr;
119 using Node =
typename GraphT::Node;
120 using Edge =
typename GraphT::Edge;
122 MachineGadgetGraph(std::unique_ptr<
Node[]> Nodes,
123 std::unique_ptr<Edge[]> Edges, size_type NodesSize,
124 size_type EdgesSize,
int NumFences = 0,
int NumGadgets = 0)
125 : GraphT(std::move(Nodes), std::move(Edges), NodesSize, EdgesSize),
126 NumFences(NumFences), NumGadgets(NumGadgets) {}
127 static inline bool isCFGEdge(
const Edge &
E) {
128 return E.getValue() != GadgetEdgeSentinel;
130 static inline bool isGadgetEdge(
const Edge &
E) {
131 return E.getValue() == GadgetEdgeSentinel;
142 return "X86 Load Value Injection (LVI) Load Hardening";
151 using Edge = MachineGadgetGraph::Edge;
152 using Node = MachineGadgetGraph::Node;
153 using EdgeSet = MachineGadgetGraph::EdgeSet;
154 using NodeSet = MachineGadgetGraph::NodeSet;
160 std::unique_ptr<MachineGadgetGraph>
165 std::unique_ptr<MachineGadgetGraph> Graph)
const;
167 std::unique_ptr<MachineGadgetGraph> Graph)
const;
168 int elimMitigatedEdgesAndNodes(MachineGadgetGraph &
G,
171 std::unique_ptr<MachineGadgetGraph>
172 trimMitigatedEdges(std::unique_ptr<MachineGadgetGraph> Graph)
const;
174 EdgeSet &CutEdges )
const;
175 bool instrUsesRegToAccessMemory(
const MachineInstr &
I,
unsigned Reg)
const;
176 bool instrUsesRegToBranch(
const MachineInstr &
I,
unsigned Reg)
const;
178 return MI && (
MI->getOpcode() == X86::LFENCE ||
179 (STI->useLVIControlFlowIntegrity() &&
MI->isCall()));
203 if (Node->getValue() == MachineGadgetGraph::ArgNodeSentinel)
208 OS << *Node->getValue();
214 if (
MI == MachineGadgetGraph::ArgNodeSentinel)
215 return "color = blue";
216 if (
MI->getOpcode() == X86::LFENCE)
217 return "color = green";
223 int EdgeVal = (*
E.getCurrent()).getValue();
224 return EdgeVal >= 0 ?
"label = " + std::to_string(EdgeVal)
225 :
"color = red, style = \"dashed\"";
231constexpr MachineInstr *MachineGadgetGraph::ArgNodeSentinel;
232constexpr int MachineGadgetGraph::GadgetEdgeSentinel;
234char X86LoadValueInjectionLoadHardeningPass::ID = 0;
236void X86LoadValueInjectionLoadHardeningPass::getAnalysisUsage(
246 MachineGadgetGraph *
G) {
248 "Speculative gadgets for \"" + MF.
getName() +
"\" function");
251bool X86LoadValueInjectionLoadHardeningPass::runOnMachineFunction(
256 if (!STI->useLVILoadHardening())
265 if (!
F.hasOptNone() && skipFunction(
F))
268 ++NumFunctionsConsidered;
269 TII = STI->getInstrInfo();
270 TRI = STI->getRegisterInfo();
272 const auto &MLI = getAnalysis<MachineLoopInfo>();
273 const auto &MDT = getAnalysis<MachineDominatorTree>();
274 const auto &MDF = getAnalysis<MachineDominanceFrontier>();
275 std::unique_ptr<MachineGadgetGraph> Graph = getGadgetGraph(MF, MLI, MDT, MDF);
277 if (Graph ==
nullptr)
288 std::string FileName =
"lvi.";
304 std::string ErrorMsg;
307 if (!ErrorMsg.empty())
314 FencesInserted = hardenLoadsWithPlugin(MF, std::move(Graph));
316 FencesInserted = hardenLoadsWithHeuristic(MF, std::move(Graph));
319 if (FencesInserted > 0)
320 ++NumFunctionsMitigated;
321 NumFences += FencesInserted;
322 return (FencesInserted > 0);
325std::unique_ptr<MachineGadgetGraph>
326X86LoadValueInjectionLoadHardeningPass::getGadgetGraph(
333 DataFlowGraph DFG{MF, *
TII, *
TRI, MDT, MDF};
338 GraphBuilder Builder;
339 using GraphIter =
typename GraphBuilder::BuilderNodeRef;
341 int FenceCount = 0, GadgetCount = 0;
344 if (
Ref == NodeMap.
end()) {
345 auto I = Builder.addVertex(
MI);
347 return std::pair<GraphIter, bool>{
I,
true};
349 return std::pair<GraphIter, bool>{
Ref->getSecond(),
false};
360 auto AnalyzeDef = [&](NodeAddr<DefNode *> SourceDef) {
362 std::function<void(NodeAddr<DefNode *>)> AnalyzeDefUseChain =
363 [&](NodeAddr<DefNode *>
Def) {
369 RegisterRef DefReg =
Def.Addr->getRegRef(DFG);
370 for (
auto UseID :
L.getAllReachedUses(DefReg, Def)) {
371 auto Use = DFG.addr<UseNode *>(UseID);
372 if (
Use.Addr->getFlags() & NodeAttrs::PhiRef) {
373 NodeAddr<PhiNode *>
Phi =
Use.Addr->getOwner(DFG);
374 for (
const auto&
I :
L.getRealUses(
Phi.Id)) {
375 if (DFG.getPRI().alias(RegisterRef(
I.first), DefReg)) {
376 for (
const auto &UA :
I.second)
377 Uses.emplace(UA.first);
388 for (
auto UseID :
Uses) {
389 if (!UsesVisited.
insert(UseID).second)
392 auto Use = DFG.addr<UseNode *>(UseID);
393 assert(!(
Use.Addr->getFlags() & NodeAttrs::PhiRef));
406 if (instrUsesRegToAccessMemory(
UseMI, UseMO.
getReg()) ||
409 Transmitters[
Def.Id].push_back(
Use.Addr->getOwner(DFG).Id);
417 NodeAddr<InstrNode *> Owner{
Use.Addr->getOwner(DFG)};
419 for (
const auto &ChildDef :
420 Owner.Addr->members_if(DataFlowGraph::IsDef, DFG)) {
421 if (!DefsVisited.
insert(ChildDef.Id).second)
423 if (
Def.Addr->getAttrs() & NodeAttrs::Dead)
425 if (
Def.Id == ChildDef.Id)
428 AnalyzeDefUseChain(ChildDef);
431 for (
auto TransmitterId : Transmitters[ChildDef.Id])
432 Transmitters[
Def.Id].push_back(TransmitterId);
438 auto &DefTransmitters = Transmitters[
Def.Id];
442 DefTransmitters.erase(
443 std::unique(DefTransmitters.begin(), DefTransmitters.end()),
444 DefTransmitters.end());
448 AnalyzeDefUseChain(SourceDef);
449 auto &SourceDefTransmitters = Transmitters[SourceDef.Id];
450 if (SourceDefTransmitters.empty())
454 ? MachineGadgetGraph::ArgNodeSentinel
456 auto GadgetSource = MaybeAddNode(Source);
458 for (
auto TransmitterId : SourceDefTransmitters) {
460 auto GadgetSink = MaybeAddNode(Sink);
462 Builder.addEdge(MachineGadgetGraph::GadgetEdgeSentinel,
463 GadgetSource.first, GadgetSink.first);
468 LLVM_DEBUG(
dbgs() <<
"Analyzing def-use chains to find gadgets\n");
470 NodeAddr<BlockNode *> EntryBlock = DFG.getFunc().Addr->getEntryBlock(DFG);
471 for (NodeAddr<PhiNode *> ArgPhi :
472 EntryBlock.Addr->members_if(DataFlowGraph::IsPhi, DFG)) {
473 NodeList Defs = ArgPhi.Addr->members_if(DataFlowGraph::IsDef, DFG);
477 for (NodeAddr<BlockNode *> BA : DFG.getFunc().Addr->members(DFG)) {
478 for (NodeAddr<StmtNode *> SA :
479 BA.Addr->members_if(DataFlowGraph::IsCode<NodeAttrs::Stmt>, DFG)) {
484 }
else if (
MI->mayLoad()) {
485 NodeList Defs = SA.Addr->members_if(DataFlowGraph::IsDef, DFG);
492 if (GadgetCount == 0)
494 NumGadgets += GadgetCount;
504 auto BeginBB = MaybeAddNode(&*NI);
505 Builder.addEdge(ParentDepth, GI, BeginBB.first);
511 while (++NI !=
MBB->
end()) {
513 if (
Ref != NodeMap.
end()) {
514 Builder.addEdge(LoopDepth, GI,
Ref->getSecond());
515 GI =
Ref->getSecond();
522 auto EndBB = MaybeAddNode(&*
T);
524 Builder.addEdge(LoopDepth, GI, EndBB.first);
529 TraverseCFG(Succ, GI, LoopDepth);
533 GraphIter ArgNode = MaybeAddNode(MachineGadgetGraph::ArgNodeSentinel).first;
534 TraverseCFG(&MF.
front(), ArgNode, 0);
535 std::unique_ptr<MachineGadgetGraph>
G{Builder.get(FenceCount, GadgetCount)};
541int X86LoadValueInjectionLoadHardeningPass::elimMitigatedEdgesAndNodes(
542 MachineGadgetGraph &
G, EdgeSet &ElimEdges ,
544 if (
G.NumFences > 0) {
547 for (
const Edge &
E :
G.edges()) {
548 const Node *Dest =
E.getDest();
549 if (isFence(Dest->getValue())) {
552 for (
const Edge &DE : Dest->edges())
553 ElimEdges.insert(DE);
559 int RemainingGadgets = 0;
561 for (
const Node &RootN :
G.nodes()) {
562 if (
llvm::none_of(RootN.edges(), MachineGadgetGraph::isGadgetEdge))
566 ReachableNodes.clear();
567 std::function<void(
const Node *,
bool)> FindReachableNodes =
568 [&](
const Node *
N,
bool FirstNode) {
570 ReachableNodes.insert(*
N);
571 for (
const Edge &
E :
N->edges()) {
572 const Node *Dest =
E.getDest();
573 if (MachineGadgetGraph::isCFGEdge(
E) && !ElimEdges.contains(
E) &&
574 !ReachableNodes.contains(*Dest))
575 FindReachableNodes(Dest,
false);
578 FindReachableNodes(&RootN,
true);
581 for (
const Edge &
E : RootN.edges()) {
582 if (MachineGadgetGraph::isGadgetEdge(
E)) {
583 if (ReachableNodes.contains(*
E.getDest())) {
592 return RemainingGadgets;
595std::unique_ptr<MachineGadgetGraph>
596X86LoadValueInjectionLoadHardeningPass::trimMitigatedEdges(
597 std::unique_ptr<MachineGadgetGraph> Graph)
const {
599 EdgeSet ElimEdges{*Graph};
600 int RemainingGadgets =
601 elimMitigatedEdgesAndNodes(*Graph, ElimEdges, ElimNodes);
602 if (ElimEdges.empty() && ElimNodes.
empty()) {
603 Graph->NumFences = 0;
604 Graph->NumGadgets = RemainingGadgets;
606 Graph = GraphBuilder::trim(*Graph, ElimNodes, ElimEdges, 0 ,
612int X86LoadValueInjectionLoadHardeningPass::hardenLoadsWithPlugin(
613 MachineFunction &MF, std::unique_ptr<MachineGadgetGraph> Graph)
const {
614 int FencesInserted = 0;
618 Graph = trimMitigatedEdges(std::move(Graph));
620 if (Graph->NumGadgets == 0)
624 EdgeSet CutEdges{*Graph};
625 auto Nodes = std::make_unique<unsigned int[]>(Graph->nodes_size() +
627 auto Edges = std::make_unique<unsigned int[]>(Graph->edges_size());
628 auto EdgeCuts = std::make_unique<int[]>(Graph->edges_size());
629 auto EdgeValues = std::make_unique<int[]>(Graph->edges_size());
630 for (
const Node &
N : Graph->nodes()) {
631 Nodes[Graph->getNodeIndex(
N)] = Graph->getEdgeIndex(*
N.edges_begin());
633 Nodes[Graph->nodes_size()] = Graph->edges_size();
634 for (
const Edge &
E : Graph->edges()) {
635 Edges[Graph->getEdgeIndex(
E)] = Graph->getNodeIndex(*
E.getDest());
636 EdgeValues[Graph->getEdgeIndex(
E)] =
E.getValue();
638 OptimizeCut(Nodes.get(), Graph->nodes_size(), Edges.get(), EdgeValues.get(),
639 EdgeCuts.get(), Graph->edges_size());
640 for (
int I = 0;
I < Graph->edges_size(); ++
I)
647 FencesInserted += insertFences(MF, *Graph, CutEdges);
649 LLVM_DEBUG(
dbgs() <<
"Inserted " << FencesInserted <<
" fences\n");
651 Graph = GraphBuilder::trim(*Graph,
NodeSet{*Graph}, CutEdges);
654 return FencesInserted;
657int X86LoadValueInjectionLoadHardeningPass::hardenLoadsWithHeuristic(
658 MachineFunction &MF, std::unique_ptr<MachineGadgetGraph> Graph)
const {
661 if (Graph->NumFences > 0) {
663 Graph = trimMitigatedEdges(std::move(Graph));
667 if (Graph->NumGadgets == 0)
671 EdgeSet CutEdges{*Graph};
675 for (
const Edge &
E : Graph->edges())
676 if (MachineGadgetGraph::isCFGEdge(
E))
677 IngressEdgeMap[
E.getDest()].push_back(&
E);
687 for (
const Node &
N : Graph->nodes()) {
688 for (
const Edge &
E :
N.edges()) {
689 if (!MachineGadgetGraph::isGadgetEdge(
E))
694 for (
const Edge &EgressEdge :
N.edges())
695 if (MachineGadgetGraph::isCFGEdge(EgressEdge))
698 int EgressCutCost = 0, IngressCutCost = 0;
699 for (
const Edge *EgressEdge : EgressEdges)
700 if (!CutEdges.contains(*EgressEdge))
701 EgressCutCost += EgressEdge->getValue();
702 for (
const Edge *IngressEdge : IngressEdges)
703 if (!CutEdges.contains(*IngressEdge))
704 IngressCutCost += IngressEdge->getValue();
707 IngressCutCost < EgressCutCost ? IngressEdges : EgressEdges;
708 for (
const Edge *
E : EdgesToCut)
716 int FencesInserted = insertFences(MF, *Graph, CutEdges);
718 LLVM_DEBUG(
dbgs() <<
"Inserted " << FencesInserted <<
" fences\n");
720 return FencesInserted;
723int X86LoadValueInjectionLoadHardeningPass::insertFences(
725 EdgeSet &CutEdges )
const {
726 int FencesInserted = 0;
727 for (
const Node &
N :
G.nodes()) {
728 for (
const Edge &
E :
N.edges()) {
729 if (CutEdges.contains(
E)) {
733 if (
MI == MachineGadgetGraph::ArgNodeSentinel) {
738 }
else if (
MI->isBranch()) {
739 MBB =
MI->getParent();
741 Prev =
MI->getPrevNode();
744 for (
const Edge &
E :
N.edges()) {
745 if (MachineGadgetGraph::isCFGEdge(
E))
749 MBB =
MI->getParent();
750 InsertionPt =
MI->getNextNode() ?
MI->getNextNode() :
MBB->
end();
751 Prev = InsertionPt ==
MBB->
end()
753 : InsertionPt->getPrevNode();
756 if ((InsertionPt ==
MBB->
end() || !isFence(&*InsertionPt)) &&
757 (!Prev || !isFence(Prev))) {
764 return FencesInserted;
767bool X86LoadValueInjectionLoadHardeningPass::instrUsesRegToAccessMemory(
769 if (!
MI.mayLoadOrStore() ||
MI.getOpcode() == X86::MFENCE ||
770 MI.getOpcode() == X86::SFENCE ||
MI.getOpcode() == X86::LFENCE)
776 if (MemRefBeginIdx < 0) {
777 LLVM_DEBUG(
dbgs() <<
"Warning: unable to obtain memory operand for loading "
788 return (BaseMO.
isReg() && BaseMO.
getReg() != X86::NoRegister &&
789 TRI->regsOverlap(BaseMO.
getReg(), Reg)) ||
790 (IndexMO.
isReg() && IndexMO.
getReg() != X86::NoRegister &&
791 TRI->regsOverlap(IndexMO.
getReg(), Reg));
794bool X86LoadValueInjectionLoadHardeningPass::instrUsesRegToBranch(
796 if (!
MI.isConditionalBranch())
799 if (
Use.isReg() &&
Use.getReg() == Reg)
805 "X86 LVI load hardening",
false,
false)
813 return new X86LoadValueInjectionLoadHardeningPass();
MachineInstrBuilder & UseMI
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
This file defines the DenseMap class.
Rewrite Partial Register Uses
const HexagonInstrInfo * TII
Description: ImmutableGraph is a fast DAG implementation that cannot be modified, except by creating ...
unsigned const TargetRegisterInfo * TRI
#define INITIALIZE_PASS_DEPENDENCY(depName)
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
This file defines the SmallSet class.
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
int(* OptimizeCutT)(unsigned int *Nodes, unsigned int NodesSize, unsigned int *Edges, int *EdgeValues, int *CutEdges, unsigned int EdgesSize)
static cl::opt< bool > EmitDot(PASS_KEY "-dot", cl::desc("For each function, emit a dot graph depicting potential LVI gadgets"), cl::init(false), cl::Hidden)
static cl::opt< std::string > OptimizePluginPath(PASS_KEY "-opt-plugin", cl::desc("Specify a plugin to optimize LFENCE insertion"), cl::Hidden)
static void writeGadgetGraph(raw_ostream &OS, MachineFunction &MF, MachineGadgetGraph *G)
static cl::opt< bool > EmitDotVerify(PASS_KEY "-dot-verify", cl::desc("For each function, emit a dot graph to stdout depicting " "potential LVI gadgets, used for testing purposes only"), cl::init(false), cl::Hidden)
static llvm::sys::DynamicLibrary OptimizeDL
static OptimizeCutT OptimizeCut
static cl::opt< bool > EmitDotOnly(PASS_KEY "-dot-only", cl::desc("For each function, emit a dot graph depicting potential LVI " "gadgets, and do not insert any fences"), cl::init(false), cl::Hidden)
static cl::opt< bool > NoConditionalBranches(PASS_KEY "-no-cbranch", cl::desc("Don't treat conditional branches as disclosure gadgets. This " "may improve performance, at the cost of security."), cl::init(false), cl::Hidden)
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
void setPreservesCFG()
This function should be called by the pass, iff they do not:
iterator find(const_arg_type_t< KeyT > Val)
bool contains(const_arg_type_t< KeyT > Val) const
Return true if the specified key is in the map, false otherwise.
virtual std::string message() const
Return the error message as a string.
This class wraps a filename and another Error.
FunctionPass class - This class is used to implement most global optimizations.
Describe properties that are true of each instruction in the target description file.
iterator getFirstTerminator()
Returns an iterator to the first terminator instruction of this basic block.
iterator_range< succ_iterator > successors()
DominatorTree Class - Concrete subclass of DominatorTreeBase that is used to compute a normal dominat...
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
virtual bool runOnMachineFunction(MachineFunction &MF)=0
runOnMachineFunction - This method must be overloaded to perform the desired machine code transformat...
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
StringRef getName() const
getName - Return the name of the corresponding LLVM function.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
const MachineBasicBlock & front() const
Representation of each machine instruction.
const MachineBasicBlock * getParent() const
unsigned getLoopDepth(const MachineBasicBlock *BB) const
Return the loop nesting level of the specified block.
MachineOperand class - Representation of each machine instruction operand.
bool isReg() const
isReg - Tests if this is a MO_Register operand.
MachineInstr * getParent()
getParent - Return the instruction that this operand belongs to.
Register getReg() const
getReg - Returns the register number.
A NodeSet contains a set of SUnit DAG nodes with additional information that assigns a priority to th...
virtual void print(raw_ostream &OS, const Module *M) const
print - Print out the internal state of the pass.
virtual StringRef getPassName() const
getPassName - Return a nice clean name for a pass.
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
StringRef - Represent a constant reference to a string, i.e.
TargetInstrInfo - Interface to description of machine instruction set.
TargetRegisterInfo base class - We assume that the target defines a static array of TargetRegisterDes...
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
A Use represents the edge between a Value definition and its users.
A raw_ostream that writes to a file descriptor.
This class implements an extremely fast bulk output stream that can only output to a stream.
A raw_ostream that writes to an std::string.
This class provides a portable interface to dynamic libraries which also might be known as shared lib...
static DynamicLibrary getPermanentLibrary(const char *filename, std::string *errMsg=nullptr)
This function permanently loads the dynamic library at the given path using the library load operatio...
void * getAddressOfSymbol(const char *symbolName)
Searches through the library for the symbol symbolName.
bool isValid() const
Returns true if the object refers to a valid library.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
int getMemoryOperandNo(uint64_t TSFlags)
unsigned getOperandBias(const MCInstrDesc &Desc)
Compute whether all of the def operands are repeated in the uses and therefore should be skipped.
initializer< Ty > init(const Ty &Val)
NodeAddr< PhiNode * > Phi
NodeAddr< DefNode * > Def
std::set< NodeId > NodeSet
This is an optimization pass for GlobalISel generic memory operations.
UnaryFunction for_each(R &&Range, UnaryFunction F)
Provide wrappers to std::for_each which take ranges instead of having to pass begin/end explicitly.
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
FunctionPass * createX86LoadValueInjectionLoadHardeningPass()
raw_fd_ostream & outs()
This returns a reference to a raw_fd_ostream for standard output.
raw_ostream & WriteGraph(raw_ostream &O, const GraphType &G, bool ShortNames=false, const Twine &Title="")
void sort(IteratorTy Start, IteratorTy End)
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
bool none_of(R &&Range, UnaryPredicate P)
Provide wrappers to std::none_of which take ranges instead of having to pass begin/end explicitly.
void report_fatal_error(Error Err, bool gen_crash_diag=true)
Report a serious error, calling any installed error handler.
raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
@ Ref
The access may reference the value stored in memory.
typename Traits::NodeRef NodeRef
static std::string getNodeAttributes(NodeRef Node, GraphType *)
typename Traits::ChildIteratorType ChildIteratorType
std::string getNodeLabel(NodeRef Node, GraphType *)
static std::string getEdgeAttributes(NodeRef, ChildIteratorType E, GraphType *)
MachineGadgetGraph GraphType
typename Traits::EdgeRef EdgeRef
typename Traits::ChildEdgeIteratorType ChildEdgeIteratorType
DOTGraphTraits(bool IsSimple=false)
DOTGraphTraits - Template class that can be specialized to customize how graphs are converted to 'dot...
Description of the encoding of one expression Op.
DefaultDOTGraphTraits - This class provides the default implementations of all of the DOTGraphTraits ...
typename GraphType::UnknownGraphTypeError NodeRef