70#define PASS_KEY "x86-lvi-load"
71#define DEBUG_TYPE PASS_KEY
73STATISTIC(NumFences,
"Number of LFENCEs inserted for LVI mitigation");
74STATISTIC(NumFunctionsConsidered,
"Number of functions analyzed");
75STATISTIC(NumFunctionsMitigated,
"Number of functions for which mitigations "
77STATISTIC(NumGadgets,
"Number of LVI gadgets detected during analysis");
85 cl::desc(
"Don't treat conditional branches as disclosure gadgets. This "
86 "may improve performance, at the cost of security."),
92 "For each function, emit a dot graph depicting potential LVI gadgets"),
97 cl::desc(
"For each function, emit a dot graph depicting potential LVI "
98 "gadgets, and do not insert any fences"),
103 cl::desc(
"For each function, emit a dot graph to stdout depicting "
104 "potential LVI gadgets, used for testing purposes only"),
108typedef int (*
OptimizeCutT)(
unsigned int *Nodes,
unsigned int NodesSize,
109 unsigned int *Edges,
int *EdgeValues,
110 int *CutEdges ,
unsigned int EdgesSize);
116 static constexpr int GadgetEdgeSentinel = -1;
117 static constexpr MachineInstr *
const ArgNodeSentinel =
nullptr;
120 using Node =
typename GraphT::Node;
121 using Edge =
typename GraphT::Edge;
123 MachineGadgetGraph(std::unique_ptr<
Node[]> Nodes,
124 std::unique_ptr<Edge[]> Edges, size_type NodesSize,
125 size_type EdgesSize,
int NumFences = 0,
int NumGadgets = 0)
126 : GraphT(std::move(Nodes), std::move(Edges), NodesSize, EdgesSize),
127 NumFences(NumFences), NumGadgets(NumGadgets) {}
128 static inline bool isCFGEdge(
const Edge &
E) {
129 return E.getValue() != GadgetEdgeSentinel;
131 static inline bool isGadgetEdge(
const Edge &
E) {
132 return E.getValue() == GadgetEdgeSentinel;
143 return "X86 Load Value Injection (LVI) Load Hardening";
152 using Edge = MachineGadgetGraph::Edge;
153 using Node = MachineGadgetGraph::Node;
154 using EdgeSet = MachineGadgetGraph::EdgeSet;
155 using NodeSet = MachineGadgetGraph::NodeSet;
161 std::unique_ptr<MachineGadgetGraph>
166 std::unique_ptr<MachineGadgetGraph> Graph)
const;
168 std::unique_ptr<MachineGadgetGraph> Graph)
const;
169 int elimMitigatedEdgesAndNodes(MachineGadgetGraph &
G,
172 std::unique_ptr<MachineGadgetGraph>
173 trimMitigatedEdges(std::unique_ptr<MachineGadgetGraph> Graph)
const;
175 EdgeSet &CutEdges )
const;
176 bool instrUsesRegToAccessMemory(
const MachineInstr &
I,
unsigned Reg)
const;
177 bool instrUsesRegToBranch(
const MachineInstr &
I,
unsigned Reg)
const;
179 return MI && (
MI->getOpcode() == X86::LFENCE ||
180 (STI->useLVIControlFlowIntegrity() &&
MI->isCall()));
204 if (Node->getValue() == MachineGadgetGraph::ArgNodeSentinel)
209 OS << *Node->getValue();
215 if (
MI == MachineGadgetGraph::ArgNodeSentinel)
216 return "color = blue";
217 if (
MI->getOpcode() == X86::LFENCE)
218 return "color = green";
224 int EdgeVal = (*
E.getCurrent()).getValue();
225 return EdgeVal >= 0 ?
"label = " + std::to_string(EdgeVal)
226 :
"color = red, style = \"dashed\"";
232constexpr MachineInstr *MachineGadgetGraph::ArgNodeSentinel;
233constexpr int MachineGadgetGraph::GadgetEdgeSentinel;
235char X86LoadValueInjectionLoadHardeningPass::ID = 0;
237void X86LoadValueInjectionLoadHardeningPass::getAnalysisUsage(
247 MachineGadgetGraph *
G) {
249 "Speculative gadgets for \"" + MF.
getName() +
"\" function");
252bool X86LoadValueInjectionLoadHardeningPass::runOnMachineFunction(
257 if (!STI->useLVILoadHardening())
266 if (!
F.hasOptNone() && skipFunction(
F))
269 ++NumFunctionsConsidered;
270 TII = STI->getInstrInfo();
271 TRI = STI->getRegisterInfo();
273 const auto &MLI = getAnalysis<MachineLoopInfo>();
274 const auto &MDT = getAnalysis<MachineDominatorTree>();
275 const auto &MDF = getAnalysis<MachineDominanceFrontier>();
276 std::unique_ptr<MachineGadgetGraph> Graph = getGadgetGraph(MF, MLI, MDT, MDF);
278 if (Graph ==
nullptr)
289 std::string FileName =
"lvi.";
305 std::string ErrorMsg;
308 if (!ErrorMsg.empty())
315 FencesInserted = hardenLoadsWithPlugin(MF, std::move(Graph));
317 FencesInserted = hardenLoadsWithHeuristic(MF, std::move(Graph));
320 if (FencesInserted > 0)
321 ++NumFunctionsMitigated;
322 NumFences += FencesInserted;
323 return (FencesInserted > 0);
326std::unique_ptr<MachineGadgetGraph>
327X86LoadValueInjectionLoadHardeningPass::getGadgetGraph(
334 DataFlowGraph DFG{MF, *
TII, *
TRI, MDT, MDF};
340 using GraphIter =
typename GraphBuilder::BuilderNodeRef;
342 int FenceCount = 0, GadgetCount = 0;
345 if (
Ref == NodeMap.
end()) {
348 return std::pair<GraphIter, bool>{
I,
true};
350 return std::pair<GraphIter, bool>{
Ref->getSecond(),
false};
361 auto AnalyzeDef = [&](NodeAddr<DefNode *> SourceDef) {
363 std::function<void(NodeAddr<DefNode *>)> AnalyzeDefUseChain =
364 [&](NodeAddr<DefNode *>
Def) {
370 RegisterRef DefReg =
Def.Addr->getRegRef(DFG);
371 for (
auto UseID :
L.getAllReachedUses(DefReg, Def)) {
372 auto Use = DFG.addr<UseNode *>(UseID);
373 if (
Use.Addr->getFlags() & NodeAttrs::PhiRef) {
374 NodeAddr<PhiNode *> Phi =
Use.Addr->getOwner(DFG);
375 for (
const auto&
I :
L.getRealUses(Phi.Id)) {
376 if (DFG.getPRI().alias(RegisterRef(
I.first), DefReg)) {
377 for (
const auto &UA :
I.second)
378 Uses.emplace(UA.first);
389 for (
auto UseID :
Uses) {
390 if (!UsesVisited.
insert(UseID).second)
393 auto Use = DFG.addr<UseNode *>(UseID);
394 assert(!(
Use.Addr->getFlags() & NodeAttrs::PhiRef));
407 if (instrUsesRegToAccessMemory(
UseMI, UseMO.
getReg()) ||
410 Transmitters[
Def.Id].push_back(
Use.Addr->getOwner(DFG).Id);
418 NodeAddr<InstrNode *> Owner{
Use.Addr->getOwner(DFG)};
420 for (
const auto &ChildDef :
421 Owner.Addr->members_if(DataFlowGraph::IsDef, DFG)) {
422 if (!DefsVisited.
insert(ChildDef.Id).second)
424 if (
Def.Addr->getAttrs() & NodeAttrs::Dead)
426 if (
Def.Id == ChildDef.Id)
429 AnalyzeDefUseChain(ChildDef);
432 for (
auto TransmitterId : Transmitters[ChildDef.Id])
433 Transmitters[
Def.Id].push_back(TransmitterId);
439 auto &DefTransmitters = Transmitters[
Def.Id];
443 DefTransmitters.erase(
444 std::unique(DefTransmitters.begin(), DefTransmitters.end()),
445 DefTransmitters.end());
449 AnalyzeDefUseChain(SourceDef);
450 auto &SourceDefTransmitters = Transmitters[SourceDef.Id];
451 if (SourceDefTransmitters.empty())
455 ? MachineGadgetGraph::ArgNodeSentinel
457 auto GadgetSource = MaybeAddNode(Source);
459 for (
auto TransmitterId : SourceDefTransmitters) {
461 auto GadgetSink = MaybeAddNode(Sink);
463 Builder.addEdge(MachineGadgetGraph::GadgetEdgeSentinel,
464 GadgetSource.first, GadgetSink.first);
469 LLVM_DEBUG(
dbgs() <<
"Analyzing def-use chains to find gadgets\n");
471 NodeAddr<BlockNode *> EntryBlock = DFG.getFunc().Addr->getEntryBlock(DFG);
472 for (NodeAddr<PhiNode *> ArgPhi :
473 EntryBlock.Addr->members_if(DataFlowGraph::IsPhi, DFG)) {
474 NodeList Defs = ArgPhi.Addr->members_if(DataFlowGraph::IsDef, DFG);
478 for (NodeAddr<BlockNode *> BA : DFG.getFunc().Addr->members(DFG)) {
479 for (NodeAddr<StmtNode *> SA :
480 BA.Addr->members_if(DataFlowGraph::IsCode<NodeAttrs::Stmt>, DFG)) {
485 }
else if (
MI->mayLoad()) {
486 NodeList Defs = SA.Addr->members_if(DataFlowGraph::IsDef, DFG);
493 if (GadgetCount == 0)
495 NumGadgets += GadgetCount;
505 auto BeginBB = MaybeAddNode(&*NI);
506 Builder.addEdge(ParentDepth, GI, BeginBB.first);
512 while (++NI !=
MBB->
end()) {
514 if (
Ref != NodeMap.
end()) {
515 Builder.addEdge(LoopDepth, GI,
Ref->getSecond());
516 GI =
Ref->getSecond();
523 auto EndBB = MaybeAddNode(&*
T);
525 Builder.addEdge(LoopDepth, GI, EndBB.first);
530 TraverseCFG(Succ, GI, LoopDepth);
534 GraphIter ArgNode = MaybeAddNode(MachineGadgetGraph::ArgNodeSentinel).first;
535 TraverseCFG(&MF.
front(), ArgNode, 0);
536 std::unique_ptr<MachineGadgetGraph>
G{
Builder.get(FenceCount, GadgetCount)};
542int X86LoadValueInjectionLoadHardeningPass::elimMitigatedEdgesAndNodes(
543 MachineGadgetGraph &
G, EdgeSet &ElimEdges ,
545 if (
G.NumFences > 0) {
548 for (
const Edge &
E :
G.edges()) {
549 const Node *Dest =
E.getDest();
550 if (isFence(Dest->getValue())) {
553 for (
const Edge &DE : Dest->edges())
554 ElimEdges.insert(DE);
560 int RemainingGadgets = 0;
562 for (
const Node &RootN :
G.nodes()) {
563 if (
llvm::none_of(RootN.edges(), MachineGadgetGraph::isGadgetEdge))
567 ReachableNodes.clear();
568 std::function<void(
const Node *,
bool)> FindReachableNodes =
569 [&](
const Node *
N,
bool FirstNode) {
571 ReachableNodes.insert(*
N);
572 for (
const Edge &
E :
N->edges()) {
573 const Node *Dest =
E.getDest();
574 if (MachineGadgetGraph::isCFGEdge(
E) && !ElimEdges.contains(
E) &&
575 !ReachableNodes.contains(*Dest))
576 FindReachableNodes(Dest,
false);
579 FindReachableNodes(&RootN,
true);
582 for (
const Edge &
E : RootN.edges()) {
583 if (MachineGadgetGraph::isGadgetEdge(
E)) {
584 if (ReachableNodes.contains(*
E.getDest())) {
593 return RemainingGadgets;
596std::unique_ptr<MachineGadgetGraph>
597X86LoadValueInjectionLoadHardeningPass::trimMitigatedEdges(
598 std::unique_ptr<MachineGadgetGraph> Graph)
const {
600 EdgeSet ElimEdges{*Graph};
601 int RemainingGadgets =
602 elimMitigatedEdgesAndNodes(*Graph, ElimEdges, ElimNodes);
603 if (ElimEdges.empty() && ElimNodes.
empty()) {
604 Graph->NumFences = 0;
605 Graph->NumGadgets = RemainingGadgets;
607 Graph = GraphBuilder::trim(*Graph, ElimNodes, ElimEdges, 0 ,
613int X86LoadValueInjectionLoadHardeningPass::hardenLoadsWithPlugin(
614 MachineFunction &MF, std::unique_ptr<MachineGadgetGraph> Graph)
const {
615 int FencesInserted = 0;
619 Graph = trimMitigatedEdges(std::move(Graph));
621 if (Graph->NumGadgets == 0)
625 EdgeSet CutEdges{*Graph};
626 auto Nodes = std::make_unique<unsigned int[]>(Graph->nodes_size() +
628 auto Edges = std::make_unique<unsigned int[]>(Graph->edges_size());
629 auto EdgeCuts = std::make_unique<int[]>(Graph->edges_size());
630 auto EdgeValues = std::make_unique<int[]>(Graph->edges_size());
631 for (
const Node &
N : Graph->nodes()) {
632 Nodes[Graph->getNodeIndex(
N)] = Graph->getEdgeIndex(*
N.edges_begin());
634 Nodes[Graph->nodes_size()] = Graph->edges_size();
635 for (
const Edge &
E : Graph->edges()) {
636 Edges[Graph->getEdgeIndex(
E)] = Graph->getNodeIndex(*
E.getDest());
637 EdgeValues[Graph->getEdgeIndex(
E)] =
E.getValue();
639 OptimizeCut(Nodes.get(), Graph->nodes_size(), Edges.get(), EdgeValues.get(),
640 EdgeCuts.get(), Graph->edges_size());
641 for (
int I = 0;
I < Graph->edges_size(); ++
I)
648 FencesInserted += insertFences(MF, *Graph, CutEdges);
650 LLVM_DEBUG(
dbgs() <<
"Inserted " << FencesInserted <<
" fences\n");
652 Graph = GraphBuilder::trim(*Graph,
NodeSet{*Graph}, CutEdges);
655 return FencesInserted;
658int X86LoadValueInjectionLoadHardeningPass::hardenLoadsWithHeuristic(
659 MachineFunction &MF, std::unique_ptr<MachineGadgetGraph> Graph)
const {
662 if (Graph->NumFences > 0) {
664 Graph = trimMitigatedEdges(std::move(Graph));
668 if (Graph->NumGadgets == 0)
672 EdgeSet CutEdges{*Graph};
676 for (
const Edge &
E : Graph->edges())
677 if (MachineGadgetGraph::isCFGEdge(
E))
678 IngressEdgeMap[
E.getDest()].push_back(&
E);
688 for (
const Node &
N : Graph->nodes()) {
689 for (
const Edge &
E :
N.edges()) {
690 if (!MachineGadgetGraph::isGadgetEdge(
E))
695 for (
const Edge &EgressEdge :
N.edges())
696 if (MachineGadgetGraph::isCFGEdge(EgressEdge))
699 int EgressCutCost = 0, IngressCutCost = 0;
700 for (
const Edge *EgressEdge : EgressEdges)
701 if (!CutEdges.contains(*EgressEdge))
702 EgressCutCost += EgressEdge->getValue();
703 for (
const Edge *IngressEdge : IngressEdges)
704 if (!CutEdges.contains(*IngressEdge))
705 IngressCutCost += IngressEdge->getValue();
708 IngressCutCost < EgressCutCost ? IngressEdges : EgressEdges;
709 for (
const Edge *
E : EdgesToCut)
717 int FencesInserted = insertFences(MF, *Graph, CutEdges);
719 LLVM_DEBUG(
dbgs() <<
"Inserted " << FencesInserted <<
" fences\n");
721 return FencesInserted;
724int X86LoadValueInjectionLoadHardeningPass::insertFences(
726 EdgeSet &CutEdges )
const {
727 int FencesInserted = 0;
728 for (
const Node &
N :
G.nodes()) {
729 for (
const Edge &
E :
N.edges()) {
730 if (CutEdges.contains(
E)) {
734 if (
MI == MachineGadgetGraph::ArgNodeSentinel) {
739 }
else if (
MI->isBranch()) {
740 MBB =
MI->getParent();
742 Prev =
MI->getPrevNode();
745 for (
const Edge &
E :
N.edges()) {
746 if (MachineGadgetGraph::isCFGEdge(
E))
750 MBB =
MI->getParent();
751 InsertionPt =
MI->getNextNode() ?
MI->getNextNode() :
MBB->
end();
752 Prev = InsertionPt ==
MBB->
end()
754 : InsertionPt->getPrevNode();
757 if ((InsertionPt ==
MBB->
end() || !isFence(&*InsertionPt)) &&
758 (!Prev || !isFence(Prev))) {
765 return FencesInserted;
768bool X86LoadValueInjectionLoadHardeningPass::instrUsesRegToAccessMemory(
770 if (!
MI.mayLoadOrStore() ||
MI.getOpcode() == X86::MFENCE ||
771 MI.getOpcode() == X86::SFENCE ||
MI.getOpcode() == X86::LFENCE)
777 if (MemRefBeginIdx < 0) {
778 LLVM_DEBUG(
dbgs() <<
"Warning: unable to obtain memory operand for loading "
789 return (BaseMO.
isReg() && BaseMO.
getReg() != X86::NoRegister &&
790 TRI->regsOverlap(BaseMO.
getReg(), Reg)) ||
791 (IndexMO.
isReg() && IndexMO.
getReg() != X86::NoRegister &&
792 TRI->regsOverlap(IndexMO.
getReg(), Reg));
795bool X86LoadValueInjectionLoadHardeningPass::instrUsesRegToBranch(
797 if (!
MI.isConditionalBranch())
800 if (
Use.isReg() &&
Use.getReg() == Reg)
806 "X86 LVI load hardening",
false,
false)
814 return new X86LoadValueInjectionLoadHardeningPass();
MachineInstrBuilder & UseMI
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
This file defines the DenseMap class.
This file defines the DenseSet and SmallDenseSet classes.
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)
The function returns the MCInst operand # for the first field of the memory operand.
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)
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...
DefaultDOTGraphTraits - This class provides the default implementations of all of the DOTGraphTraits ...
typename GraphType::UnknownGraphTypeError NodeRef