Mark files not tagged with llvm-337282 as dead
This commit is contained in:
parent
36d17e5737
commit
8ff672edf7
@ -1,253 +0,0 @@
|
||||
//===- DIBuilderBindings.cpp - Bindings for DIBuilder ---------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines C bindings for the DIBuilder class.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "DIBuilderBindings.h"
|
||||
#include "IRBindings.h"
|
||||
#include "llvm/IR/DIBuilder.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
LLVMDIBuilderRef LLVMNewDIBuilder(LLVMModuleRef mref) {
|
||||
Module *m = unwrap(mref);
|
||||
return wrap(new DIBuilder(*m));
|
||||
}
|
||||
|
||||
void LLVMDIBuilderDestroy(LLVMDIBuilderRef dref) {
|
||||
DIBuilder *d = unwrap(dref);
|
||||
delete d;
|
||||
}
|
||||
|
||||
void LLVMDIBuilderFinalize(LLVMDIBuilderRef dref) { unwrap(dref)->finalize(); }
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateCompileUnit(LLVMDIBuilderRef Dref,
|
||||
unsigned Lang, const char *File,
|
||||
const char *Dir,
|
||||
const char *Producer,
|
||||
int Optimized, const char *Flags,
|
||||
unsigned RuntimeVersion) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
return wrap(D->createCompileUnit(Lang, D->createFile(File, Dir), Producer,
|
||||
Optimized, Flags, RuntimeVersion));
|
||||
}
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateFile(LLVMDIBuilderRef Dref, const char *File,
|
||||
const char *Dir) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
return wrap(D->createFile(File, Dir));
|
||||
}
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateLexicalBlock(LLVMDIBuilderRef Dref,
|
||||
LLVMMetadataRef Scope,
|
||||
LLVMMetadataRef File,
|
||||
unsigned Line,
|
||||
unsigned Column) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
auto *LB = D->createLexicalBlock(unwrap<DILocalScope>(Scope),
|
||||
unwrap<DIFile>(File), Line, Column);
|
||||
return wrap(LB);
|
||||
}
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateLexicalBlockFile(LLVMDIBuilderRef Dref,
|
||||
LLVMMetadataRef Scope,
|
||||
LLVMMetadataRef File,
|
||||
unsigned Discriminator) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
return wrap(D->createLexicalBlockFile(unwrap<DILocalScope>(Scope),
|
||||
unwrap<DIFile>(File), Discriminator));
|
||||
}
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateFunction(
|
||||
LLVMDIBuilderRef Dref, LLVMMetadataRef Scope, const char *Name,
|
||||
const char *LinkageName, LLVMMetadataRef File, unsigned Line,
|
||||
LLVMMetadataRef CompositeType, int IsLocalToUnit, int IsDefinition,
|
||||
unsigned ScopeLine, unsigned Flags, int IsOptimized) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
return wrap(D->createFunction(
|
||||
unwrap<DIScope>(Scope), Name, LinkageName,
|
||||
File ? unwrap<DIFile>(File) : nullptr, Line,
|
||||
unwrap<DISubroutineType>(CompositeType), IsLocalToUnit, IsDefinition,
|
||||
ScopeLine, static_cast<DINode::DIFlags>(Flags), IsOptimized));
|
||||
}
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateAutoVariable(
|
||||
LLVMDIBuilderRef Dref, LLVMMetadataRef Scope, const char *Name,
|
||||
LLVMMetadataRef File, unsigned Line, LLVMMetadataRef Ty, int AlwaysPreserve,
|
||||
unsigned Flags, uint32_t AlignInBits) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
return wrap(
|
||||
D->createAutoVariable(unwrap<DIScope>(Scope), Name, unwrap<DIFile>(File),
|
||||
Line, unwrap<DIType>(Ty), AlwaysPreserve,
|
||||
static_cast<DINode::DIFlags>(Flags), AlignInBits));
|
||||
}
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateParameterVariable(
|
||||
LLVMDIBuilderRef Dref, LLVMMetadataRef Scope, const char *Name,
|
||||
unsigned ArgNo, LLVMMetadataRef File, unsigned Line, LLVMMetadataRef Ty,
|
||||
int AlwaysPreserve, unsigned Flags) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
return wrap(D->createParameterVariable(
|
||||
unwrap<DIScope>(Scope), Name, ArgNo, unwrap<DIFile>(File), Line,
|
||||
unwrap<DIType>(Ty), AlwaysPreserve, static_cast<DINode::DIFlags>(Flags)));
|
||||
}
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateBasicType(LLVMDIBuilderRef Dref,
|
||||
const char *Name,
|
||||
uint64_t SizeInBits,
|
||||
unsigned Encoding) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
return wrap(D->createBasicType(Name, SizeInBits, Encoding));
|
||||
}
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreatePointerType(LLVMDIBuilderRef Dref,
|
||||
LLVMMetadataRef PointeeType,
|
||||
uint64_t SizeInBits,
|
||||
uint32_t AlignInBits,
|
||||
const char *Name) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
return wrap(D->createPointerType(unwrap<DIType>(PointeeType), SizeInBits,
|
||||
AlignInBits, /* DWARFAddressSpace */ None,
|
||||
Name));
|
||||
}
|
||||
|
||||
LLVMMetadataRef
|
||||
LLVMDIBuilderCreateSubroutineType(LLVMDIBuilderRef Dref, LLVMMetadataRef File,
|
||||
LLVMMetadataRef ParameterTypes) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
return wrap(
|
||||
D->createSubroutineType(DITypeRefArray(unwrap<MDTuple>(ParameterTypes))));
|
||||
}
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateStructType(
|
||||
LLVMDIBuilderRef Dref, LLVMMetadataRef Scope, const char *Name,
|
||||
LLVMMetadataRef File, unsigned Line, uint64_t SizeInBits,
|
||||
uint32_t AlignInBits, unsigned Flags, LLVMMetadataRef DerivedFrom,
|
||||
LLVMMetadataRef ElementTypes) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
return wrap(D->createStructType(
|
||||
unwrap<DIScope>(Scope), Name, File ? unwrap<DIFile>(File) : nullptr, Line,
|
||||
SizeInBits, AlignInBits, static_cast<DINode::DIFlags>(Flags),
|
||||
DerivedFrom ? unwrap<DIType>(DerivedFrom) : nullptr,
|
||||
ElementTypes ? DINodeArray(unwrap<MDTuple>(ElementTypes)) : nullptr));
|
||||
}
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateReplaceableCompositeType(
|
||||
LLVMDIBuilderRef Dref, unsigned Tag, const char *Name,
|
||||
LLVMMetadataRef Scope, LLVMMetadataRef File, unsigned Line,
|
||||
unsigned RuntimeLang, uint64_t SizeInBits, uint32_t AlignInBits,
|
||||
unsigned Flags) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
return wrap(D->createReplaceableCompositeType(
|
||||
Tag, Name, unwrap<DIScope>(Scope), File ? unwrap<DIFile>(File) : nullptr,
|
||||
Line, RuntimeLang, SizeInBits, AlignInBits,
|
||||
static_cast<DINode::DIFlags>(Flags)));
|
||||
}
|
||||
|
||||
LLVMMetadataRef
|
||||
LLVMDIBuilderCreateMemberType(LLVMDIBuilderRef Dref, LLVMMetadataRef Scope,
|
||||
const char *Name, LLVMMetadataRef File,
|
||||
unsigned Line, uint64_t SizeInBits,
|
||||
uint32_t AlignInBits, uint64_t OffsetInBits,
|
||||
unsigned Flags, LLVMMetadataRef Ty) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
return wrap(D->createMemberType(
|
||||
unwrap<DIScope>(Scope), Name, File ? unwrap<DIFile>(File) : nullptr, Line,
|
||||
SizeInBits, AlignInBits, OffsetInBits,
|
||||
static_cast<DINode::DIFlags>(Flags), unwrap<DIType>(Ty)));
|
||||
}
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateArrayType(LLVMDIBuilderRef Dref,
|
||||
uint64_t SizeInBits,
|
||||
uint32_t AlignInBits,
|
||||
LLVMMetadataRef ElementType,
|
||||
LLVMMetadataRef Subscripts) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
return wrap(D->createArrayType(SizeInBits, AlignInBits,
|
||||
unwrap<DIType>(ElementType),
|
||||
DINodeArray(unwrap<MDTuple>(Subscripts))));
|
||||
}
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateTypedef(LLVMDIBuilderRef Dref,
|
||||
LLVMMetadataRef Ty, const char *Name,
|
||||
LLVMMetadataRef File, unsigned Line,
|
||||
LLVMMetadataRef Context) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
return wrap(D->createTypedef(unwrap<DIType>(Ty), Name,
|
||||
File ? unwrap<DIFile>(File) : nullptr, Line,
|
||||
Context ? unwrap<DIScope>(Context) : nullptr));
|
||||
}
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderGetOrCreateSubrange(LLVMDIBuilderRef Dref,
|
||||
int64_t Lo, int64_t Count) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
return wrap(D->getOrCreateSubrange(Lo, Count));
|
||||
}
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderGetOrCreateArray(LLVMDIBuilderRef Dref,
|
||||
LLVMMetadataRef *Data,
|
||||
size_t Length) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
Metadata **DataValue = unwrap(Data);
|
||||
ArrayRef<Metadata *> Elements(DataValue, Length);
|
||||
DINodeArray A = D->getOrCreateArray(Elements);
|
||||
return wrap(A.get());
|
||||
}
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderGetOrCreateTypeArray(LLVMDIBuilderRef Dref,
|
||||
LLVMMetadataRef *Data,
|
||||
size_t Length) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
Metadata **DataValue = unwrap(Data);
|
||||
ArrayRef<Metadata *> Elements(DataValue, Length);
|
||||
DITypeRefArray A = D->getOrCreateTypeArray(Elements);
|
||||
return wrap(A.get());
|
||||
}
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateExpression(LLVMDIBuilderRef Dref,
|
||||
int64_t *Addr, size_t Length) {
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
return wrap(D->createExpression(ArrayRef<int64_t>(Addr, Length)));
|
||||
}
|
||||
|
||||
LLVMValueRef LLVMDIBuilderInsertDeclareAtEnd(LLVMDIBuilderRef Dref,
|
||||
LLVMValueRef Storage,
|
||||
LLVMMetadataRef VarInfo,
|
||||
LLVMMetadataRef Expr,
|
||||
LLVMBasicBlockRef Block) {
|
||||
// Fail immediately here until the llgo folks update their bindings. The
|
||||
// called function is going to assert out anyway.
|
||||
llvm_unreachable("DIBuilder API change requires a DebugLoc");
|
||||
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
Instruction *Instr = D->insertDeclare(
|
||||
unwrap(Storage), unwrap<DILocalVariable>(VarInfo),
|
||||
unwrap<DIExpression>(Expr), /* DebugLoc */ nullptr, unwrap(Block));
|
||||
return wrap(Instr);
|
||||
}
|
||||
|
||||
LLVMValueRef LLVMDIBuilderInsertValueAtEnd(LLVMDIBuilderRef Dref,
|
||||
LLVMValueRef Val, uint64_t Offset,
|
||||
LLVMMetadataRef VarInfo,
|
||||
LLVMMetadataRef Expr,
|
||||
LLVMBasicBlockRef Block) {
|
||||
// Fail immediately here until the llgo folks update their bindings. The
|
||||
// called function is going to assert out anyway.
|
||||
llvm_unreachable("DIBuilder API change requires a DebugLoc");
|
||||
|
||||
DIBuilder *D = unwrap(Dref);
|
||||
Instruction *Instr = D->insertDbgValueIntrinsic(
|
||||
unwrap(Val), Offset, unwrap<DILocalVariable>(VarInfo),
|
||||
unwrap<DIExpression>(Expr), /* DebugLoc */ nullptr, unwrap(Block));
|
||||
return wrap(Instr);
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
//===- DIBuilderBindings.h - Bindings for DIBuilder -------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines C bindings for the DIBuilder class.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_BINDINGS_GO_LLVM_DIBUILDERBINDINGS_H
|
||||
#define LLVM_BINDINGS_GO_LLVM_DIBUILDERBINDINGS_H
|
||||
|
||||
#include "IRBindings.h"
|
||||
#include "llvm-c/Core.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// FIXME: These bindings shouldn't be Go-specific and should eventually move to
|
||||
// a (somewhat) less stable collection of C APIs for use in creating bindings of
|
||||
// LLVM in other languages.
|
||||
|
||||
typedef struct LLVMOpaqueDIBuilder *LLVMDIBuilderRef;
|
||||
|
||||
LLVMDIBuilderRef LLVMNewDIBuilder(LLVMModuleRef m);
|
||||
|
||||
void LLVMDIBuilderDestroy(LLVMDIBuilderRef d);
|
||||
void LLVMDIBuilderFinalize(LLVMDIBuilderRef d);
|
||||
|
||||
LLVMMetadataRef
|
||||
LLVMDIBuilderCreateCompileUnit(LLVMDIBuilderRef D, unsigned Language,
|
||||
const char *File, const char *Dir,
|
||||
const char *Producer, int Optimized,
|
||||
const char *Flags, unsigned RuntimeVersion);
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateFile(LLVMDIBuilderRef D, const char *File,
|
||||
const char *Dir);
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateLexicalBlock(LLVMDIBuilderRef D,
|
||||
LLVMMetadataRef Scope,
|
||||
LLVMMetadataRef File,
|
||||
unsigned Line, unsigned Column);
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateLexicalBlockFile(LLVMDIBuilderRef D,
|
||||
LLVMMetadataRef Scope,
|
||||
LLVMMetadataRef File,
|
||||
unsigned Discriminator);
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateFunction(
|
||||
LLVMDIBuilderRef D, LLVMMetadataRef Scope, const char *Name,
|
||||
const char *LinkageName, LLVMMetadataRef File, unsigned Line,
|
||||
LLVMMetadataRef CompositeType, int IsLocalToUnit, int IsDefinition,
|
||||
unsigned ScopeLine, unsigned Flags, int IsOptimized);
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateAutoVariable(
|
||||
LLVMDIBuilderRef D, LLVMMetadataRef Scope, const char *Name,
|
||||
LLVMMetadataRef File, unsigned Line, LLVMMetadataRef Ty, int AlwaysPreserve,
|
||||
unsigned Flags, uint32_t AlignInBits);
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateParameterVariable(
|
||||
LLVMDIBuilderRef D, LLVMMetadataRef Scope, const char *Name, unsigned ArgNo,
|
||||
LLVMMetadataRef File, unsigned Line, LLVMMetadataRef Ty, int AlwaysPreserve,
|
||||
unsigned Flags);
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateBasicType(LLVMDIBuilderRef D,
|
||||
const char *Name,
|
||||
uint64_t SizeInBits,
|
||||
unsigned Encoding);
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreatePointerType(LLVMDIBuilderRef D,
|
||||
LLVMMetadataRef PointeeType,
|
||||
uint64_t SizeInBits,
|
||||
uint32_t AlignInBits,
|
||||
const char *Name);
|
||||
|
||||
LLVMMetadataRef
|
||||
LLVMDIBuilderCreateSubroutineType(LLVMDIBuilderRef D, LLVMMetadataRef File,
|
||||
LLVMMetadataRef ParameterTypes);
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateStructType(
|
||||
LLVMDIBuilderRef D, LLVMMetadataRef Scope, const char *Name,
|
||||
LLVMMetadataRef File, unsigned Line, uint64_t SizeInBits,
|
||||
uint32_t AlignInBits, unsigned Flags, LLVMMetadataRef DerivedFrom,
|
||||
LLVMMetadataRef ElementTypes);
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateReplaceableCompositeType(
|
||||
LLVMDIBuilderRef D, unsigned Tag, const char *Name, LLVMMetadataRef Scope,
|
||||
LLVMMetadataRef File, unsigned Line, unsigned RuntimeLang,
|
||||
uint64_t SizeInBits, uint32_t AlignInBits, unsigned Flags);
|
||||
|
||||
LLVMMetadataRef
|
||||
LLVMDIBuilderCreateMemberType(LLVMDIBuilderRef D, LLVMMetadataRef Scope,
|
||||
const char *Name, LLVMMetadataRef File,
|
||||
unsigned Line, uint64_t SizeInBits,
|
||||
uint32_t AlignInBits, uint64_t OffsetInBits,
|
||||
unsigned Flags, LLVMMetadataRef Ty);
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateArrayType(LLVMDIBuilderRef D,
|
||||
uint64_t SizeInBits,
|
||||
uint32_t AlignInBits,
|
||||
LLVMMetadataRef ElementType,
|
||||
LLVMMetadataRef Subscripts);
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateTypedef(LLVMDIBuilderRef D,
|
||||
LLVMMetadataRef Ty, const char *Name,
|
||||
LLVMMetadataRef File, unsigned Line,
|
||||
LLVMMetadataRef Context);
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderGetOrCreateSubrange(LLVMDIBuilderRef D, int64_t Lo,
|
||||
int64_t Count);
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderGetOrCreateArray(LLVMDIBuilderRef D,
|
||||
LLVMMetadataRef *Data,
|
||||
size_t Length);
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderGetOrCreateTypeArray(LLVMDIBuilderRef D,
|
||||
LLVMMetadataRef *Data,
|
||||
size_t Length);
|
||||
|
||||
LLVMMetadataRef LLVMDIBuilderCreateExpression(LLVMDIBuilderRef Dref,
|
||||
int64_t *Addr, size_t Length);
|
||||
|
||||
LLVMValueRef LLVMDIBuilderInsertDeclareAtEnd(LLVMDIBuilderRef D,
|
||||
LLVMValueRef Storage,
|
||||
LLVMMetadataRef VarInfo,
|
||||
LLVMMetadataRef Expr,
|
||||
LLVMBasicBlockRef Block);
|
||||
|
||||
LLVMValueRef LLVMDIBuilderInsertValueAtEnd(LLVMDIBuilderRef D, LLVMValueRef Val,
|
||||
uint64_t Offset,
|
||||
LLVMMetadataRef VarInfo,
|
||||
LLVMMetadataRef Expr,
|
||||
LLVMBasicBlockRef Block);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,42 +0,0 @@
|
||||
//===- Analysis/ObjectUtils.h - analysis utils for object files -*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_ANALYSIS_OBJECT_UTILS_H
|
||||
#define LLVM_ANALYSIS_OBJECT_UTILS_H
|
||||
|
||||
#include "llvm/IR/GlobalVariable.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// True if GV can be left out of the object symbol table. This is the case
|
||||
/// for linkonce_odr values whose address is not significant. While legal, it is
|
||||
/// not normally profitable to omit them from the .o symbol table. Using this
|
||||
/// analysis makes sense when the information can be passed down to the linker
|
||||
/// or we are in LTO.
|
||||
inline bool canBeOmittedFromSymbolTable(const GlobalValue *GV) {
|
||||
if (!GV->hasLinkOnceODRLinkage())
|
||||
return false;
|
||||
|
||||
// We assume that anyone who sets global unnamed_addr on a non-constant knows
|
||||
// what they're doing.
|
||||
if (GV->hasGlobalUnnamedAddr())
|
||||
return true;
|
||||
|
||||
// If it is a non constant variable, it needs to be uniqued across shared
|
||||
// objects.
|
||||
if (auto *Var = dyn_cast<GlobalVariable>(GV))
|
||||
if (!Var->isConstant())
|
||||
return false;
|
||||
|
||||
return GV->hasAtLeastLocalUnnamedAddr();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,164 +0,0 @@
|
||||
//===- OptimizationDiagnosticInfo.h - Optimization Diagnostic ---*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Optimization diagnostic interfaces. It's packaged as an analysis pass so
|
||||
// that by using this service passes become dependent on BFI as well. BFI is
|
||||
// used to compute the "hotness" of the diagnostic message.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_IR_OPTIMIZATIONDIAGNOSTICINFO_H
|
||||
#define LLVM_IR_OPTIMIZATIONDIAGNOSTICINFO_H
|
||||
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/Analysis/BlockFrequencyInfo.h"
|
||||
#include "llvm/IR/DiagnosticInfo.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/PassManager.h"
|
||||
#include "llvm/Pass.h"
|
||||
|
||||
namespace llvm {
|
||||
class DebugLoc;
|
||||
class LLVMContext;
|
||||
class Loop;
|
||||
class Pass;
|
||||
class Twine;
|
||||
class Value;
|
||||
|
||||
/// The optimization diagnostic interface.
|
||||
///
|
||||
/// It allows reporting when optimizations are performed and when they are not
|
||||
/// along with the reasons for it. Hotness information of the corresponding
|
||||
/// code region can be included in the remark if DiagnosticsHotnessRequested is
|
||||
/// enabled in the LLVM context.
|
||||
class OptimizationRemarkEmitter {
|
||||
public:
|
||||
OptimizationRemarkEmitter(const Function *F, BlockFrequencyInfo *BFI)
|
||||
: F(F), BFI(BFI) {}
|
||||
|
||||
/// \brief This variant can be used to generate ORE on demand (without the
|
||||
/// analysis pass).
|
||||
///
|
||||
/// Note that this ctor has a very different cost depending on whether
|
||||
/// F->getContext().getDiagnosticsHotnessRequested() is on or not. If it's off
|
||||
/// the operation is free.
|
||||
///
|
||||
/// Whereas if DiagnosticsHotnessRequested is on, it is fairly expensive
|
||||
/// operation since BFI and all its required analyses are computed. This is
|
||||
/// for example useful for CGSCC passes that can't use function analyses
|
||||
/// passes in the old PM.
|
||||
OptimizationRemarkEmitter(const Function *F);
|
||||
|
||||
OptimizationRemarkEmitter(OptimizationRemarkEmitter &&Arg)
|
||||
: F(Arg.F), BFI(Arg.BFI) {}
|
||||
|
||||
OptimizationRemarkEmitter &operator=(OptimizationRemarkEmitter &&RHS) {
|
||||
F = RHS.F;
|
||||
BFI = RHS.BFI;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Handle invalidation events in the new pass manager.
|
||||
bool invalidate(Function &F, const PreservedAnalyses &PA,
|
||||
FunctionAnalysisManager::Invalidator &Inv);
|
||||
|
||||
/// \brief Output the remark via the diagnostic handler and to the
|
||||
/// optimization record file.
|
||||
///
|
||||
/// This is the new interface that should be now used rather than the legacy
|
||||
/// emit* APIs.
|
||||
void emit(DiagnosticInfoOptimizationBase &OptDiag);
|
||||
|
||||
/// \brief Whether we allow for extra compile-time budget to perform more
|
||||
/// analysis to produce fewer false positives.
|
||||
///
|
||||
/// This is useful when reporting missed optimizations. In this case we can
|
||||
/// use the extra analysis (1) to filter trivial false positives or (2) to
|
||||
/// provide more context so that non-trivial false positives can be quickly
|
||||
/// detected by the user.
|
||||
bool allowExtraAnalysis() const {
|
||||
// For now, only allow this with -fsave-optimization-record since the -Rpass
|
||||
// options are handled in the front-end.
|
||||
return F->getContext().getDiagnosticsOutputFile();
|
||||
}
|
||||
|
||||
private:
|
||||
const Function *F;
|
||||
|
||||
BlockFrequencyInfo *BFI;
|
||||
|
||||
/// If we generate BFI on demand, we need to free it when ORE is freed.
|
||||
std::unique_ptr<BlockFrequencyInfo> OwnedBFI;
|
||||
|
||||
/// Compute hotness from IR value (currently assumed to be a block) if PGO is
|
||||
/// available.
|
||||
Optional<uint64_t> computeHotness(const Value *V);
|
||||
|
||||
/// Similar but use value from \p OptDiag and update hotness there.
|
||||
void computeHotness(DiagnosticInfoIROptimization &OptDiag);
|
||||
|
||||
/// \brief Only allow verbose messages if we know we're filtering by hotness
|
||||
/// (BFI is only set in this case).
|
||||
bool shouldEmitVerbose() { return BFI != nullptr; }
|
||||
|
||||
OptimizationRemarkEmitter(const OptimizationRemarkEmitter &) = delete;
|
||||
void operator=(const OptimizationRemarkEmitter &) = delete;
|
||||
};
|
||||
|
||||
/// \brief Add a small namespace to avoid name clashes with the classes used in
|
||||
/// the streaming interface. We want these to be short for better
|
||||
/// write/readability.
|
||||
namespace ore {
|
||||
using NV = DiagnosticInfoOptimizationBase::Argument;
|
||||
using setIsVerbose = DiagnosticInfoOptimizationBase::setIsVerbose;
|
||||
using setExtraArgs = DiagnosticInfoOptimizationBase::setExtraArgs;
|
||||
}
|
||||
|
||||
/// OptimizationRemarkEmitter legacy analysis pass
|
||||
///
|
||||
/// Note that this pass shouldn't generally be marked as preserved by other
|
||||
/// passes. It's holding onto BFI, so if the pass does not preserve BFI, BFI
|
||||
/// could be freed.
|
||||
class OptimizationRemarkEmitterWrapperPass : public FunctionPass {
|
||||
std::unique_ptr<OptimizationRemarkEmitter> ORE;
|
||||
|
||||
public:
|
||||
OptimizationRemarkEmitterWrapperPass();
|
||||
|
||||
bool runOnFunction(Function &F) override;
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override;
|
||||
|
||||
OptimizationRemarkEmitter &getORE() {
|
||||
assert(ORE && "pass not run yet");
|
||||
return *ORE;
|
||||
}
|
||||
|
||||
static char ID;
|
||||
};
|
||||
|
||||
class OptimizationRemarkEmitterAnalysis
|
||||
: public AnalysisInfoMixin<OptimizationRemarkEmitterAnalysis> {
|
||||
friend AnalysisInfoMixin<OptimizationRemarkEmitterAnalysis>;
|
||||
static AnalysisKey Key;
|
||||
|
||||
public:
|
||||
/// \brief Provide the result typedef for this analysis pass.
|
||||
typedef OptimizationRemarkEmitter Result;
|
||||
|
||||
/// \brief Run the analysis pass over a function and produce BFI.
|
||||
Result run(Function &F, FunctionAnalysisManager &AM);
|
||||
};
|
||||
|
||||
namespace yaml {
|
||||
template <> struct MappingTraits<DiagnosticInfoOptimizationBase *> {
|
||||
static void mapping(IO &io, DiagnosticInfoOptimizationBase *&OptDiag);
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif // LLVM_IR_OPTIMIZATIONDIAGNOSTICINFO_H
|
@ -1,8 +0,0 @@
|
||||
|
||||
#ifndef ELF_RELOC
|
||||
#error "ELF_RELOC must be defined"
|
||||
#endif
|
||||
|
||||
ELF_RELOC(R_WEBASSEMBLY_NONE, 0)
|
||||
ELF_RELOC(R_WEBASSEMBLY_DATA, 1)
|
||||
ELF_RELOC(R_WEBASSEMBLY_FUNCTION, 2)
|
@ -1,13 +0,0 @@
|
||||
|
||||
#ifndef WASM_RELOC
|
||||
#error "WASM_RELOC must be defined"
|
||||
#endif
|
||||
|
||||
WASM_RELOC(R_WEBASSEMBLY_FUNCTION_INDEX_LEB, 0)
|
||||
WASM_RELOC(R_WEBASSEMBLY_TABLE_INDEX_SLEB, 1)
|
||||
WASM_RELOC(R_WEBASSEMBLY_TABLE_INDEX_I32, 2)
|
||||
WASM_RELOC(R_WEBASSEMBLY_GLOBAL_ADDR_LEB, 3)
|
||||
WASM_RELOC(R_WEBASSEMBLY_GLOBAL_ADDR_SLEB, 4)
|
||||
WASM_RELOC(R_WEBASSEMBLY_GLOBAL_ADDR_I32, 5)
|
||||
WASM_RELOC(R_WEBASSEMBLY_TYPE_INDEX_LEB, 6)
|
||||
WASM_RELOC(R_WEBASSEMBLY_GLOBAL_INDEX_LEB, 7)
|
@ -1,382 +0,0 @@
|
||||
//===-- CommandFlags.h - Command Line Flags Interface -----------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains codegen-specific flags that are shared between different
|
||||
// command line tools. The tools "llc" and "opt" both use this file to prevent
|
||||
// flag duplication.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CODEGEN_COMMANDFLAGS_H
|
||||
#define LLVM_CODEGEN_COMMANDFLAGS_H
|
||||
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/Intrinsics.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/MC/MCTargetOptionsCommandFlags.h"
|
||||
#include "llvm/MC/SubtargetFeature.h"
|
||||
#include "llvm/Support/CodeGen.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Host.h"
|
||||
#include "llvm/Target/TargetMachine.h"
|
||||
#include "llvm/Target/TargetOptions.h"
|
||||
#include <string>
|
||||
using namespace llvm;
|
||||
|
||||
cl::opt<std::string>
|
||||
MArch("march", cl::desc("Architecture to generate code for (see --version)"));
|
||||
|
||||
cl::opt<std::string>
|
||||
MCPU("mcpu",
|
||||
cl::desc("Target a specific cpu type (-mcpu=help for details)"),
|
||||
cl::value_desc("cpu-name"),
|
||||
cl::init(""));
|
||||
|
||||
cl::list<std::string>
|
||||
MAttrs("mattr",
|
||||
cl::CommaSeparated,
|
||||
cl::desc("Target specific attributes (-mattr=help for details)"),
|
||||
cl::value_desc("a1,+a2,-a3,..."));
|
||||
|
||||
cl::opt<Reloc::Model> RelocModel(
|
||||
"relocation-model", cl::desc("Choose relocation model"),
|
||||
cl::values(
|
||||
clEnumValN(Reloc::Static, "static", "Non-relocatable code"),
|
||||
clEnumValN(Reloc::PIC_, "pic",
|
||||
"Fully relocatable, position independent code"),
|
||||
clEnumValN(Reloc::DynamicNoPIC, "dynamic-no-pic",
|
||||
"Relocatable external references, non-relocatable code"),
|
||||
clEnumValN(Reloc::ROPI, "ropi",
|
||||
"Code and read-only data relocatable, accessed PC-relative"),
|
||||
clEnumValN(Reloc::RWPI, "rwpi",
|
||||
"Read-write data relocatable, accessed relative to static base"),
|
||||
clEnumValN(Reloc::ROPI_RWPI, "ropi-rwpi",
|
||||
"Combination of ropi and rwpi")));
|
||||
|
||||
static inline Optional<Reloc::Model> getRelocModel() {
|
||||
if (RelocModel.getNumOccurrences()) {
|
||||
Reloc::Model R = RelocModel;
|
||||
return R;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
cl::opt<ThreadModel::Model>
|
||||
TMModel("thread-model",
|
||||
cl::desc("Choose threading model"),
|
||||
cl::init(ThreadModel::POSIX),
|
||||
cl::values(clEnumValN(ThreadModel::POSIX, "posix",
|
||||
"POSIX thread model"),
|
||||
clEnumValN(ThreadModel::Single, "single",
|
||||
"Single thread model")));
|
||||
|
||||
cl::opt<llvm::CodeModel::Model>
|
||||
CMModel("code-model",
|
||||
cl::desc("Choose code model"),
|
||||
cl::init(CodeModel::Default),
|
||||
cl::values(clEnumValN(CodeModel::Default, "default",
|
||||
"Target default code model"),
|
||||
clEnumValN(CodeModel::Small, "small",
|
||||
"Small code model"),
|
||||
clEnumValN(CodeModel::Kernel, "kernel",
|
||||
"Kernel code model"),
|
||||
clEnumValN(CodeModel::Medium, "medium",
|
||||
"Medium code model"),
|
||||
clEnumValN(CodeModel::Large, "large",
|
||||
"Large code model")));
|
||||
|
||||
cl::opt<llvm::ExceptionHandling>
|
||||
ExceptionModel("exception-model",
|
||||
cl::desc("exception model"),
|
||||
cl::init(ExceptionHandling::None),
|
||||
cl::values(clEnumValN(ExceptionHandling::None, "default",
|
||||
"default exception handling model"),
|
||||
clEnumValN(ExceptionHandling::DwarfCFI, "dwarf",
|
||||
"DWARF-like CFI based exception handling"),
|
||||
clEnumValN(ExceptionHandling::SjLj, "sjlj",
|
||||
"SjLj exception handling"),
|
||||
clEnumValN(ExceptionHandling::ARM, "arm",
|
||||
"ARM EHABI exceptions"),
|
||||
clEnumValN(ExceptionHandling::WinEH, "wineh",
|
||||
"Windows exception model")));
|
||||
|
||||
cl::opt<TargetMachine::CodeGenFileType>
|
||||
FileType("filetype", cl::init(TargetMachine::CGFT_AssemblyFile),
|
||||
cl::desc("Choose a file type (not all types are supported by all targets):"),
|
||||
cl::values(
|
||||
clEnumValN(TargetMachine::CGFT_AssemblyFile, "asm",
|
||||
"Emit an assembly ('.s') file"),
|
||||
clEnumValN(TargetMachine::CGFT_ObjectFile, "obj",
|
||||
"Emit a native object ('.o') file"),
|
||||
clEnumValN(TargetMachine::CGFT_Null, "null",
|
||||
"Emit nothing, for performance testing")));
|
||||
|
||||
cl::opt<bool>
|
||||
DisableFPElim("disable-fp-elim",
|
||||
cl::desc("Disable frame pointer elimination optimization"),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<bool>
|
||||
EnableUnsafeFPMath("enable-unsafe-fp-math",
|
||||
cl::desc("Enable optimizations that may decrease FP precision"),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<bool>
|
||||
EnableNoInfsFPMath("enable-no-infs-fp-math",
|
||||
cl::desc("Enable FP math optimizations that assume no +-Infs"),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<bool>
|
||||
EnableNoNaNsFPMath("enable-no-nans-fp-math",
|
||||
cl::desc("Enable FP math optimizations that assume no NaNs"),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<bool>
|
||||
EnableNoSignedZerosFPMath("enable-no-signed-zeros-fp-math",
|
||||
cl::desc("Enable FP math optimizations that assume "
|
||||
"the sign of 0 is insignificant"),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<bool>
|
||||
EnableNoTrappingFPMath("enable-no-trapping-fp-math",
|
||||
cl::desc("Enable setting the FP exceptions build "
|
||||
"attribute not to use exceptions"),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<llvm::FPDenormal::DenormalMode>
|
||||
DenormalMode("denormal-fp-math",
|
||||
cl::desc("Select which denormal numbers the code is permitted to require"),
|
||||
cl::init(FPDenormal::IEEE),
|
||||
cl::values(
|
||||
clEnumValN(FPDenormal::IEEE, "ieee",
|
||||
"IEEE 754 denormal numbers"),
|
||||
clEnumValN(FPDenormal::PreserveSign, "preserve-sign",
|
||||
"the sign of a flushed-to-zero number is preserved "
|
||||
"in the sign of 0"),
|
||||
clEnumValN(FPDenormal::PositiveZero, "positive-zero",
|
||||
"denormals are flushed to positive zero")));
|
||||
|
||||
cl::opt<bool>
|
||||
EnableHonorSignDependentRoundingFPMath("enable-sign-dependent-rounding-fp-math",
|
||||
cl::Hidden,
|
||||
cl::desc("Force codegen to assume rounding mode can change dynamically"),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<llvm::FloatABI::ABIType>
|
||||
FloatABIForCalls("float-abi",
|
||||
cl::desc("Choose float ABI type"),
|
||||
cl::init(FloatABI::Default),
|
||||
cl::values(
|
||||
clEnumValN(FloatABI::Default, "default",
|
||||
"Target default float ABI type"),
|
||||
clEnumValN(FloatABI::Soft, "soft",
|
||||
"Soft float ABI (implied by -soft-float)"),
|
||||
clEnumValN(FloatABI::Hard, "hard",
|
||||
"Hard float ABI (uses FP registers)")));
|
||||
|
||||
cl::opt<llvm::FPOpFusion::FPOpFusionMode>
|
||||
FuseFPOps("fp-contract",
|
||||
cl::desc("Enable aggressive formation of fused FP ops"),
|
||||
cl::init(FPOpFusion::Standard),
|
||||
cl::values(
|
||||
clEnumValN(FPOpFusion::Fast, "fast",
|
||||
"Fuse FP ops whenever profitable"),
|
||||
clEnumValN(FPOpFusion::Standard, "on",
|
||||
"Only fuse 'blessed' FP ops."),
|
||||
clEnumValN(FPOpFusion::Strict, "off",
|
||||
"Only fuse FP ops when the result won't be affected.")));
|
||||
|
||||
cl::opt<bool>
|
||||
DontPlaceZerosInBSS("nozero-initialized-in-bss",
|
||||
cl::desc("Don't place zero-initialized symbols into bss section"),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<bool>
|
||||
EnableGuaranteedTailCallOpt("tailcallopt",
|
||||
cl::desc("Turn fastcc calls into tail calls by (potentially) changing ABI."),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<bool>
|
||||
DisableTailCalls("disable-tail-calls",
|
||||
cl::desc("Never emit tail calls"),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<bool>
|
||||
StackSymbolOrdering("stack-symbol-ordering",
|
||||
cl::desc("Order local stack symbols."),
|
||||
cl::init(true));
|
||||
|
||||
cl::opt<unsigned>
|
||||
OverrideStackAlignment("stack-alignment",
|
||||
cl::desc("Override default stack alignment"),
|
||||
cl::init(0));
|
||||
|
||||
cl::opt<bool>
|
||||
StackRealign("stackrealign",
|
||||
cl::desc("Force align the stack to the minimum alignment"),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<std::string>
|
||||
TrapFuncName("trap-func", cl::Hidden,
|
||||
cl::desc("Emit a call to trap function rather than a trap instruction"),
|
||||
cl::init(""));
|
||||
|
||||
cl::opt<bool>
|
||||
UseCtors("use-ctors",
|
||||
cl::desc("Use .ctors instead of .init_array."),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<bool> RelaxELFRelocations(
|
||||
"relax-elf-relocations",
|
||||
cl::desc("Emit GOTPCRELX/REX_GOTPCRELX instead of GOTPCREL on x86-64 ELF"),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<bool> DataSections("data-sections",
|
||||
cl::desc("Emit data into separate sections"),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<bool>
|
||||
FunctionSections("function-sections",
|
||||
cl::desc("Emit functions into separate sections"),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<bool> EmulatedTLS("emulated-tls",
|
||||
cl::desc("Use emulated TLS model"),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<bool> UniqueSectionNames("unique-section-names",
|
||||
cl::desc("Give unique names to every section"),
|
||||
cl::init(true));
|
||||
|
||||
cl::opt<llvm::EABI> EABIVersion(
|
||||
"meabi", cl::desc("Set EABI type (default depends on triple):"),
|
||||
cl::init(EABI::Default),
|
||||
cl::values(clEnumValN(EABI::Default, "default",
|
||||
"Triple default EABI version"),
|
||||
clEnumValN(EABI::EABI4, "4", "EABI version 4"),
|
||||
clEnumValN(EABI::EABI5, "5", "EABI version 5"),
|
||||
clEnumValN(EABI::GNU, "gnu", "EABI GNU")));
|
||||
|
||||
cl::opt<DebuggerKind>
|
||||
DebuggerTuningOpt("debugger-tune",
|
||||
cl::desc("Tune debug info for a particular debugger"),
|
||||
cl::init(DebuggerKind::Default),
|
||||
cl::values(
|
||||
clEnumValN(DebuggerKind::GDB, "gdb", "gdb"),
|
||||
clEnumValN(DebuggerKind::LLDB, "lldb", "lldb"),
|
||||
clEnumValN(DebuggerKind::SCE, "sce",
|
||||
"SCE targets (e.g. PS4)")));
|
||||
|
||||
// Common utility function tightly tied to the options listed here. Initializes
|
||||
// a TargetOptions object with CodeGen flags and returns it.
|
||||
static inline TargetOptions InitTargetOptionsFromCodeGenFlags() {
|
||||
TargetOptions Options;
|
||||
Options.AllowFPOpFusion = FuseFPOps;
|
||||
Options.UnsafeFPMath = EnableUnsafeFPMath;
|
||||
Options.NoInfsFPMath = EnableNoInfsFPMath;
|
||||
Options.NoNaNsFPMath = EnableNoNaNsFPMath;
|
||||
Options.NoSignedZerosFPMath = EnableNoSignedZerosFPMath;
|
||||
Options.NoTrappingFPMath = EnableNoTrappingFPMath;
|
||||
Options.FPDenormalMode = DenormalMode;
|
||||
Options.HonorSignDependentRoundingFPMathOption =
|
||||
EnableHonorSignDependentRoundingFPMath;
|
||||
if (FloatABIForCalls != FloatABI::Default)
|
||||
Options.FloatABIType = FloatABIForCalls;
|
||||
Options.NoZerosInBSS = DontPlaceZerosInBSS;
|
||||
Options.GuaranteedTailCallOpt = EnableGuaranteedTailCallOpt;
|
||||
Options.StackAlignmentOverride = OverrideStackAlignment;
|
||||
Options.StackSymbolOrdering = StackSymbolOrdering;
|
||||
Options.UseInitArray = !UseCtors;
|
||||
Options.RelaxELFRelocations = RelaxELFRelocations;
|
||||
Options.DataSections = DataSections;
|
||||
Options.FunctionSections = FunctionSections;
|
||||
Options.UniqueSectionNames = UniqueSectionNames;
|
||||
Options.EmulatedTLS = EmulatedTLS;
|
||||
Options.ExceptionModel = ExceptionModel;
|
||||
|
||||
Options.MCOptions = InitMCTargetOptionsFromFlags();
|
||||
|
||||
Options.ThreadModel = TMModel;
|
||||
Options.EABIVersion = EABIVersion;
|
||||
Options.DebuggerTuning = DebuggerTuningOpt;
|
||||
|
||||
return Options;
|
||||
}
|
||||
|
||||
static inline std::string getCPUStr() {
|
||||
// If user asked for the 'native' CPU, autodetect here. If autodection fails,
|
||||
// this will set the CPU to an empty string which tells the target to
|
||||
// pick a basic default.
|
||||
if (MCPU == "native")
|
||||
return sys::getHostCPUName();
|
||||
|
||||
return MCPU;
|
||||
}
|
||||
|
||||
static inline std::string getFeaturesStr() {
|
||||
SubtargetFeatures Features;
|
||||
|
||||
// If user asked for the 'native' CPU, we need to autodetect features.
|
||||
// This is necessary for x86 where the CPU might not support all the
|
||||
// features the autodetected CPU name lists in the target. For example,
|
||||
// not all Sandybridge processors support AVX.
|
||||
if (MCPU == "native") {
|
||||
StringMap<bool> HostFeatures;
|
||||
if (sys::getHostCPUFeatures(HostFeatures))
|
||||
for (auto &F : HostFeatures)
|
||||
Features.AddFeature(F.first(), F.second);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i != MAttrs.size(); ++i)
|
||||
Features.AddFeature(MAttrs[i]);
|
||||
|
||||
return Features.getString();
|
||||
}
|
||||
|
||||
/// \brief Set function attributes of functions in Module M based on CPU,
|
||||
/// Features, and command line flags.
|
||||
static inline void setFunctionAttributes(StringRef CPU, StringRef Features,
|
||||
Module &M) {
|
||||
for (auto &F : M) {
|
||||
auto &Ctx = F.getContext();
|
||||
AttributeList Attrs = F.getAttributes();
|
||||
AttrBuilder NewAttrs;
|
||||
|
||||
if (!CPU.empty())
|
||||
NewAttrs.addAttribute("target-cpu", CPU);
|
||||
if (!Features.empty())
|
||||
NewAttrs.addAttribute("target-features", Features);
|
||||
if (DisableFPElim.getNumOccurrences() > 0)
|
||||
NewAttrs.addAttribute("no-frame-pointer-elim",
|
||||
DisableFPElim ? "true" : "false");
|
||||
if (DisableTailCalls.getNumOccurrences() > 0)
|
||||
NewAttrs.addAttribute("disable-tail-calls",
|
||||
toStringRef(DisableTailCalls));
|
||||
if (StackRealign)
|
||||
NewAttrs.addAttribute("stackrealign");
|
||||
|
||||
if (TrapFuncName.getNumOccurrences() > 0)
|
||||
for (auto &B : F)
|
||||
for (auto &I : B)
|
||||
if (auto *Call = dyn_cast<CallInst>(&I))
|
||||
if (const auto *F = Call->getCalledFunction())
|
||||
if (F->getIntrinsicID() == Intrinsic::debugtrap ||
|
||||
F->getIntrinsicID() == Intrinsic::trap)
|
||||
Call->addAttribute(
|
||||
llvm::AttributeList::FunctionIndex,
|
||||
Attribute::get(Ctx, "trap-func-name", TrapFuncName));
|
||||
|
||||
// Let NewAttrs override Attrs.
|
||||
F.setAttributes(
|
||||
Attrs.addAttributes(Ctx, AttributeList::FunctionIndex, NewAttrs));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,230 +0,0 @@
|
||||
//==- llvm/CodeGen/ExecutionDepsFix.h - Execution Dependency Fix -*- C++ -*-==//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
/// \file Execution Dependency Fix pass.
|
||||
///
|
||||
/// Some X86 SSE instructions like mov, and, or, xor are available in different
|
||||
/// variants for different operand types. These variant instructions are
|
||||
/// equivalent, but on Nehalem and newer cpus there is extra latency
|
||||
/// transferring data between integer and floating point domains. ARM cores
|
||||
/// have similar issues when they are configured with both VFP and NEON
|
||||
/// pipelines.
|
||||
///
|
||||
/// This pass changes the variant instructions to minimize domain crossings.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CODEGEN_EXECUTIONDEPSFIX_H
|
||||
#define LLVM_CODEGEN_EXECUTIONDEPSFIX_H
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/iterator_range.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/CodeGen/LivePhysRegs.h"
|
||||
#include "llvm/CodeGen/MachineFunction.h"
|
||||
#include "llvm/CodeGen/MachineFunctionPass.h"
|
||||
#include "llvm/CodeGen/RegisterClassInfo.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class MachineBasicBlock;
|
||||
class MachineInstr;
|
||||
class TargetInstrInfo;
|
||||
|
||||
/// A DomainValue is a bit like LiveIntervals' ValNo, but it also keeps track
|
||||
/// of execution domains.
|
||||
///
|
||||
/// An open DomainValue represents a set of instructions that can still switch
|
||||
/// execution domain. Multiple registers may refer to the same open
|
||||
/// DomainValue - they will eventually be collapsed to the same execution
|
||||
/// domain.
|
||||
///
|
||||
/// A collapsed DomainValue represents a single register that has been forced
|
||||
/// into one of more execution domains. There is a separate collapsed
|
||||
/// DomainValue for each register, but it may contain multiple execution
|
||||
/// domains. A register value is initially created in a single execution
|
||||
/// domain, but if we were forced to pay the penalty of a domain crossing, we
|
||||
/// keep track of the fact that the register is now available in multiple
|
||||
/// domains.
|
||||
struct DomainValue {
|
||||
// Basic reference counting.
|
||||
unsigned Refs = 0;
|
||||
|
||||
// Bitmask of available domains. For an open DomainValue, it is the still
|
||||
// possible domains for collapsing. For a collapsed DomainValue it is the
|
||||
// domains where the register is available for free.
|
||||
unsigned AvailableDomains;
|
||||
|
||||
// Pointer to the next DomainValue in a chain. When two DomainValues are
|
||||
// merged, Victim.Next is set to point to Victor, so old DomainValue
|
||||
// references can be updated by following the chain.
|
||||
DomainValue *Next;
|
||||
|
||||
// Twiddleable instructions using or defining these registers.
|
||||
SmallVector<MachineInstr*, 8> Instrs;
|
||||
|
||||
DomainValue() { clear(); }
|
||||
|
||||
// A collapsed DomainValue has no instructions to twiddle - it simply keeps
|
||||
// track of the domains where the registers are already available.
|
||||
bool isCollapsed() const { return Instrs.empty(); }
|
||||
|
||||
// Is domain available?
|
||||
bool hasDomain(unsigned domain) const {
|
||||
assert(domain <
|
||||
static_cast<unsigned>(std::numeric_limits<unsigned>::digits) &&
|
||||
"undefined behavior");
|
||||
return AvailableDomains & (1u << domain);
|
||||
}
|
||||
|
||||
// Mark domain as available.
|
||||
void addDomain(unsigned domain) {
|
||||
AvailableDomains |= 1u << domain;
|
||||
}
|
||||
|
||||
// Restrict to a single domain available.
|
||||
void setSingleDomain(unsigned domain) {
|
||||
AvailableDomains = 1u << domain;
|
||||
}
|
||||
|
||||
// Return bitmask of domains that are available and in mask.
|
||||
unsigned getCommonDomains(unsigned mask) const {
|
||||
return AvailableDomains & mask;
|
||||
}
|
||||
|
||||
// First domain available.
|
||||
unsigned getFirstDomain() const {
|
||||
return countTrailingZeros(AvailableDomains);
|
||||
}
|
||||
|
||||
// Clear this DomainValue and point to next which has all its data.
|
||||
void clear() {
|
||||
AvailableDomains = 0;
|
||||
Next = nullptr;
|
||||
Instrs.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/// Information about a live register.
|
||||
struct LiveReg {
|
||||
/// Value currently in this register, or NULL when no value is being tracked.
|
||||
/// This counts as a DomainValue reference.
|
||||
DomainValue *Value;
|
||||
|
||||
/// Instruction that defined this register, relative to the beginning of the
|
||||
/// current basic block. When a LiveReg is used to represent a live-out
|
||||
/// register, this value is relative to the end of the basic block, so it
|
||||
/// will be a negative number.
|
||||
int Def;
|
||||
};
|
||||
|
||||
class ExecutionDepsFix : public MachineFunctionPass {
|
||||
SpecificBumpPtrAllocator<DomainValue> Allocator;
|
||||
SmallVector<DomainValue*,16> Avail;
|
||||
|
||||
const TargetRegisterClass *const RC;
|
||||
MachineFunction *MF;
|
||||
const TargetInstrInfo *TII;
|
||||
const TargetRegisterInfo *TRI;
|
||||
RegisterClassInfo RegClassInfo;
|
||||
std::vector<SmallVector<int, 1>> AliasMap;
|
||||
const unsigned NumRegs;
|
||||
LiveReg *LiveRegs;
|
||||
struct MBBInfo {
|
||||
// Keeps clearance and domain information for all registers. Note that this
|
||||
// is different from the usual definition notion of liveness. The CPU
|
||||
// doesn't care whether or not we consider a register killed.
|
||||
LiveReg *OutRegs = nullptr;
|
||||
|
||||
// Whether we have gotten to this block in primary processing yet.
|
||||
bool PrimaryCompleted = false;
|
||||
|
||||
// The number of predecessors for which primary processing has completed
|
||||
unsigned IncomingProcessed = 0;
|
||||
|
||||
// The value of `IncomingProcessed` at the start of primary processing
|
||||
unsigned PrimaryIncoming = 0;
|
||||
|
||||
// The number of predecessors for which all processing steps are done.
|
||||
unsigned IncomingCompleted = 0;
|
||||
|
||||
MBBInfo() = default;
|
||||
};
|
||||
using MBBInfoMap = DenseMap<MachineBasicBlock *, MBBInfo>;
|
||||
MBBInfoMap MBBInfos;
|
||||
|
||||
/// List of undefined register reads in this block in forward order.
|
||||
std::vector<std::pair<MachineInstr *, unsigned>> UndefReads;
|
||||
|
||||
/// Storage for register unit liveness.
|
||||
LivePhysRegs LiveRegSet;
|
||||
|
||||
/// Current instruction number.
|
||||
/// The first instruction in each basic block is 0.
|
||||
int CurInstr;
|
||||
|
||||
public:
|
||||
ExecutionDepsFix(char &PassID, const TargetRegisterClass &RC)
|
||||
: MachineFunctionPass(PassID), RC(&RC), NumRegs(RC.getNumRegs()) {}
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.setPreservesAll();
|
||||
MachineFunctionPass::getAnalysisUsage(AU);
|
||||
}
|
||||
|
||||
bool runOnMachineFunction(MachineFunction &MF) override;
|
||||
|
||||
MachineFunctionProperties getRequiredProperties() const override {
|
||||
return MachineFunctionProperties().set(
|
||||
MachineFunctionProperties::Property::NoVRegs);
|
||||
}
|
||||
|
||||
private:
|
||||
iterator_range<SmallVectorImpl<int>::const_iterator>
|
||||
regIndices(unsigned Reg) const;
|
||||
// DomainValue allocation.
|
||||
DomainValue *alloc(int domain = -1);
|
||||
DomainValue *retain(DomainValue *DV) {
|
||||
if (DV) ++DV->Refs;
|
||||
return DV;
|
||||
}
|
||||
void release(DomainValue*);
|
||||
DomainValue *resolve(DomainValue*&);
|
||||
|
||||
// LiveRegs manipulations.
|
||||
void setLiveReg(int rx, DomainValue *DV);
|
||||
void kill(int rx);
|
||||
void force(int rx, unsigned domain);
|
||||
void collapse(DomainValue *dv, unsigned domain);
|
||||
bool merge(DomainValue *A, DomainValue *B);
|
||||
|
||||
void enterBasicBlock(MachineBasicBlock*);
|
||||
void leaveBasicBlock(MachineBasicBlock*);
|
||||
bool isBlockDone(MachineBasicBlock *);
|
||||
void processBasicBlock(MachineBasicBlock *MBB, bool PrimaryPass);
|
||||
bool visitInstr(MachineInstr *);
|
||||
void processDefs(MachineInstr *, bool breakDependency, bool Kill);
|
||||
void visitSoftInstr(MachineInstr*, unsigned mask);
|
||||
void visitHardInstr(MachineInstr*, unsigned domain);
|
||||
bool pickBestRegisterForUndef(MachineInstr *MI, unsigned OpIdx,
|
||||
unsigned Pref);
|
||||
bool shouldBreakDependence(MachineInstr*, unsigned OpIdx, unsigned Pref);
|
||||
void processUndefReads(MachineBasicBlock*);
|
||||
};
|
||||
|
||||
} // end namepsace llvm
|
||||
|
||||
#endif // LLVM_CODEGEN_EXECUTIONDEPSFIX_H
|
@ -1,39 +0,0 @@
|
||||
//===-- GISelAccessor.h - GISel Accessor ------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
/// This file declares the API to access the various APIs related
|
||||
/// to GlobalISel.
|
||||
//
|
||||
//===----------------------------------------------------------------------===/
|
||||
|
||||
#ifndef LLVM_CODEGEN_GLOBALISEL_GISELACCESSOR_H
|
||||
#define LLVM_CODEGEN_GLOBALISEL_GISELACCESSOR_H
|
||||
|
||||
namespace llvm {
|
||||
class CallLowering;
|
||||
class InstructionSelector;
|
||||
class LegalizerInfo;
|
||||
class RegisterBankInfo;
|
||||
|
||||
/// The goal of this helper class is to gather the accessor to all
|
||||
/// the APIs related to GlobalISel.
|
||||
/// It should be derived to feature an actual accessor to the GISel APIs.
|
||||
/// The reason why this is not simply done into the subtarget is to avoid
|
||||
/// spreading ifdefs around.
|
||||
struct GISelAccessor {
|
||||
virtual ~GISelAccessor() {}
|
||||
virtual const CallLowering *getCallLowering() const { return nullptr;}
|
||||
virtual const InstructionSelector *getInstructionSelector() const {
|
||||
return nullptr;
|
||||
}
|
||||
virtual const LegalizerInfo *getLegalizerInfo() const { return nullptr; }
|
||||
virtual const RegisterBankInfo *getRegBankInfo() const { return nullptr;}
|
||||
};
|
||||
} // End namespace llvm;
|
||||
#endif
|
@ -1,476 +0,0 @@
|
||||
//===- LiveIntervalAnalysis.h - Live Interval Analysis ----------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
/// \file This file implements the LiveInterval analysis pass. Given some
|
||||
/// numbering of each the machine instructions (in this implemention depth-first
|
||||
/// order) an interval [i, j) is said to be a live interval for register v if
|
||||
/// there is no instruction with number j' > j such that v is live at j' and
|
||||
/// there is no instruction with number i' < i such that v is live at i'. In
|
||||
/// this implementation intervals can have holes, i.e. an interval might look
|
||||
/// like [1,20), [50,65), [1000,1001).
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CODEGEN_LIVEINTERVALANALYSIS_H
|
||||
#define LLVM_CODEGEN_LIVEINTERVALANALYSIS_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/IndexedMap.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/Analysis/AliasAnalysis.h"
|
||||
#include "llvm/CodeGen/LiveInterval.h"
|
||||
#include "llvm/CodeGen/MachineBasicBlock.h"
|
||||
#include "llvm/CodeGen/MachineFunctionPass.h"
|
||||
#include "llvm/CodeGen/SlotIndexes.h"
|
||||
#include "llvm/MC/LaneBitmask.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Target/TargetRegisterInfo.h"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
extern cl::opt<bool> UseSegmentSetForPhysRegs;
|
||||
|
||||
class BitVector;
|
||||
class LiveRangeCalc;
|
||||
class MachineBlockFrequencyInfo;
|
||||
class MachineDominatorTree;
|
||||
class MachineFunction;
|
||||
class MachineInstr;
|
||||
class MachineRegisterInfo;
|
||||
class raw_ostream;
|
||||
class TargetInstrInfo;
|
||||
class VirtRegMap;
|
||||
|
||||
class LiveIntervals : public MachineFunctionPass {
|
||||
MachineFunction* MF;
|
||||
MachineRegisterInfo* MRI;
|
||||
const TargetRegisterInfo* TRI;
|
||||
const TargetInstrInfo* TII;
|
||||
AliasAnalysis *AA;
|
||||
SlotIndexes* Indexes;
|
||||
MachineDominatorTree *DomTree = nullptr;
|
||||
LiveRangeCalc *LRCalc = nullptr;
|
||||
|
||||
/// Special pool allocator for VNInfo's (LiveInterval val#).
|
||||
VNInfo::Allocator VNInfoAllocator;
|
||||
|
||||
/// Live interval pointers for all the virtual registers.
|
||||
IndexedMap<LiveInterval*, VirtReg2IndexFunctor> VirtRegIntervals;
|
||||
|
||||
/// Sorted list of instructions with register mask operands. Always use the
|
||||
/// 'r' slot, RegMasks are normal clobbers, not early clobbers.
|
||||
SmallVector<SlotIndex, 8> RegMaskSlots;
|
||||
|
||||
/// This vector is parallel to RegMaskSlots, it holds a pointer to the
|
||||
/// corresponding register mask. This pointer can be recomputed as:
|
||||
///
|
||||
/// MI = Indexes->getInstructionFromIndex(RegMaskSlot[N]);
|
||||
/// unsigned OpNum = findRegMaskOperand(MI);
|
||||
/// RegMaskBits[N] = MI->getOperand(OpNum).getRegMask();
|
||||
///
|
||||
/// This is kept in a separate vector partly because some standard
|
||||
/// libraries don't support lower_bound() with mixed objects, partly to
|
||||
/// improve locality when searching in RegMaskSlots.
|
||||
/// Also see the comment in LiveInterval::find().
|
||||
SmallVector<const uint32_t*, 8> RegMaskBits;
|
||||
|
||||
/// For each basic block number, keep (begin, size) pairs indexing into the
|
||||
/// RegMaskSlots and RegMaskBits arrays.
|
||||
/// Note that basic block numbers may not be layout contiguous, that's why
|
||||
/// we can't just keep track of the first register mask in each basic
|
||||
/// block.
|
||||
SmallVector<std::pair<unsigned, unsigned>, 8> RegMaskBlocks;
|
||||
|
||||
/// Keeps a live range set for each register unit to track fixed physreg
|
||||
/// interference.
|
||||
SmallVector<LiveRange*, 0> RegUnitRanges;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
LiveIntervals();
|
||||
~LiveIntervals() override;
|
||||
|
||||
/// Calculate the spill weight to assign to a single instruction.
|
||||
static float getSpillWeight(bool isDef, bool isUse,
|
||||
const MachineBlockFrequencyInfo *MBFI,
|
||||
const MachineInstr &Instr);
|
||||
|
||||
LiveInterval &getInterval(unsigned Reg) {
|
||||
if (hasInterval(Reg))
|
||||
return *VirtRegIntervals[Reg];
|
||||
else
|
||||
return createAndComputeVirtRegInterval(Reg);
|
||||
}
|
||||
|
||||
const LiveInterval &getInterval(unsigned Reg) const {
|
||||
return const_cast<LiveIntervals*>(this)->getInterval(Reg);
|
||||
}
|
||||
|
||||
bool hasInterval(unsigned Reg) const {
|
||||
return VirtRegIntervals.inBounds(Reg) && VirtRegIntervals[Reg];
|
||||
}
|
||||
|
||||
/// Interval creation.
|
||||
LiveInterval &createEmptyInterval(unsigned Reg) {
|
||||
assert(!hasInterval(Reg) && "Interval already exists!");
|
||||
VirtRegIntervals.grow(Reg);
|
||||
VirtRegIntervals[Reg] = createInterval(Reg);
|
||||
return *VirtRegIntervals[Reg];
|
||||
}
|
||||
|
||||
LiveInterval &createAndComputeVirtRegInterval(unsigned Reg) {
|
||||
LiveInterval &LI = createEmptyInterval(Reg);
|
||||
computeVirtRegInterval(LI);
|
||||
return LI;
|
||||
}
|
||||
|
||||
/// Interval removal.
|
||||
void removeInterval(unsigned Reg) {
|
||||
delete VirtRegIntervals[Reg];
|
||||
VirtRegIntervals[Reg] = nullptr;
|
||||
}
|
||||
|
||||
/// Given a register and an instruction, adds a live segment from that
|
||||
/// instruction to the end of its MBB.
|
||||
LiveInterval::Segment addSegmentToEndOfBlock(unsigned reg,
|
||||
MachineInstr &startInst);
|
||||
|
||||
/// After removing some uses of a register, shrink its live range to just
|
||||
/// the remaining uses. This method does not compute reaching defs for new
|
||||
/// uses, and it doesn't remove dead defs.
|
||||
/// Dead PHIDef values are marked as unused. New dead machine instructions
|
||||
/// are added to the dead vector. Returns true if the interval may have been
|
||||
/// separated into multiple connected components.
|
||||
bool shrinkToUses(LiveInterval *li,
|
||||
SmallVectorImpl<MachineInstr*> *dead = nullptr);
|
||||
|
||||
/// Specialized version of
|
||||
/// shrinkToUses(LiveInterval *li, SmallVectorImpl<MachineInstr*> *dead)
|
||||
/// that works on a subregister live range and only looks at uses matching
|
||||
/// the lane mask of the subregister range.
|
||||
/// This may leave the subrange empty which needs to be cleaned up with
|
||||
/// LiveInterval::removeEmptySubranges() afterwards.
|
||||
void shrinkToUses(LiveInterval::SubRange &SR, unsigned Reg);
|
||||
|
||||
/// Extend the live range \p LR to reach all points in \p Indices. The
|
||||
/// points in the \p Indices array must be jointly dominated by the union
|
||||
/// of the existing defs in \p LR and points in \p Undefs.
|
||||
///
|
||||
/// PHI-defs are added as needed to maintain SSA form.
|
||||
///
|
||||
/// If a SlotIndex in \p Indices is the end index of a basic block, \p LR
|
||||
/// will be extended to be live out of the basic block.
|
||||
/// If a SlotIndex in \p Indices is jointy dominated only by points in
|
||||
/// \p Undefs, the live range will not be extended to that point.
|
||||
///
|
||||
/// See also LiveRangeCalc::extend().
|
||||
void extendToIndices(LiveRange &LR, ArrayRef<SlotIndex> Indices,
|
||||
ArrayRef<SlotIndex> Undefs);
|
||||
|
||||
void extendToIndices(LiveRange &LR, ArrayRef<SlotIndex> Indices) {
|
||||
extendToIndices(LR, Indices, /*Undefs=*/{});
|
||||
}
|
||||
|
||||
/// If \p LR has a live value at \p Kill, prune its live range by removing
|
||||
/// any liveness reachable from Kill. Add live range end points to
|
||||
/// EndPoints such that extendToIndices(LI, EndPoints) will reconstruct the
|
||||
/// value's live range.
|
||||
///
|
||||
/// Calling pruneValue() and extendToIndices() can be used to reconstruct
|
||||
/// SSA form after adding defs to a virtual register.
|
||||
void pruneValue(LiveRange &LR, SlotIndex Kill,
|
||||
SmallVectorImpl<SlotIndex> *EndPoints);
|
||||
|
||||
/// This function should not be used. Its intend is to tell you that
|
||||
/// you are doing something wrong if you call pruveValue directly on a
|
||||
/// LiveInterval. Indeed, you are supposed to call pruneValue on the main
|
||||
/// LiveRange and all the LiveRange of the subranges if any.
|
||||
LLVM_ATTRIBUTE_UNUSED void pruneValue(LiveInterval &, SlotIndex,
|
||||
SmallVectorImpl<SlotIndex> *) {
|
||||
llvm_unreachable(
|
||||
"Use pruneValue on the main LiveRange and on each subrange");
|
||||
}
|
||||
|
||||
SlotIndexes *getSlotIndexes() const {
|
||||
return Indexes;
|
||||
}
|
||||
|
||||
AliasAnalysis *getAliasAnalysis() const {
|
||||
return AA;
|
||||
}
|
||||
|
||||
/// Returns true if the specified machine instr has been removed or was
|
||||
/// never entered in the map.
|
||||
bool isNotInMIMap(const MachineInstr &Instr) const {
|
||||
return !Indexes->hasIndex(Instr);
|
||||
}
|
||||
|
||||
/// Returns the base index of the given instruction.
|
||||
SlotIndex getInstructionIndex(const MachineInstr &Instr) const {
|
||||
return Indexes->getInstructionIndex(Instr);
|
||||
}
|
||||
|
||||
/// Returns the instruction associated with the given index.
|
||||
MachineInstr* getInstructionFromIndex(SlotIndex index) const {
|
||||
return Indexes->getInstructionFromIndex(index);
|
||||
}
|
||||
|
||||
/// Return the first index in the given basic block.
|
||||
SlotIndex getMBBStartIdx(const MachineBasicBlock *mbb) const {
|
||||
return Indexes->getMBBStartIdx(mbb);
|
||||
}
|
||||
|
||||
/// Return the last index in the given basic block.
|
||||
SlotIndex getMBBEndIdx(const MachineBasicBlock *mbb) const {
|
||||
return Indexes->getMBBEndIdx(mbb);
|
||||
}
|
||||
|
||||
bool isLiveInToMBB(const LiveRange &LR,
|
||||
const MachineBasicBlock *mbb) const {
|
||||
return LR.liveAt(getMBBStartIdx(mbb));
|
||||
}
|
||||
|
||||
bool isLiveOutOfMBB(const LiveRange &LR,
|
||||
const MachineBasicBlock *mbb) const {
|
||||
return LR.liveAt(getMBBEndIdx(mbb).getPrevSlot());
|
||||
}
|
||||
|
||||
MachineBasicBlock* getMBBFromIndex(SlotIndex index) const {
|
||||
return Indexes->getMBBFromIndex(index);
|
||||
}
|
||||
|
||||
void insertMBBInMaps(MachineBasicBlock *MBB) {
|
||||
Indexes->insertMBBInMaps(MBB);
|
||||
assert(unsigned(MBB->getNumber()) == RegMaskBlocks.size() &&
|
||||
"Blocks must be added in order.");
|
||||
RegMaskBlocks.push_back(std::make_pair(RegMaskSlots.size(), 0));
|
||||
}
|
||||
|
||||
SlotIndex InsertMachineInstrInMaps(MachineInstr &MI) {
|
||||
return Indexes->insertMachineInstrInMaps(MI);
|
||||
}
|
||||
|
||||
void InsertMachineInstrRangeInMaps(MachineBasicBlock::iterator B,
|
||||
MachineBasicBlock::iterator E) {
|
||||
for (MachineBasicBlock::iterator I = B; I != E; ++I)
|
||||
Indexes->insertMachineInstrInMaps(*I);
|
||||
}
|
||||
|
||||
void RemoveMachineInstrFromMaps(MachineInstr &MI) {
|
||||
Indexes->removeMachineInstrFromMaps(MI);
|
||||
}
|
||||
|
||||
SlotIndex ReplaceMachineInstrInMaps(MachineInstr &MI, MachineInstr &NewMI) {
|
||||
return Indexes->replaceMachineInstrInMaps(MI, NewMI);
|
||||
}
|
||||
|
||||
VNInfo::Allocator& getVNInfoAllocator() { return VNInfoAllocator; }
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override;
|
||||
void releaseMemory() override;
|
||||
|
||||
/// Pass entry point; Calculates LiveIntervals.
|
||||
bool runOnMachineFunction(MachineFunction&) override;
|
||||
|
||||
/// Implement the dump method.
|
||||
void print(raw_ostream &O, const Module* = nullptr) const override;
|
||||
|
||||
/// If LI is confined to a single basic block, return a pointer to that
|
||||
/// block. If LI is live in to or out of any block, return NULL.
|
||||
MachineBasicBlock *intervalIsInOneMBB(const LiveInterval &LI) const;
|
||||
|
||||
/// Returns true if VNI is killed by any PHI-def values in LI.
|
||||
/// This may conservatively return true to avoid expensive computations.
|
||||
bool hasPHIKill(const LiveInterval &LI, const VNInfo *VNI) const;
|
||||
|
||||
/// Add kill flags to any instruction that kills a virtual register.
|
||||
void addKillFlags(const VirtRegMap*);
|
||||
|
||||
/// Call this method to notify LiveIntervals that instruction \p MI has been
|
||||
/// moved within a basic block. This will update the live intervals for all
|
||||
/// operands of \p MI. Moves between basic blocks are not supported.
|
||||
///
|
||||
/// \param UpdateFlags Update live intervals for nonallocatable physregs.
|
||||
void handleMove(MachineInstr &MI, bool UpdateFlags = false);
|
||||
|
||||
/// Update intervals for operands of \p MI so that they begin/end on the
|
||||
/// SlotIndex for \p BundleStart.
|
||||
///
|
||||
/// \param UpdateFlags Update live intervals for nonallocatable physregs.
|
||||
///
|
||||
/// Requires MI and BundleStart to have SlotIndexes, and assumes
|
||||
/// existing liveness is accurate. BundleStart should be the first
|
||||
/// instruction in the Bundle.
|
||||
void handleMoveIntoBundle(MachineInstr &MI, MachineInstr &BundleStart,
|
||||
bool UpdateFlags = false);
|
||||
|
||||
/// Update live intervals for instructions in a range of iterators. It is
|
||||
/// intended for use after target hooks that may insert or remove
|
||||
/// instructions, and is only efficient for a small number of instructions.
|
||||
///
|
||||
/// OrigRegs is a vector of registers that were originally used by the
|
||||
/// instructions in the range between the two iterators.
|
||||
///
|
||||
/// Currently, the only only changes that are supported are simple removal
|
||||
/// and addition of uses.
|
||||
void repairIntervalsInRange(MachineBasicBlock *MBB,
|
||||
MachineBasicBlock::iterator Begin,
|
||||
MachineBasicBlock::iterator End,
|
||||
ArrayRef<unsigned> OrigRegs);
|
||||
|
||||
// Register mask functions.
|
||||
//
|
||||
// Machine instructions may use a register mask operand to indicate that a
|
||||
// large number of registers are clobbered by the instruction. This is
|
||||
// typically used for calls.
|
||||
//
|
||||
// For compile time performance reasons, these clobbers are not recorded in
|
||||
// the live intervals for individual physical registers. Instead,
|
||||
// LiveIntervalAnalysis maintains a sorted list of instructions with
|
||||
// register mask operands.
|
||||
|
||||
/// Returns a sorted array of slot indices of all instructions with
|
||||
/// register mask operands.
|
||||
ArrayRef<SlotIndex> getRegMaskSlots() const { return RegMaskSlots; }
|
||||
|
||||
/// Returns a sorted array of slot indices of all instructions with register
|
||||
/// mask operands in the basic block numbered \p MBBNum.
|
||||
ArrayRef<SlotIndex> getRegMaskSlotsInBlock(unsigned MBBNum) const {
|
||||
std::pair<unsigned, unsigned> P = RegMaskBlocks[MBBNum];
|
||||
return getRegMaskSlots().slice(P.first, P.second);
|
||||
}
|
||||
|
||||
/// Returns an array of register mask pointers corresponding to
|
||||
/// getRegMaskSlots().
|
||||
ArrayRef<const uint32_t*> getRegMaskBits() const { return RegMaskBits; }
|
||||
|
||||
/// Returns an array of mask pointers corresponding to
|
||||
/// getRegMaskSlotsInBlock(MBBNum).
|
||||
ArrayRef<const uint32_t*> getRegMaskBitsInBlock(unsigned MBBNum) const {
|
||||
std::pair<unsigned, unsigned> P = RegMaskBlocks[MBBNum];
|
||||
return getRegMaskBits().slice(P.first, P.second);
|
||||
}
|
||||
|
||||
/// Test if \p LI is live across any register mask instructions, and
|
||||
/// compute a bit mask of physical registers that are not clobbered by any
|
||||
/// of them.
|
||||
///
|
||||
/// Returns false if \p LI doesn't cross any register mask instructions. In
|
||||
/// that case, the bit vector is not filled in.
|
||||
bool checkRegMaskInterference(LiveInterval &LI,
|
||||
BitVector &UsableRegs);
|
||||
|
||||
// Register unit functions.
|
||||
//
|
||||
// Fixed interference occurs when MachineInstrs use physregs directly
|
||||
// instead of virtual registers. This typically happens when passing
|
||||
// arguments to a function call, or when instructions require operands in
|
||||
// fixed registers.
|
||||
//
|
||||
// Each physreg has one or more register units, see MCRegisterInfo. We
|
||||
// track liveness per register unit to handle aliasing registers more
|
||||
// efficiently.
|
||||
|
||||
/// Return the live range for register unit \p Unit. It will be computed if
|
||||
/// it doesn't exist.
|
||||
LiveRange &getRegUnit(unsigned Unit) {
|
||||
LiveRange *LR = RegUnitRanges[Unit];
|
||||
if (!LR) {
|
||||
// Compute missing ranges on demand.
|
||||
// Use segment set to speed-up initial computation of the live range.
|
||||
RegUnitRanges[Unit] = LR = new LiveRange(UseSegmentSetForPhysRegs);
|
||||
computeRegUnitRange(*LR, Unit);
|
||||
}
|
||||
return *LR;
|
||||
}
|
||||
|
||||
/// Return the live range for register unit \p Unit if it has already been
|
||||
/// computed, or nullptr if it hasn't been computed yet.
|
||||
LiveRange *getCachedRegUnit(unsigned Unit) {
|
||||
return RegUnitRanges[Unit];
|
||||
}
|
||||
|
||||
const LiveRange *getCachedRegUnit(unsigned Unit) const {
|
||||
return RegUnitRanges[Unit];
|
||||
}
|
||||
|
||||
/// Remove computed live range for register unit \p Unit. Subsequent uses
|
||||
/// should rely on on-demand recomputation.
|
||||
void removeRegUnit(unsigned Unit) {
|
||||
delete RegUnitRanges[Unit];
|
||||
RegUnitRanges[Unit] = nullptr;
|
||||
}
|
||||
|
||||
/// Remove value numbers and related live segments starting at position
|
||||
/// \p Pos that are part of any liverange of physical register \p Reg or one
|
||||
/// of its subregisters.
|
||||
void removePhysRegDefAt(unsigned Reg, SlotIndex Pos);
|
||||
|
||||
/// Remove value number and related live segments of \p LI and its subranges
|
||||
/// that start at position \p Pos.
|
||||
void removeVRegDefAt(LiveInterval &LI, SlotIndex Pos);
|
||||
|
||||
/// Split separate components in LiveInterval \p LI into separate intervals.
|
||||
void splitSeparateComponents(LiveInterval &LI,
|
||||
SmallVectorImpl<LiveInterval*> &SplitLIs);
|
||||
|
||||
/// For live interval \p LI with correct SubRanges construct matching
|
||||
/// information for the main live range. Expects the main live range to not
|
||||
/// have any segments or value numbers.
|
||||
void constructMainRangeFromSubranges(LiveInterval &LI);
|
||||
|
||||
private:
|
||||
/// Compute live intervals for all virtual registers.
|
||||
void computeVirtRegs();
|
||||
|
||||
/// Compute RegMaskSlots and RegMaskBits.
|
||||
void computeRegMasks();
|
||||
|
||||
/// Walk the values in \p LI and check for dead values:
|
||||
/// - Dead PHIDef values are marked as unused.
|
||||
/// - Dead operands are marked as such.
|
||||
/// - Completely dead machine instructions are added to the \p dead vector
|
||||
/// if it is not nullptr.
|
||||
/// Returns true if any PHI value numbers have been removed which may
|
||||
/// have separated the interval into multiple connected components.
|
||||
bool computeDeadValues(LiveInterval &LI,
|
||||
SmallVectorImpl<MachineInstr*> *dead);
|
||||
|
||||
static LiveInterval* createInterval(unsigned Reg);
|
||||
|
||||
void printInstrs(raw_ostream &O) const;
|
||||
void dumpInstrs() const;
|
||||
|
||||
void computeLiveInRegUnits();
|
||||
void computeRegUnitRange(LiveRange&, unsigned Unit);
|
||||
void computeVirtRegInterval(LiveInterval&);
|
||||
|
||||
|
||||
/// Helper function for repairIntervalsInRange(), walks backwards and
|
||||
/// creates/modifies live segments in \p LR to match the operands found.
|
||||
/// Only full operands or operands with subregisters matching \p LaneMask
|
||||
/// are considered.
|
||||
void repairOldRegInRange(MachineBasicBlock::iterator Begin,
|
||||
MachineBasicBlock::iterator End,
|
||||
const SlotIndex endIdx, LiveRange &LR,
|
||||
unsigned Reg,
|
||||
LaneBitmask LaneMask = LaneBitmask::getAll());
|
||||
|
||||
class HMEditor;
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_CODEGEN_LIVEINTERVALANALYSIS_H
|
@ -1,103 +0,0 @@
|
||||
//===- LiveStackAnalysis.h - Live Stack Slot Analysis -----------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the live stack slot analysis pass. It is analogous to
|
||||
// live interval analysis except it's analyzing liveness of stack slots rather
|
||||
// than registers.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CODEGEN_LIVESTACKANALYSIS_H
|
||||
#define LLVM_CODEGEN_LIVESTACKANALYSIS_H
|
||||
|
||||
#include "llvm/CodeGen/LiveInterval.h"
|
||||
#include "llvm/CodeGen/MachineFunctionPass.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class TargetRegisterClass;
|
||||
class TargetRegisterInfo;
|
||||
|
||||
class LiveStacks : public MachineFunctionPass {
|
||||
const TargetRegisterInfo *TRI;
|
||||
|
||||
/// Special pool allocator for VNInfo's (LiveInterval val#).
|
||||
///
|
||||
VNInfo::Allocator VNInfoAllocator;
|
||||
|
||||
/// S2IMap - Stack slot indices to live interval mapping.
|
||||
using SS2IntervalMap = std::unordered_map<int, LiveInterval>;
|
||||
SS2IntervalMap S2IMap;
|
||||
|
||||
/// S2RCMap - Stack slot indices to register class mapping.
|
||||
std::map<int, const TargetRegisterClass *> S2RCMap;
|
||||
|
||||
public:
|
||||
static char ID; // Pass identification, replacement for typeid
|
||||
|
||||
LiveStacks() : MachineFunctionPass(ID) {
|
||||
initializeLiveStacksPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
using iterator = SS2IntervalMap::iterator;
|
||||
using const_iterator = SS2IntervalMap::const_iterator;
|
||||
|
||||
const_iterator begin() const { return S2IMap.begin(); }
|
||||
const_iterator end() const { return S2IMap.end(); }
|
||||
iterator begin() { return S2IMap.begin(); }
|
||||
iterator end() { return S2IMap.end(); }
|
||||
|
||||
unsigned getNumIntervals() const { return (unsigned)S2IMap.size(); }
|
||||
|
||||
LiveInterval &getOrCreateInterval(int Slot, const TargetRegisterClass *RC);
|
||||
|
||||
LiveInterval &getInterval(int Slot) {
|
||||
assert(Slot >= 0 && "Spill slot indice must be >= 0");
|
||||
SS2IntervalMap::iterator I = S2IMap.find(Slot);
|
||||
assert(I != S2IMap.end() && "Interval does not exist for stack slot");
|
||||
return I->second;
|
||||
}
|
||||
|
||||
const LiveInterval &getInterval(int Slot) const {
|
||||
assert(Slot >= 0 && "Spill slot indice must be >= 0");
|
||||
SS2IntervalMap::const_iterator I = S2IMap.find(Slot);
|
||||
assert(I != S2IMap.end() && "Interval does not exist for stack slot");
|
||||
return I->second;
|
||||
}
|
||||
|
||||
bool hasInterval(int Slot) const { return S2IMap.count(Slot); }
|
||||
|
||||
const TargetRegisterClass *getIntervalRegClass(int Slot) const {
|
||||
assert(Slot >= 0 && "Spill slot indice must be >= 0");
|
||||
std::map<int, const TargetRegisterClass *>::const_iterator I =
|
||||
S2RCMap.find(Slot);
|
||||
assert(I != S2RCMap.end() &&
|
||||
"Register class info does not exist for stack slot");
|
||||
return I->second;
|
||||
}
|
||||
|
||||
VNInfo::Allocator &getVNInfoAllocator() { return VNInfoAllocator; }
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override;
|
||||
void releaseMemory() override;
|
||||
|
||||
/// runOnMachineFunction - pass entry point
|
||||
bool runOnMachineFunction(MachineFunction &) override;
|
||||
|
||||
/// print - Implement the dump method.
|
||||
void print(raw_ostream &O, const Module * = nullptr) const override;
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_CODEGEN_LIVESTACK_ANALYSIS_H
|
File diff suppressed because it is too large
Load Diff
@ -1,55 +0,0 @@
|
||||
//===- CVDebugRecord.h ------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_DEBUGINFO_CODEVIEW_CVDEBUGRECORD_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_CVDEBUGRECORD_H
|
||||
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace OMF {
|
||||
struct Signature {
|
||||
enum ID : uint32_t {
|
||||
PDB70 = 0x53445352, // RSDS
|
||||
PDB20 = 0x3031424e, // NB10
|
||||
CV50 = 0x3131424e, // NB11
|
||||
CV41 = 0x3930424e, // NB09
|
||||
};
|
||||
|
||||
support::ulittle32_t CVSignature;
|
||||
support::ulittle32_t Offset;
|
||||
};
|
||||
}
|
||||
|
||||
namespace codeview {
|
||||
struct PDB70DebugInfo {
|
||||
support::ulittle32_t CVSignature;
|
||||
uint8_t Signature[16];
|
||||
support::ulittle32_t Age;
|
||||
// char PDBFileName[];
|
||||
};
|
||||
|
||||
struct PDB20DebugInfo {
|
||||
support::ulittle32_t CVSignature;
|
||||
support::ulittle32_t Offset;
|
||||
support::ulittle32_t Signature;
|
||||
support::ulittle32_t Age;
|
||||
// char PDBFileName[];
|
||||
};
|
||||
|
||||
union DebugInfo {
|
||||
struct OMF::Signature Signature;
|
||||
struct PDB20DebugInfo PDB20;
|
||||
struct PDB70DebugInfo PDB70;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,22 +0,0 @@
|
||||
//===- TypeName.h --------------------------------------------- *- C++ --*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPENAME_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_TYPENAME_H
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/TypeCollection.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
std::string computeTypeName(TypeCollection &Types, TypeIndex Index);
|
||||
}
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
@ -1,78 +0,0 @@
|
||||
//===- TypeRecordBuilder.h --------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPERECORDBUILDER_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_TYPERECORDBUILDER_H
|
||||
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/Support/EndianStream.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
|
||||
class TypeRecordBuilder {
|
||||
private:
|
||||
TypeRecordBuilder(const TypeRecordBuilder &) = delete;
|
||||
TypeRecordBuilder &operator=(const TypeRecordBuilder &) = delete;
|
||||
|
||||
public:
|
||||
explicit TypeRecordBuilder(TypeRecordKind Kind);
|
||||
|
||||
void writeUInt8(uint8_t Value);
|
||||
void writeInt16(int16_t Value);
|
||||
void writeUInt16(uint16_t Value);
|
||||
void writeInt32(int32_t Value);
|
||||
void writeUInt32(uint32_t Value);
|
||||
void writeInt64(int64_t Value);
|
||||
void writeUInt64(uint64_t Value);
|
||||
void writeTypeIndex(TypeIndex TypeInd);
|
||||
void writeTypeRecordKind(TypeRecordKind Kind);
|
||||
void writeEncodedInteger(int64_t Value);
|
||||
void writeEncodedSignedInteger(int64_t Value);
|
||||
void writeEncodedUnsignedInteger(uint64_t Value);
|
||||
void writeNullTerminatedString(StringRef Value);
|
||||
void writeGuid(StringRef Guid);
|
||||
void writeBytes(StringRef Value) { Stream << Value; }
|
||||
|
||||
llvm::StringRef str();
|
||||
|
||||
uint64_t size() const { return Stream.tell(); }
|
||||
TypeRecordKind kind() const { return Kind; }
|
||||
|
||||
/// Returns the number of bytes remaining before this record is larger than
|
||||
/// the maximum record length. Accounts for the extra two byte size field in
|
||||
/// the header.
|
||||
size_t maxBytesRemaining() const { return MaxRecordLength - size() - 2; }
|
||||
|
||||
void truncate(uint64_t Size) {
|
||||
// This works because raw_svector_ostream is not buffered.
|
||||
assert(Size < Buffer.size());
|
||||
Buffer.resize(Size);
|
||||
}
|
||||
|
||||
void reset(TypeRecordKind K) {
|
||||
Buffer.clear();
|
||||
Kind = K;
|
||||
writeTypeRecordKind(K);
|
||||
}
|
||||
|
||||
private:
|
||||
TypeRecordKind Kind;
|
||||
llvm::SmallVector<char, 256> Buffer;
|
||||
llvm::raw_svector_ostream Stream;
|
||||
llvm::support::endian::Writer<llvm::support::endianness::little> Writer;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,159 +0,0 @@
|
||||
//===- TypeSerializer.h -----------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZER_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZER_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
||||
#include "llvm/DebugInfo/CodeView/RecordSerialization.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/BinaryByteStream.h"
|
||||
#include "llvm/Support/BinaryStreamWriter.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
|
||||
class TypeHasher;
|
||||
|
||||
class TypeSerializer : public TypeVisitorCallbacks {
|
||||
struct SubRecord {
|
||||
SubRecord(TypeLeafKind K, uint32_t S) : Kind(K), Size(S) {}
|
||||
|
||||
TypeLeafKind Kind;
|
||||
uint32_t Size = 0;
|
||||
};
|
||||
struct RecordSegment {
|
||||
SmallVector<SubRecord, 16> SubRecords;
|
||||
|
||||
uint32_t length() const {
|
||||
uint32_t L = sizeof(RecordPrefix);
|
||||
for (const auto &R : SubRecords) {
|
||||
L += R.Size;
|
||||
}
|
||||
return L;
|
||||
}
|
||||
};
|
||||
|
||||
using MutableRecordList = SmallVector<MutableArrayRef<uint8_t>, 2>;
|
||||
|
||||
static constexpr uint8_t ContinuationLength = 8;
|
||||
BumpPtrAllocator &RecordStorage;
|
||||
RecordSegment CurrentSegment;
|
||||
MutableRecordList FieldListSegments;
|
||||
|
||||
Optional<TypeLeafKind> TypeKind;
|
||||
Optional<TypeLeafKind> MemberKind;
|
||||
std::vector<uint8_t> RecordBuffer;
|
||||
MutableBinaryByteStream Stream;
|
||||
BinaryStreamWriter Writer;
|
||||
TypeRecordMapping Mapping;
|
||||
|
||||
/// Private type record hashing implementation details are handled here.
|
||||
std::unique_ptr<TypeHasher> Hasher;
|
||||
|
||||
/// Contains a list of all records indexed by TypeIndex.toArrayIndex().
|
||||
SmallVector<ArrayRef<uint8_t>, 2> SeenRecords;
|
||||
|
||||
/// Temporary storage that we use to copy a record's data while re-writing
|
||||
/// its type indices.
|
||||
SmallVector<uint8_t, 256> RemapStorage;
|
||||
|
||||
TypeIndex nextTypeIndex() const;
|
||||
|
||||
bool isInFieldList() const;
|
||||
MutableArrayRef<uint8_t> getCurrentSubRecordData();
|
||||
MutableArrayRef<uint8_t> getCurrentRecordData();
|
||||
Error writeRecordPrefix(TypeLeafKind Kind);
|
||||
|
||||
Expected<MutableArrayRef<uint8_t>>
|
||||
addPadding(MutableArrayRef<uint8_t> Record);
|
||||
|
||||
public:
|
||||
explicit TypeSerializer(BumpPtrAllocator &Storage, bool Hash = true);
|
||||
~TypeSerializer() override;
|
||||
|
||||
void reset();
|
||||
|
||||
BumpPtrAllocator &getAllocator() { return RecordStorage; }
|
||||
|
||||
ArrayRef<ArrayRef<uint8_t>> records() const;
|
||||
TypeIndex insertRecordBytes(ArrayRef<uint8_t> &Record);
|
||||
TypeIndex insertRecord(const RemappedType &Record);
|
||||
Expected<TypeIndex> visitTypeEndGetIndex(CVType &Record);
|
||||
|
||||
using TypeVisitorCallbacks::visitTypeBegin;
|
||||
Error visitTypeBegin(CVType &Record) override;
|
||||
Error visitTypeEnd(CVType &Record) override;
|
||||
Error visitMemberBegin(CVMemberRecord &Record) override;
|
||||
Error visitMemberEnd(CVMemberRecord &Record) override;
|
||||
|
||||
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
||||
virtual Error visitKnownRecord(CVType &CVR, Name##Record &Record) override { \
|
||||
return visitKnownRecordImpl(CVR, Record); \
|
||||
}
|
||||
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
||||
Error visitKnownMember(CVMemberRecord &CVR, Name##Record &Record) override { \
|
||||
return visitKnownMemberImpl<Name##Record>(CVR, Record); \
|
||||
}
|
||||
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
|
||||
|
||||
private:
|
||||
template <typename RecordKind>
|
||||
Error visitKnownRecordImpl(CVType &CVR, RecordKind &Record) {
|
||||
return Mapping.visitKnownRecord(CVR, Record);
|
||||
}
|
||||
|
||||
template <typename RecordType>
|
||||
Error visitKnownMemberImpl(CVMemberRecord &CVR, RecordType &Record) {
|
||||
assert(CVR.Kind == static_cast<TypeLeafKind>(Record.getKind()));
|
||||
|
||||
if (auto EC = Writer.writeEnum(CVR.Kind))
|
||||
return EC;
|
||||
|
||||
if (auto EC = Mapping.visitKnownMember(CVR, Record))
|
||||
return EC;
|
||||
|
||||
// Get all the data that was just written and is yet to be committed to
|
||||
// the current segment. Then pad it to 4 bytes.
|
||||
MutableArrayRef<uint8_t> ThisRecord = getCurrentSubRecordData();
|
||||
auto ExpectedRecord = addPadding(ThisRecord);
|
||||
if (!ExpectedRecord)
|
||||
return ExpectedRecord.takeError();
|
||||
ThisRecord = *ExpectedRecord;
|
||||
|
||||
CurrentSegment.SubRecords.emplace_back(CVR.Kind, ThisRecord.size());
|
||||
CVR.Data = ThisRecord;
|
||||
|
||||
// Both the last subrecord and the total length of this segment should be
|
||||
// multiples of 4.
|
||||
assert(ThisRecord.size() % 4 == 0);
|
||||
assert(CurrentSegment.length() % 4 == 0);
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace codeview
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZER_H
|
@ -1,137 +0,0 @@
|
||||
//===- TypeTableBuilder.h ---------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPETABLEBUILDER_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_TYPETABLEBUILDER_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeSerializer.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
|
||||
class TypeTableBuilder {
|
||||
private:
|
||||
TypeIndex handleError(Error EC) const {
|
||||
assert(false && "Couldn't write Type!");
|
||||
consumeError(std::move(EC));
|
||||
return TypeIndex();
|
||||
}
|
||||
|
||||
BumpPtrAllocator &Allocator;
|
||||
TypeSerializer Serializer;
|
||||
|
||||
public:
|
||||
explicit TypeTableBuilder(BumpPtrAllocator &Allocator,
|
||||
bool WriteUnique = true)
|
||||
: Allocator(Allocator), Serializer(Allocator, WriteUnique) {}
|
||||
TypeTableBuilder(const TypeTableBuilder &) = delete;
|
||||
TypeTableBuilder &operator=(const TypeTableBuilder &) = delete;
|
||||
|
||||
bool empty() const { return Serializer.records().empty(); }
|
||||
|
||||
BumpPtrAllocator &getAllocator() const { return Allocator; }
|
||||
|
||||
template <typename T> TypeIndex writeKnownType(T &Record) {
|
||||
static_assert(!std::is_same<T, FieldListRecord>::value,
|
||||
"Can't serialize FieldList!");
|
||||
|
||||
CVType Type;
|
||||
Type.Type = static_cast<TypeLeafKind>(Record.getKind());
|
||||
if (auto EC = Serializer.visitTypeBegin(Type))
|
||||
return handleError(std::move(EC));
|
||||
if (auto EC = Serializer.visitKnownRecord(Type, Record))
|
||||
return handleError(std::move(EC));
|
||||
|
||||
auto ExpectedIndex = Serializer.visitTypeEndGetIndex(Type);
|
||||
if (!ExpectedIndex)
|
||||
return handleError(ExpectedIndex.takeError());
|
||||
|
||||
return *ExpectedIndex;
|
||||
}
|
||||
|
||||
TypeIndex writeSerializedRecord(ArrayRef<uint8_t> Record) {
|
||||
return Serializer.insertRecordBytes(Record);
|
||||
}
|
||||
|
||||
TypeIndex writeSerializedRecord(const RemappedType &Record) {
|
||||
return Serializer.insertRecord(Record);
|
||||
}
|
||||
|
||||
template <typename TFunc> void ForEachRecord(TFunc Func) {
|
||||
uint32_t Index = TypeIndex::FirstNonSimpleIndex;
|
||||
|
||||
for (auto Record : Serializer.records()) {
|
||||
Func(TypeIndex(Index), Record);
|
||||
++Index;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayRef<ArrayRef<uint8_t>> records() const { return Serializer.records(); }
|
||||
};
|
||||
|
||||
class FieldListRecordBuilder {
|
||||
TypeTableBuilder &TypeTable;
|
||||
BumpPtrAllocator Allocator;
|
||||
TypeSerializer TempSerializer;
|
||||
CVType Type;
|
||||
|
||||
public:
|
||||
explicit FieldListRecordBuilder(TypeTableBuilder &TypeTable)
|
||||
: TypeTable(TypeTable), TempSerializer(Allocator, false) {
|
||||
Type.Type = TypeLeafKind::LF_FIELDLIST;
|
||||
}
|
||||
|
||||
void begin() {
|
||||
TempSerializer.reset();
|
||||
|
||||
if (auto EC = TempSerializer.visitTypeBegin(Type))
|
||||
consumeError(std::move(EC));
|
||||
}
|
||||
|
||||
template <typename T> void writeMemberType(T &Record) {
|
||||
CVMemberRecord CVMR;
|
||||
CVMR.Kind = static_cast<TypeLeafKind>(Record.getKind());
|
||||
if (auto EC = TempSerializer.visitMemberBegin(CVMR))
|
||||
consumeError(std::move(EC));
|
||||
if (auto EC = TempSerializer.visitKnownMember(CVMR, Record))
|
||||
consumeError(std::move(EC));
|
||||
if (auto EC = TempSerializer.visitMemberEnd(CVMR))
|
||||
consumeError(std::move(EC));
|
||||
}
|
||||
|
||||
TypeIndex end(bool Write) {
|
||||
TypeIndex Index;
|
||||
if (auto EC = TempSerializer.visitTypeEnd(Type)) {
|
||||
consumeError(std::move(EC));
|
||||
return TypeIndex();
|
||||
}
|
||||
|
||||
if (Write) {
|
||||
for (auto Record : TempSerializer.records())
|
||||
Index = TypeTable.writeSerializedRecord(Record);
|
||||
}
|
||||
|
||||
return Index;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace codeview
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_CODEVIEW_TYPETABLEBUILDER_H
|
@ -1,35 +0,0 @@
|
||||
//===- MSFStreamLayout.h - Describes the layout of a stream -----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_DEBUGINFO_MSF_MSFSTREAMLAYOUT_H
|
||||
#define LLVM_DEBUGINFO_MSF_MSFSTREAMLAYOUT_H
|
||||
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
namespace msf {
|
||||
|
||||
/// \brief Describes the layout of a stream in an MSF layout. A "stream" here
|
||||
/// is defined as any logical unit of data which may be arranged inside the MSF
|
||||
/// file as a sequence of (possibly discontiguous) blocks. When we want to read
|
||||
/// from a particular MSF Stream, we fill out a stream layout structure and the
|
||||
/// reader uses it to determine which blocks in the underlying MSF file contain
|
||||
/// the data, so that it can be pieced together in the right order.
|
||||
class MSFStreamLayout {
|
||||
public:
|
||||
uint32_t Length;
|
||||
std::vector<support::ulittle32_t> Blocks;
|
||||
};
|
||||
} // namespace msf
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_MSF_MSFSTREAMLAYOUT_H
|
@ -1,54 +0,0 @@
|
||||
//===- PublicsStreamBuilder.h - PDB Publics Stream Creation -----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_DEBUGINFO_PDB_RAW_PDBPUBLICSTREAMBUILDER_H
|
||||
#define LLVM_DEBUGINFO_PDB_RAW_PDBPUBLICSTREAMBUILDER_H
|
||||
|
||||
#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
|
||||
#include "llvm/Support/BinaryByteStream.h"
|
||||
#include "llvm/Support/BinaryStreamRef.h"
|
||||
#include "llvm/Support/BinaryStreamWriter.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace msf {
|
||||
class MSFBuilder;
|
||||
}
|
||||
namespace pdb {
|
||||
class PublicsStream;
|
||||
struct PublicsStreamHeader;
|
||||
|
||||
class PublicsStreamBuilder {
|
||||
public:
|
||||
explicit PublicsStreamBuilder(msf::MSFBuilder &Msf);
|
||||
~PublicsStreamBuilder();
|
||||
|
||||
PublicsStreamBuilder(const PublicsStreamBuilder &) = delete;
|
||||
PublicsStreamBuilder &operator=(const PublicsStreamBuilder &) = delete;
|
||||
|
||||
Error finalizeMsfLayout();
|
||||
uint32_t calculateSerializedLength() const;
|
||||
|
||||
Error commit(BinaryStreamWriter &PublicsWriter);
|
||||
|
||||
uint32_t getStreamIndex() const { return StreamIdx; }
|
||||
uint32_t getRecordStreamIdx() const { return RecordStreamIdx; }
|
||||
|
||||
private:
|
||||
uint32_t StreamIdx = kInvalidStreamIndex;
|
||||
uint32_t RecordStreamIdx = kInvalidStreamIndex;
|
||||
std::vector<PSHashRecord> HashRecords;
|
||||
msf::MSFBuilder &Msf;
|
||||
};
|
||||
} // namespace pdb
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
@ -1,63 +0,0 @@
|
||||
//===- ObjectMemoryBuffer.h - SmallVector-backed MemoryBuffrer -*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file declares a wrapper class to hold the memory into which an
|
||||
// object will be generated.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_EXECUTIONENGINE_OBJECTMEMORYBUFFER_H
|
||||
#define LLVM_EXECUTIONENGINE_OBJECTMEMORYBUFFER_H
|
||||
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// \brief SmallVector-backed MemoryBuffer instance.
|
||||
///
|
||||
/// This class enables efficient construction of MemoryBuffers from SmallVector
|
||||
/// instances. This is useful for MCJIT and Orc, where object files are streamed
|
||||
/// into SmallVectors, then inspected using ObjectFile (which takes a
|
||||
/// MemoryBuffer).
|
||||
class ObjectMemoryBuffer : public MemoryBuffer {
|
||||
public:
|
||||
|
||||
/// \brief Construct an ObjectMemoryBuffer from the given SmallVector r-value.
|
||||
///
|
||||
/// FIXME: It'd be nice for this to be a non-templated constructor taking a
|
||||
/// SmallVectorImpl here instead of a templated one taking a SmallVector<N>,
|
||||
/// but SmallVector's move-construction/assignment currently only take
|
||||
/// SmallVectors. If/when that is fixed we can simplify this constructor and
|
||||
/// the following one.
|
||||
ObjectMemoryBuffer(SmallVectorImpl<char> &&SV)
|
||||
: SV(std::move(SV)), BufferName("<in-memory object>") {
|
||||
init(this->SV.begin(), this->SV.end(), false);
|
||||
}
|
||||
|
||||
/// \brief Construct a named ObjectMemoryBuffer from the given SmallVector
|
||||
/// r-value and StringRef.
|
||||
ObjectMemoryBuffer(SmallVectorImpl<char> &&SV, StringRef Name)
|
||||
: SV(std::move(SV)), BufferName(Name) {
|
||||
init(this->SV.begin(), this->SV.end(), false);
|
||||
}
|
||||
|
||||
StringRef getBufferIdentifier() const override { return BufferName; }
|
||||
|
||||
BufferKind getBufferKind() const override { return MemoryBuffer_Malloc; }
|
||||
|
||||
private:
|
||||
SmallVector<char, 0> SV;
|
||||
std::string BufferName;
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
@ -1,80 +0,0 @@
|
||||
//===-- MCTargetOptionsCommandFlags.h --------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains machine code-specific flags that are shared between
|
||||
// different command line tools.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_MC_MCTARGETOPTIONSCOMMANDFLAGS_H
|
||||
#define LLVM_MC_MCTARGETOPTIONSCOMMANDFLAGS_H
|
||||
|
||||
#include "llvm/MC/MCTargetOptions.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
using namespace llvm;
|
||||
|
||||
cl::opt<MCTargetOptions::AsmInstrumentation> AsmInstrumentation(
|
||||
"asm-instrumentation", cl::desc("Instrumentation of inline assembly and "
|
||||
"assembly source files"),
|
||||
cl::init(MCTargetOptions::AsmInstrumentationNone),
|
||||
cl::values(clEnumValN(MCTargetOptions::AsmInstrumentationNone, "none",
|
||||
"no instrumentation at all"),
|
||||
clEnumValN(MCTargetOptions::AsmInstrumentationAddress, "address",
|
||||
"instrument instructions with memory arguments")));
|
||||
|
||||
cl::opt<bool> RelaxAll("mc-relax-all",
|
||||
cl::desc("When used with filetype=obj, "
|
||||
"relax all fixups in the emitted object file"));
|
||||
|
||||
cl::opt<bool> IncrementalLinkerCompatible(
|
||||
"incremental-linker-compatible",
|
||||
cl::desc(
|
||||
"When used with filetype=obj, "
|
||||
"emit an object file which can be used with an incremental linker"));
|
||||
|
||||
cl::opt<bool> PIECopyRelocations("pie-copy-relocations", cl::desc("PIE Copy Relocations"));
|
||||
|
||||
cl::opt<int> DwarfVersion("dwarf-version", cl::desc("Dwarf version"),
|
||||
cl::init(0));
|
||||
|
||||
cl::opt<bool> ShowMCInst("asm-show-inst",
|
||||
cl::desc("Emit internal instruction representation to "
|
||||
"assembly file"));
|
||||
|
||||
cl::opt<bool> FatalWarnings("fatal-warnings",
|
||||
cl::desc("Treat warnings as errors"));
|
||||
|
||||
cl::opt<bool> NoWarn("no-warn", cl::desc("Suppress all warnings"));
|
||||
cl::alias NoWarnW("W", cl::desc("Alias for --no-warn"), cl::aliasopt(NoWarn));
|
||||
|
||||
cl::opt<bool> NoDeprecatedWarn("no-deprecated-warn",
|
||||
cl::desc("Suppress all deprecated warnings"));
|
||||
|
||||
cl::opt<std::string>
|
||||
ABIName("target-abi", cl::Hidden,
|
||||
cl::desc("The name of the ABI to be targeted from the backend."),
|
||||
cl::init(""));
|
||||
|
||||
static inline MCTargetOptions InitMCTargetOptionsFromFlags() {
|
||||
MCTargetOptions Options;
|
||||
Options.SanitizeAddress =
|
||||
(AsmInstrumentation == MCTargetOptions::AsmInstrumentationAddress);
|
||||
Options.MCRelaxAll = RelaxAll;
|
||||
Options.MCIncrementalLinkerCompatible = IncrementalLinkerCompatible;
|
||||
Options.MCPIECopyRelocations = PIECopyRelocations;
|
||||
Options.DwarfVersion = DwarfVersion;
|
||||
Options.ShowMCInst = ShowMCInst;
|
||||
Options.ABIName = ABIName;
|
||||
Options.MCFatalWarnings = FatalWarnings;
|
||||
Options.MCNoWarn = NoWarn;
|
||||
Options.MCNoDeprecatedWarn = NoDeprecatedWarn;
|
||||
return Options;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,422 +0,0 @@
|
||||
//===--- AMDGPUCodeObjectMetadata.h -----------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
/// \file
|
||||
/// \brief AMDGPU Code Object Metadata definitions and in-memory
|
||||
/// representations.
|
||||
///
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_SUPPORT_AMDGPUCODEOBJECTMETADATA_H
|
||||
#define LLVM_SUPPORT_AMDGPUCODEOBJECTMETADATA_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
namespace AMDGPU {
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Code Object Metadata.
|
||||
//===----------------------------------------------------------------------===//
|
||||
namespace CodeObject {
|
||||
|
||||
/// \brief Code object metadata major version.
|
||||
constexpr uint32_t MetadataVersionMajor = 1;
|
||||
/// \brief Code object metadata minor version.
|
||||
constexpr uint32_t MetadataVersionMinor = 0;
|
||||
|
||||
/// \brief Code object metadata beginning assembler directive.
|
||||
constexpr char MetadataAssemblerDirectiveBegin[] =
|
||||
".amdgpu_code_object_metadata";
|
||||
/// \brief Code object metadata ending assembler directive.
|
||||
constexpr char MetadataAssemblerDirectiveEnd[] =
|
||||
".end_amdgpu_code_object_metadata";
|
||||
|
||||
/// \brief Access qualifiers.
|
||||
enum class AccessQualifier : uint8_t {
|
||||
Default = 0,
|
||||
ReadOnly = 1,
|
||||
WriteOnly = 2,
|
||||
ReadWrite = 3,
|
||||
Unknown = 0xff
|
||||
};
|
||||
|
||||
/// \brief Address space qualifiers.
|
||||
enum class AddressSpaceQualifier : uint8_t {
|
||||
Private = 0,
|
||||
Global = 1,
|
||||
Constant = 2,
|
||||
Local = 3,
|
||||
Generic = 4,
|
||||
Region = 5,
|
||||
Unknown = 0xff
|
||||
};
|
||||
|
||||
/// \brief Value kinds.
|
||||
enum class ValueKind : uint8_t {
|
||||
ByValue = 0,
|
||||
GlobalBuffer = 1,
|
||||
DynamicSharedPointer = 2,
|
||||
Sampler = 3,
|
||||
Image = 4,
|
||||
Pipe = 5,
|
||||
Queue = 6,
|
||||
HiddenGlobalOffsetX = 7,
|
||||
HiddenGlobalOffsetY = 8,
|
||||
HiddenGlobalOffsetZ = 9,
|
||||
HiddenNone = 10,
|
||||
HiddenPrintfBuffer = 11,
|
||||
HiddenDefaultQueue = 12,
|
||||
HiddenCompletionAction = 13,
|
||||
Unknown = 0xff
|
||||
};
|
||||
|
||||
/// \brief Value types.
|
||||
enum class ValueType : uint8_t {
|
||||
Struct = 0,
|
||||
I8 = 1,
|
||||
U8 = 2,
|
||||
I16 = 3,
|
||||
U16 = 4,
|
||||
F16 = 5,
|
||||
I32 = 6,
|
||||
U32 = 7,
|
||||
F32 = 8,
|
||||
I64 = 9,
|
||||
U64 = 10,
|
||||
F64 = 11,
|
||||
Unknown = 0xff
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Kernel Metadata.
|
||||
//===----------------------------------------------------------------------===//
|
||||
namespace Kernel {
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Kernel Attributes Metadata.
|
||||
//===----------------------------------------------------------------------===//
|
||||
namespace Attrs {
|
||||
|
||||
namespace Key {
|
||||
/// \brief Key for Kernel::Attr::Metadata::mReqdWorkGroupSize.
|
||||
constexpr char ReqdWorkGroupSize[] = "ReqdWorkGroupSize";
|
||||
/// \brief Key for Kernel::Attr::Metadata::mWorkGroupSizeHint.
|
||||
constexpr char WorkGroupSizeHint[] = "WorkGroupSizeHint";
|
||||
/// \brief Key for Kernel::Attr::Metadata::mVecTypeHint.
|
||||
constexpr char VecTypeHint[] = "VecTypeHint";
|
||||
} // end namespace Key
|
||||
|
||||
/// \brief In-memory representation of kernel attributes metadata.
|
||||
struct Metadata final {
|
||||
/// \brief 'reqd_work_group_size' attribute. Optional.
|
||||
std::vector<uint32_t> mReqdWorkGroupSize = std::vector<uint32_t>();
|
||||
/// \brief 'work_group_size_hint' attribute. Optional.
|
||||
std::vector<uint32_t> mWorkGroupSizeHint = std::vector<uint32_t>();
|
||||
/// \brief 'vec_type_hint' attribute. Optional.
|
||||
std::string mVecTypeHint = std::string();
|
||||
|
||||
/// \brief Default constructor.
|
||||
Metadata() = default;
|
||||
|
||||
/// \returns True if kernel attributes metadata is empty, false otherwise.
|
||||
bool empty() const {
|
||||
return mReqdWorkGroupSize.empty() &&
|
||||
mWorkGroupSizeHint.empty() &&
|
||||
mVecTypeHint.empty();
|
||||
}
|
||||
|
||||
/// \returns True if kernel attributes metadata is not empty, false otherwise.
|
||||
bool notEmpty() const {
|
||||
return !empty();
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace Attrs
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Kernel Argument Metadata.
|
||||
//===----------------------------------------------------------------------===//
|
||||
namespace Arg {
|
||||
|
||||
namespace Key {
|
||||
/// \brief Key for Kernel::Arg::Metadata::mSize.
|
||||
constexpr char Size[] = "Size";
|
||||
/// \brief Key for Kernel::Arg::Metadata::mAlign.
|
||||
constexpr char Align[] = "Align";
|
||||
/// \brief Key for Kernel::Arg::Metadata::mValueKind.
|
||||
constexpr char ValueKind[] = "ValueKind";
|
||||
/// \brief Key for Kernel::Arg::Metadata::mValueType.
|
||||
constexpr char ValueType[] = "ValueType";
|
||||
/// \brief Key for Kernel::Arg::Metadata::mPointeeAlign.
|
||||
constexpr char PointeeAlign[] = "PointeeAlign";
|
||||
/// \brief Key for Kernel::Arg::Metadata::mAccQual.
|
||||
constexpr char AccQual[] = "AccQual";
|
||||
/// \brief Key for Kernel::Arg::Metadata::mAddrSpaceQual.
|
||||
constexpr char AddrSpaceQual[] = "AddrSpaceQual";
|
||||
/// \brief Key for Kernel::Arg::Metadata::mIsConst.
|
||||
constexpr char IsConst[] = "IsConst";
|
||||
/// \brief Key for Kernel::Arg::Metadata::mIsPipe.
|
||||
constexpr char IsPipe[] = "IsPipe";
|
||||
/// \brief Key for Kernel::Arg::Metadata::mIsRestrict.
|
||||
constexpr char IsRestrict[] = "IsRestrict";
|
||||
/// \brief Key for Kernel::Arg::Metadata::mIsVolatile.
|
||||
constexpr char IsVolatile[] = "IsVolatile";
|
||||
/// \brief Key for Kernel::Arg::Metadata::mName.
|
||||
constexpr char Name[] = "Name";
|
||||
/// \brief Key for Kernel::Arg::Metadata::mTypeName.
|
||||
constexpr char TypeName[] = "TypeName";
|
||||
} // end namespace Key
|
||||
|
||||
/// \brief In-memory representation of kernel argument metadata.
|
||||
struct Metadata final {
|
||||
/// \brief Size in bytes. Required.
|
||||
uint32_t mSize = 0;
|
||||
/// \brief Alignment in bytes. Required.
|
||||
uint32_t mAlign = 0;
|
||||
/// \brief Value kind. Required.
|
||||
ValueKind mValueKind = ValueKind::Unknown;
|
||||
/// \brief Value type. Required.
|
||||
ValueType mValueType = ValueType::Unknown;
|
||||
/// \brief Pointee alignment in bytes. Optional.
|
||||
uint32_t mPointeeAlign = 0;
|
||||
/// \brief Access qualifier. Optional.
|
||||
AccessQualifier mAccQual = AccessQualifier::Unknown;
|
||||
/// \brief Address space qualifier. Optional.
|
||||
AddressSpaceQualifier mAddrSpaceQual = AddressSpaceQualifier::Unknown;
|
||||
/// \brief True if 'const' qualifier is specified. Optional.
|
||||
bool mIsConst = false;
|
||||
/// \brief True if 'pipe' qualifier is specified. Optional.
|
||||
bool mIsPipe = false;
|
||||
/// \brief True if 'restrict' qualifier is specified. Optional.
|
||||
bool mIsRestrict = false;
|
||||
/// \brief True if 'volatile' qualifier is specified. Optional.
|
||||
bool mIsVolatile = false;
|
||||
/// \brief Name. Optional.
|
||||
std::string mName = std::string();
|
||||
/// \brief Type name. Optional.
|
||||
std::string mTypeName = std::string();
|
||||
|
||||
/// \brief Default constructor.
|
||||
Metadata() = default;
|
||||
};
|
||||
|
||||
} // end namespace Arg
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Kernel Code Properties Metadata.
|
||||
//===----------------------------------------------------------------------===//
|
||||
namespace CodeProps {
|
||||
|
||||
namespace Key {
|
||||
/// \brief Key for Kernel::CodeProps::Metadata::mKernargSegmentSize.
|
||||
constexpr char KernargSegmentSize[] = "KernargSegmentSize";
|
||||
/// \brief Key for Kernel::CodeProps::Metadata::mWorkgroupGroupSegmentSize.
|
||||
constexpr char WorkgroupGroupSegmentSize[] = "WorkgroupGroupSegmentSize";
|
||||
/// \brief Key for Kernel::CodeProps::Metadata::mWorkitemPrivateSegmentSize.
|
||||
constexpr char WorkitemPrivateSegmentSize[] = "WorkitemPrivateSegmentSize";
|
||||
/// \brief Key for Kernel::CodeProps::Metadata::mWavefrontNumSGPRs.
|
||||
constexpr char WavefrontNumSGPRs[] = "WavefrontNumSGPRs";
|
||||
/// \brief Key for Kernel::CodeProps::Metadata::mWorkitemNumVGPRs.
|
||||
constexpr char WorkitemNumVGPRs[] = "WorkitemNumVGPRs";
|
||||
/// \brief Key for Kernel::CodeProps::Metadata::mKernargSegmentAlign.
|
||||
constexpr char KernargSegmentAlign[] = "KernargSegmentAlign";
|
||||
/// \brief Key for Kernel::CodeProps::Metadata::mGroupSegmentAlign.
|
||||
constexpr char GroupSegmentAlign[] = "GroupSegmentAlign";
|
||||
/// \brief Key for Kernel::CodeProps::Metadata::mPrivateSegmentAlign.
|
||||
constexpr char PrivateSegmentAlign[] = "PrivateSegmentAlign";
|
||||
/// \brief Key for Kernel::CodeProps::Metadata::mWavefrontSize.
|
||||
constexpr char WavefrontSize[] = "WavefrontSize";
|
||||
} // end namespace Key
|
||||
|
||||
/// \brief In-memory representation of kernel code properties metadata.
|
||||
struct Metadata final {
|
||||
/// \brief Size in bytes of the kernarg segment memory. Kernarg segment memory
|
||||
/// holds the values of the arguments to the kernel. Optional.
|
||||
uint64_t mKernargSegmentSize = 0;
|
||||
/// \brief Size in bytes of the group segment memory required by a workgroup.
|
||||
/// This value does not include any dynamically allocated group segment memory
|
||||
/// that may be added when the kernel is dispatched. Optional.
|
||||
uint32_t mWorkgroupGroupSegmentSize = 0;
|
||||
/// \brief Size in bytes of the private segment memory required by a workitem.
|
||||
/// Private segment memory includes arg, spill and private segments. Optional.
|
||||
uint32_t mWorkitemPrivateSegmentSize = 0;
|
||||
/// \brief Total number of SGPRs used by a wavefront. Optional.
|
||||
uint16_t mWavefrontNumSGPRs = 0;
|
||||
/// \brief Total number of VGPRs used by a workitem. Optional.
|
||||
uint16_t mWorkitemNumVGPRs = 0;
|
||||
/// \brief Maximum byte alignment of variables used by the kernel in the
|
||||
/// kernarg memory segment. Expressed as a power of two. Optional.
|
||||
uint8_t mKernargSegmentAlign = 0;
|
||||
/// \brief Maximum byte alignment of variables used by the kernel in the
|
||||
/// group memory segment. Expressed as a power of two. Optional.
|
||||
uint8_t mGroupSegmentAlign = 0;
|
||||
/// \brief Maximum byte alignment of variables used by the kernel in the
|
||||
/// private memory segment. Expressed as a power of two. Optional.
|
||||
uint8_t mPrivateSegmentAlign = 0;
|
||||
/// \brief Wavefront size. Expressed as a power of two. Optional.
|
||||
uint8_t mWavefrontSize = 0;
|
||||
|
||||
/// \brief Default constructor.
|
||||
Metadata() = default;
|
||||
|
||||
/// \returns True if kernel code properties metadata is empty, false
|
||||
/// otherwise.
|
||||
bool empty() const {
|
||||
return !notEmpty();
|
||||
}
|
||||
|
||||
/// \returns True if kernel code properties metadata is not empty, false
|
||||
/// otherwise.
|
||||
bool notEmpty() const {
|
||||
return mKernargSegmentSize || mWorkgroupGroupSegmentSize ||
|
||||
mWorkitemPrivateSegmentSize || mWavefrontNumSGPRs ||
|
||||
mWorkitemNumVGPRs || mKernargSegmentAlign || mGroupSegmentAlign ||
|
||||
mPrivateSegmentAlign || mWavefrontSize;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace CodeProps
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Kernel Debug Properties Metadata.
|
||||
//===----------------------------------------------------------------------===//
|
||||
namespace DebugProps {
|
||||
|
||||
namespace Key {
|
||||
/// \brief Key for Kernel::DebugProps::Metadata::mDebuggerABIVersion.
|
||||
constexpr char DebuggerABIVersion[] = "DebuggerABIVersion";
|
||||
/// \brief Key for Kernel::DebugProps::Metadata::mReservedNumVGPRs.
|
||||
constexpr char ReservedNumVGPRs[] = "ReservedNumVGPRs";
|
||||
/// \brief Key for Kernel::DebugProps::Metadata::mReservedFirstVGPR.
|
||||
constexpr char ReservedFirstVGPR[] = "ReservedFirstVGPR";
|
||||
/// \brief Key for Kernel::DebugProps::Metadata::mPrivateSegmentBufferSGPR.
|
||||
constexpr char PrivateSegmentBufferSGPR[] = "PrivateSegmentBufferSGPR";
|
||||
/// \brief Key for
|
||||
/// Kernel::DebugProps::Metadata::mWavefrontPrivateSegmentOffsetSGPR.
|
||||
constexpr char WavefrontPrivateSegmentOffsetSGPR[] =
|
||||
"WavefrontPrivateSegmentOffsetSGPR";
|
||||
} // end namespace Key
|
||||
|
||||
/// \brief In-memory representation of kernel debug properties metadata.
|
||||
struct Metadata final {
|
||||
/// \brief Debugger ABI version. Optional.
|
||||
std::vector<uint32_t> mDebuggerABIVersion = std::vector<uint32_t>();
|
||||
/// \brief Consecutive number of VGPRs reserved for debugger use. Must be 0 if
|
||||
/// mDebuggerABIVersion is not set. Optional.
|
||||
uint16_t mReservedNumVGPRs = 0;
|
||||
/// \brief First fixed VGPR reserved. Must be uint16_t(-1) if
|
||||
/// mDebuggerABIVersion is not set or mReservedFirstVGPR is 0. Optional.
|
||||
uint16_t mReservedFirstVGPR = uint16_t(-1);
|
||||
/// \brief Fixed SGPR of the first of 4 SGPRs used to hold the scratch V# used
|
||||
/// for the entire kernel execution. Must be uint16_t(-1) if
|
||||
/// mDebuggerABIVersion is not set or SGPR not used or not known. Optional.
|
||||
uint16_t mPrivateSegmentBufferSGPR = uint16_t(-1);
|
||||
/// \brief Fixed SGPR used to hold the wave scratch offset for the entire
|
||||
/// kernel execution. Must be uint16_t(-1) if mDebuggerABIVersion is not set
|
||||
/// or SGPR is not used or not known. Optional.
|
||||
uint16_t mWavefrontPrivateSegmentOffsetSGPR = uint16_t(-1);
|
||||
|
||||
/// \brief Default constructor.
|
||||
Metadata() = default;
|
||||
|
||||
/// \returns True if kernel debug properties metadata is empty, false
|
||||
/// otherwise.
|
||||
bool empty() const {
|
||||
return !notEmpty();
|
||||
}
|
||||
|
||||
/// \returns True if kernel debug properties metadata is not empty, false
|
||||
/// otherwise.
|
||||
bool notEmpty() const {
|
||||
return !mDebuggerABIVersion.empty();
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace DebugProps
|
||||
|
||||
namespace Key {
|
||||
/// \brief Key for Kernel::Metadata::mName.
|
||||
constexpr char Name[] = "Name";
|
||||
/// \brief Key for Kernel::Metadata::mLanguage.
|
||||
constexpr char Language[] = "Language";
|
||||
/// \brief Key for Kernel::Metadata::mLanguageVersion.
|
||||
constexpr char LanguageVersion[] = "LanguageVersion";
|
||||
/// \brief Key for Kernel::Metadata::mAttrs.
|
||||
constexpr char Attrs[] = "Attrs";
|
||||
/// \brief Key for Kernel::Metadata::mArgs.
|
||||
constexpr char Args[] = "Args";
|
||||
/// \brief Key for Kernel::Metadata::mCodeProps.
|
||||
constexpr char CodeProps[] = "CodeProps";
|
||||
/// \brief Key for Kernel::Metadata::mDebugProps.
|
||||
constexpr char DebugProps[] = "DebugProps";
|
||||
} // end namespace Key
|
||||
|
||||
/// \brief In-memory representation of kernel metadata.
|
||||
struct Metadata final {
|
||||
/// \brief Name. Required.
|
||||
std::string mName = std::string();
|
||||
/// \brief Language. Optional.
|
||||
std::string mLanguage = std::string();
|
||||
/// \brief Language version. Optional.
|
||||
std::vector<uint32_t> mLanguageVersion = std::vector<uint32_t>();
|
||||
/// \brief Attributes metadata. Optional.
|
||||
Attrs::Metadata mAttrs = Attrs::Metadata();
|
||||
/// \brief Arguments metadata. Optional.
|
||||
std::vector<Arg::Metadata> mArgs = std::vector<Arg::Metadata>();
|
||||
/// \brief Code properties metadata. Optional.
|
||||
CodeProps::Metadata mCodeProps = CodeProps::Metadata();
|
||||
/// \brief Debug properties metadata. Optional.
|
||||
DebugProps::Metadata mDebugProps = DebugProps::Metadata();
|
||||
|
||||
/// \brief Default constructor.
|
||||
Metadata() = default;
|
||||
};
|
||||
|
||||
} // end namespace Kernel
|
||||
|
||||
namespace Key {
|
||||
/// \brief Key for CodeObject::Metadata::mVersion.
|
||||
constexpr char Version[] = "Version";
|
||||
/// \brief Key for CodeObject::Metadata::mPrintf.
|
||||
constexpr char Printf[] = "Printf";
|
||||
/// \brief Key for CodeObject::Metadata::mKernels.
|
||||
constexpr char Kernels[] = "Kernels";
|
||||
} // end namespace Key
|
||||
|
||||
/// \brief In-memory representation of code object metadata.
|
||||
struct Metadata final {
|
||||
/// \brief Code object metadata version. Required.
|
||||
std::vector<uint32_t> mVersion = std::vector<uint32_t>();
|
||||
/// \brief Printf metadata. Optional.
|
||||
std::vector<std::string> mPrintf = std::vector<std::string>();
|
||||
/// \brief Kernels metadata. Optional.
|
||||
std::vector<Kernel::Metadata> mKernels = std::vector<Kernel::Metadata>();
|
||||
|
||||
/// \brief Default constructor.
|
||||
Metadata() = default;
|
||||
|
||||
/// \brief Converts \p YamlString to \p CodeObjectMetadata.
|
||||
static std::error_code fromYamlString(std::string YamlString,
|
||||
Metadata &CodeObjectMetadata);
|
||||
|
||||
/// \brief Converts \p CodeObjectMetadata to \p YamlString.
|
||||
static std::error_code toYamlString(Metadata CodeObjectMetadata,
|
||||
std::string &YamlString);
|
||||
};
|
||||
|
||||
} // end namespace CodeObject
|
||||
} // end namespace AMDGPU
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_SUPPORT_AMDGPUCODEOBJECTMETADATA_H
|
@ -1,64 +0,0 @@
|
||||
//===- llvm/Support/CodeGenCWrappers.h - CodeGen C Wrappers -----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines C bindings wrappers for enums in llvm/Support/CodeGen.h
|
||||
// that need them. The wrappers are separated to avoid adding an indirect
|
||||
// dependency on llvm/Config/Targets.def to CodeGen.h.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_SUPPORT_CODEGENCWRAPPERS_H
|
||||
#define LLVM_SUPPORT_CODEGENCWRAPPERS_H
|
||||
|
||||
#include "llvm-c/TargetMachine.h"
|
||||
#include "llvm/Support/CodeGen.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
inline CodeModel::Model unwrap(LLVMCodeModel Model) {
|
||||
switch (Model) {
|
||||
case LLVMCodeModelDefault:
|
||||
return CodeModel::Default;
|
||||
case LLVMCodeModelJITDefault:
|
||||
return CodeModel::JITDefault;
|
||||
case LLVMCodeModelSmall:
|
||||
return CodeModel::Small;
|
||||
case LLVMCodeModelKernel:
|
||||
return CodeModel::Kernel;
|
||||
case LLVMCodeModelMedium:
|
||||
return CodeModel::Medium;
|
||||
case LLVMCodeModelLarge:
|
||||
return CodeModel::Large;
|
||||
}
|
||||
return CodeModel::Default;
|
||||
}
|
||||
|
||||
inline LLVMCodeModel wrap(CodeModel::Model Model) {
|
||||
switch (Model) {
|
||||
case CodeModel::Default:
|
||||
return LLVMCodeModelDefault;
|
||||
case CodeModel::JITDefault:
|
||||
return LLVMCodeModelJITDefault;
|
||||
case CodeModel::Small:
|
||||
return LLVMCodeModelSmall;
|
||||
case CodeModel::Kernel:
|
||||
return LLVMCodeModelKernel;
|
||||
case CodeModel::Medium:
|
||||
return LLVMCodeModelMedium;
|
||||
case CodeModel::Large:
|
||||
return LLVMCodeModelLarge;
|
||||
}
|
||||
llvm_unreachable("Bad CodeModel!");
|
||||
}
|
||||
|
||||
} // end llvm namespace
|
||||
|
||||
#endif
|
||||
|
@ -1,135 +0,0 @@
|
||||
/*===-- include/Support/DataTypes.h - Define fixed size types -----*- C -*-===*\
|
||||
|* *|
|
||||
|* The LLVM Compiler Infrastructure *|
|
||||
|* *|
|
||||
|* This file is distributed under the University of Illinois Open Source *|
|
||||
|* License. See LICENSE.TXT for details. *|
|
||||
|* *|
|
||||
|*===----------------------------------------------------------------------===*|
|
||||
|* *|
|
||||
|* This file contains definitions to figure out the size of _HOST_ data types.*|
|
||||
|* This file is important because different host OS's define different macros,*|
|
||||
|* which makes portability tough. This file exports the following *|
|
||||
|* definitions: *|
|
||||
|* *|
|
||||
|* [u]int(32|64)_t : typedefs for signed and unsigned 32/64 bit system types*|
|
||||
|* [U]INT(8|16|32|64)_(MIN|MAX) : Constants for the min and max values. *|
|
||||
|* *|
|
||||
|* No library is required when using these functions. *|
|
||||
|* *|
|
||||
|*===----------------------------------------------------------------------===*/
|
||||
|
||||
/* Please leave this file C-compatible. */
|
||||
|
||||
#ifndef SUPPORT_DATATYPES_H
|
||||
#define SUPPORT_DATATYPES_H
|
||||
|
||||
#cmakedefine HAVE_INTTYPES_H ${HAVE_INTTYPES_H}
|
||||
#cmakedefine HAVE_STDINT_H ${HAVE_STDINT_H}
|
||||
#cmakedefine HAVE_UINT64_T ${HAVE_UINT64_T}
|
||||
#cmakedefine HAVE_U_INT64_T ${HAVE_U_INT64_T}
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cmath>
|
||||
#else
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cinttypes>
|
||||
#else
|
||||
#ifdef HAVE_INTTYPES_H
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cstdint>
|
||||
#else
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#else
|
||||
#error "Compiler must provide an implementation of stdint.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
|
||||
#if !defined(UINT32_MAX)
|
||||
# error "The standard header <cstdint> is not C++11 compliant. Must #define "\
|
||||
"__STDC_LIMIT_MACROS before #including Support/DataTypes.h"
|
||||
#endif
|
||||
|
||||
#if !defined(UINT32_C)
|
||||
# error "The standard header <cstdint> is not C++11 compliant. Must #define "\
|
||||
"__STDC_CONSTANT_MACROS before #including Support/DataTypes.h"
|
||||
#endif
|
||||
|
||||
/* Note that <inttypes.h> includes <stdint.h>, if this is a C99 system. */
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef _AIX
|
||||
// GCC is strict about defining large constants: they must have LL modifier.
|
||||
#undef INT64_MAX
|
||||
#undef INT64_MIN
|
||||
#endif
|
||||
|
||||
/* Handle incorrect definition of uint64_t as u_int64_t */
|
||||
#ifndef HAVE_UINT64_T
|
||||
#ifdef HAVE_U_INT64_T
|
||||
typedef u_int64_t uint64_t;
|
||||
#else
|
||||
# error "Don't have a definition for uint64_t on this platform"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#else /* _MSC_VER */
|
||||
#ifdef __cplusplus
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#else
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(_WIN64)
|
||||
typedef signed __int64 ssize_t;
|
||||
#else
|
||||
typedef signed int ssize_t;
|
||||
#endif /* _WIN64 */
|
||||
|
||||
#ifndef HAVE_INTTYPES_H
|
||||
#define PRId64 "I64d"
|
||||
#define PRIi64 "I64i"
|
||||
#define PRIo64 "I64o"
|
||||
#define PRIu64 "I64u"
|
||||
#define PRIx64 "I64x"
|
||||
#define PRIX64 "I64X"
|
||||
|
||||
#define PRId32 "d"
|
||||
#define PRIi32 "i"
|
||||
#define PRIo32 "o"
|
||||
#define PRIu32 "u"
|
||||
#define PRIx32 "x"
|
||||
#define PRIX32 "X"
|
||||
#endif /* HAVE_INTTYPES_H */
|
||||
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
/* Set defaults for constants which we cannot find. */
|
||||
#if !defined(INT64_MAX)
|
||||
# define INT64_MAX 9223372036854775807LL
|
||||
#endif
|
||||
#if !defined(INT64_MIN)
|
||||
# define INT64_MIN ((-INT64_MAX)-1)
|
||||
#endif
|
||||
#if !defined(UINT64_MAX)
|
||||
# define UINT64_MAX 0xffffffffffffffffULL
|
||||
#endif
|
||||
|
||||
#ifndef HUGE_VALF
|
||||
#define HUGE_VALF (float)HUGE_VAL
|
||||
#endif
|
||||
|
||||
#endif /* SUPPORT_DATATYPES_H */
|
@ -1,460 +0,0 @@
|
||||
//===- GCOV.h - LLVM coverage tool ------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This header provides the interface to read and write coverage files that
|
||||
// use 'gcov' format.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_SUPPORT_GCOV_H
|
||||
#define LLVM_SUPPORT_GCOV_H
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/MapVector.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/iterator.h"
|
||||
#include "llvm/ADT/iterator_range.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class GCOVFunction;
|
||||
class GCOVBlock;
|
||||
class FileInfo;
|
||||
|
||||
namespace GCOV {
|
||||
|
||||
enum GCOVVersion { V402, V404, V704 };
|
||||
|
||||
/// \brief A struct for passing gcov options between functions.
|
||||
struct Options {
|
||||
Options(bool A, bool B, bool C, bool F, bool P, bool U, bool L, bool N)
|
||||
: AllBlocks(A), BranchInfo(B), BranchCount(C), FuncCoverage(F),
|
||||
PreservePaths(P), UncondBranch(U), LongFileNames(L), NoOutput(N) {}
|
||||
|
||||
bool AllBlocks;
|
||||
bool BranchInfo;
|
||||
bool BranchCount;
|
||||
bool FuncCoverage;
|
||||
bool PreservePaths;
|
||||
bool UncondBranch;
|
||||
bool LongFileNames;
|
||||
bool NoOutput;
|
||||
};
|
||||
|
||||
} // end namespace GCOV
|
||||
|
||||
/// GCOVBuffer - A wrapper around MemoryBuffer to provide GCOV specific
|
||||
/// read operations.
|
||||
class GCOVBuffer {
|
||||
public:
|
||||
GCOVBuffer(MemoryBuffer *B) : Buffer(B) {}
|
||||
|
||||
/// readGCNOFormat - Check GCNO signature is valid at the beginning of buffer.
|
||||
bool readGCNOFormat() {
|
||||
StringRef File = Buffer->getBuffer().slice(0, 4);
|
||||
if (File != "oncg") {
|
||||
errs() << "Unexpected file type: " << File << ".\n";
|
||||
return false;
|
||||
}
|
||||
Cursor = 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// readGCDAFormat - Check GCDA signature is valid at the beginning of buffer.
|
||||
bool readGCDAFormat() {
|
||||
StringRef File = Buffer->getBuffer().slice(0, 4);
|
||||
if (File != "adcg") {
|
||||
errs() << "Unexpected file type: " << File << ".\n";
|
||||
return false;
|
||||
}
|
||||
Cursor = 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// readGCOVVersion - Read GCOV version.
|
||||
bool readGCOVVersion(GCOV::GCOVVersion &Version) {
|
||||
StringRef VersionStr = Buffer->getBuffer().slice(Cursor, Cursor + 4);
|
||||
if (VersionStr == "*204") {
|
||||
Cursor += 4;
|
||||
Version = GCOV::V402;
|
||||
return true;
|
||||
}
|
||||
if (VersionStr == "*404") {
|
||||
Cursor += 4;
|
||||
Version = GCOV::V404;
|
||||
return true;
|
||||
}
|
||||
if (VersionStr == "*704") {
|
||||
Cursor += 4;
|
||||
Version = GCOV::V704;
|
||||
return true;
|
||||
}
|
||||
errs() << "Unexpected version: " << VersionStr << ".\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
/// readFunctionTag - If cursor points to a function tag then increment the
|
||||
/// cursor and return true otherwise return false.
|
||||
bool readFunctionTag() {
|
||||
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
|
||||
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' ||
|
||||
Tag[3] != '\1') {
|
||||
return false;
|
||||
}
|
||||
Cursor += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// readBlockTag - If cursor points to a block tag then increment the
|
||||
/// cursor and return true otherwise return false.
|
||||
bool readBlockTag() {
|
||||
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
|
||||
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x41' ||
|
||||
Tag[3] != '\x01') {
|
||||
return false;
|
||||
}
|
||||
Cursor += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// readEdgeTag - If cursor points to an edge tag then increment the
|
||||
/// cursor and return true otherwise return false.
|
||||
bool readEdgeTag() {
|
||||
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
|
||||
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x43' ||
|
||||
Tag[3] != '\x01') {
|
||||
return false;
|
||||
}
|
||||
Cursor += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// readLineTag - If cursor points to a line tag then increment the
|
||||
/// cursor and return true otherwise return false.
|
||||
bool readLineTag() {
|
||||
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
|
||||
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x45' ||
|
||||
Tag[3] != '\x01') {
|
||||
return false;
|
||||
}
|
||||
Cursor += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// readArcTag - If cursor points to an gcda arc tag then increment the
|
||||
/// cursor and return true otherwise return false.
|
||||
bool readArcTag() {
|
||||
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
|
||||
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\xa1' ||
|
||||
Tag[3] != '\1') {
|
||||
return false;
|
||||
}
|
||||
Cursor += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// readObjectTag - If cursor points to an object summary tag then increment
|
||||
/// the cursor and return true otherwise return false.
|
||||
bool readObjectTag() {
|
||||
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
|
||||
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' ||
|
||||
Tag[3] != '\xa1') {
|
||||
return false;
|
||||
}
|
||||
Cursor += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// readProgramTag - If cursor points to a program summary tag then increment
|
||||
/// the cursor and return true otherwise return false.
|
||||
bool readProgramTag() {
|
||||
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
|
||||
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' ||
|
||||
Tag[3] != '\xa3') {
|
||||
return false;
|
||||
}
|
||||
Cursor += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readInt(uint32_t &Val) {
|
||||
if (Buffer->getBuffer().size() < Cursor + 4) {
|
||||
errs() << "Unexpected end of memory buffer: " << Cursor + 4 << ".\n";
|
||||
return false;
|
||||
}
|
||||
StringRef Str = Buffer->getBuffer().slice(Cursor, Cursor + 4);
|
||||
Cursor += 4;
|
||||
Val = *(const uint32_t *)(Str.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readInt64(uint64_t &Val) {
|
||||
uint32_t Lo, Hi;
|
||||
if (!readInt(Lo) || !readInt(Hi))
|
||||
return false;
|
||||
Val = ((uint64_t)Hi << 32) | Lo;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readString(StringRef &Str) {
|
||||
uint32_t Len = 0;
|
||||
// Keep reading until we find a non-zero length. This emulates gcov's
|
||||
// behaviour, which appears to do the same.
|
||||
while (Len == 0)
|
||||
if (!readInt(Len))
|
||||
return false;
|
||||
Len *= 4;
|
||||
if (Buffer->getBuffer().size() < Cursor + Len) {
|
||||
errs() << "Unexpected end of memory buffer: " << Cursor + Len << ".\n";
|
||||
return false;
|
||||
}
|
||||
Str = Buffer->getBuffer().slice(Cursor, Cursor + Len).split('\0').first;
|
||||
Cursor += Len;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t getCursor() const { return Cursor; }
|
||||
void advanceCursor(uint32_t n) { Cursor += n * 4; }
|
||||
|
||||
private:
|
||||
MemoryBuffer *Buffer;
|
||||
uint64_t Cursor = 0;
|
||||
};
|
||||
|
||||
/// GCOVFile - Collects coverage information for one pair of coverage file
|
||||
/// (.gcno and .gcda).
|
||||
class GCOVFile {
|
||||
public:
|
||||
GCOVFile() = default;
|
||||
|
||||
bool readGCNO(GCOVBuffer &Buffer);
|
||||
bool readGCDA(GCOVBuffer &Buffer);
|
||||
uint32_t getChecksum() const { return Checksum; }
|
||||
void print(raw_ostream &OS) const;
|
||||
void dump() const;
|
||||
void collectLineCounts(FileInfo &FI);
|
||||
|
||||
private:
|
||||
bool GCNOInitialized = false;
|
||||
GCOV::GCOVVersion Version;
|
||||
uint32_t Checksum = 0;
|
||||
SmallVector<std::unique_ptr<GCOVFunction>, 16> Functions;
|
||||
uint32_t RunCount = 0;
|
||||
uint32_t ProgramCount = 0;
|
||||
};
|
||||
|
||||
/// GCOVEdge - Collects edge information.
|
||||
struct GCOVEdge {
|
||||
GCOVEdge(GCOVBlock &S, GCOVBlock &D) : Src(S), Dst(D) {}
|
||||
|
||||
GCOVBlock &Src;
|
||||
GCOVBlock &Dst;
|
||||
uint64_t Count = 0;
|
||||
};
|
||||
|
||||
/// GCOVFunction - Collects function information.
|
||||
class GCOVFunction {
|
||||
public:
|
||||
using BlockIterator = pointee_iterator<SmallVectorImpl<
|
||||
std::unique_ptr<GCOVBlock>>::const_iterator>;
|
||||
|
||||
GCOVFunction(GCOVFile &P) : Parent(P) {}
|
||||
|
||||
bool readGCNO(GCOVBuffer &Buffer, GCOV::GCOVVersion Version);
|
||||
bool readGCDA(GCOVBuffer &Buffer, GCOV::GCOVVersion Version);
|
||||
StringRef getName() const { return Name; }
|
||||
StringRef getFilename() const { return Filename; }
|
||||
size_t getNumBlocks() const { return Blocks.size(); }
|
||||
uint64_t getEntryCount() const;
|
||||
uint64_t getExitCount() const;
|
||||
|
||||
BlockIterator block_begin() const { return Blocks.begin(); }
|
||||
BlockIterator block_end() const { return Blocks.end(); }
|
||||
iterator_range<BlockIterator> blocks() const {
|
||||
return make_range(block_begin(), block_end());
|
||||
}
|
||||
|
||||
void print(raw_ostream &OS) const;
|
||||
void dump() const;
|
||||
void collectLineCounts(FileInfo &FI);
|
||||
|
||||
private:
|
||||
GCOVFile &Parent;
|
||||
uint32_t Ident = 0;
|
||||
uint32_t Checksum;
|
||||
uint32_t LineNumber = 0;
|
||||
StringRef Name;
|
||||
StringRef Filename;
|
||||
SmallVector<std::unique_ptr<GCOVBlock>, 16> Blocks;
|
||||
SmallVector<std::unique_ptr<GCOVEdge>, 16> Edges;
|
||||
};
|
||||
|
||||
/// GCOVBlock - Collects block information.
|
||||
class GCOVBlock {
|
||||
struct EdgeWeight {
|
||||
EdgeWeight(GCOVBlock *D) : Dst(D) {}
|
||||
|
||||
GCOVBlock *Dst;
|
||||
uint64_t Count = 0;
|
||||
};
|
||||
|
||||
struct SortDstEdgesFunctor {
|
||||
bool operator()(const GCOVEdge *E1, const GCOVEdge *E2) {
|
||||
return E1->Dst.Number < E2->Dst.Number;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
using EdgeIterator = SmallVectorImpl<GCOVEdge *>::const_iterator;
|
||||
|
||||
GCOVBlock(GCOVFunction &P, uint32_t N) : Parent(P), Number(N) {}
|
||||
~GCOVBlock();
|
||||
|
||||
const GCOVFunction &getParent() const { return Parent; }
|
||||
void addLine(uint32_t N) { Lines.push_back(N); }
|
||||
uint32_t getLastLine() const { return Lines.back(); }
|
||||
void addCount(size_t DstEdgeNo, uint64_t N);
|
||||
uint64_t getCount() const { return Counter; }
|
||||
|
||||
void addSrcEdge(GCOVEdge *Edge) {
|
||||
assert(&Edge->Dst == this); // up to caller to ensure edge is valid
|
||||
SrcEdges.push_back(Edge);
|
||||
}
|
||||
|
||||
void addDstEdge(GCOVEdge *Edge) {
|
||||
assert(&Edge->Src == this); // up to caller to ensure edge is valid
|
||||
// Check if adding this edge causes list to become unsorted.
|
||||
if (DstEdges.size() && DstEdges.back()->Dst.Number > Edge->Dst.Number)
|
||||
DstEdgesAreSorted = false;
|
||||
DstEdges.push_back(Edge);
|
||||
}
|
||||
|
||||
size_t getNumSrcEdges() const { return SrcEdges.size(); }
|
||||
size_t getNumDstEdges() const { return DstEdges.size(); }
|
||||
void sortDstEdges();
|
||||
|
||||
EdgeIterator src_begin() const { return SrcEdges.begin(); }
|
||||
EdgeIterator src_end() const { return SrcEdges.end(); }
|
||||
iterator_range<EdgeIterator> srcs() const {
|
||||
return make_range(src_begin(), src_end());
|
||||
}
|
||||
|
||||
EdgeIterator dst_begin() const { return DstEdges.begin(); }
|
||||
EdgeIterator dst_end() const { return DstEdges.end(); }
|
||||
iterator_range<EdgeIterator> dsts() const {
|
||||
return make_range(dst_begin(), dst_end());
|
||||
}
|
||||
|
||||
void print(raw_ostream &OS) const;
|
||||
void dump() const;
|
||||
void collectLineCounts(FileInfo &FI);
|
||||
|
||||
private:
|
||||
GCOVFunction &Parent;
|
||||
uint32_t Number;
|
||||
uint64_t Counter = 0;
|
||||
bool DstEdgesAreSorted = true;
|
||||
SmallVector<GCOVEdge *, 16> SrcEdges;
|
||||
SmallVector<GCOVEdge *, 16> DstEdges;
|
||||
SmallVector<uint32_t, 16> Lines;
|
||||
};
|
||||
|
||||
class FileInfo {
|
||||
// It is unlikely--but possible--for multiple functions to be on the same
|
||||
// line.
|
||||
// Therefore this typedef allows LineData.Functions to store multiple
|
||||
// functions
|
||||
// per instance. This is rare, however, so optimize for the common case.
|
||||
using FunctionVector = SmallVector<const GCOVFunction *, 1>;
|
||||
using FunctionLines = DenseMap<uint32_t, FunctionVector>;
|
||||
using BlockVector = SmallVector<const GCOVBlock *, 4>;
|
||||
using BlockLines = DenseMap<uint32_t, BlockVector>;
|
||||
|
||||
struct LineData {
|
||||
LineData() = default;
|
||||
|
||||
BlockLines Blocks;
|
||||
FunctionLines Functions;
|
||||
uint32_t LastLine = 0;
|
||||
};
|
||||
|
||||
struct GCOVCoverage {
|
||||
GCOVCoverage(StringRef Name) : Name(Name) {}
|
||||
|
||||
StringRef Name;
|
||||
|
||||
uint32_t LogicalLines = 0;
|
||||
uint32_t LinesExec = 0;
|
||||
|
||||
uint32_t Branches = 0;
|
||||
uint32_t BranchesExec = 0;
|
||||
uint32_t BranchesTaken = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
FileInfo(const GCOV::Options &Options) : Options(Options) {}
|
||||
|
||||
void addBlockLine(StringRef Filename, uint32_t Line, const GCOVBlock *Block) {
|
||||
if (Line > LineInfo[Filename].LastLine)
|
||||
LineInfo[Filename].LastLine = Line;
|
||||
LineInfo[Filename].Blocks[Line - 1].push_back(Block);
|
||||
}
|
||||
|
||||
void addFunctionLine(StringRef Filename, uint32_t Line,
|
||||
const GCOVFunction *Function) {
|
||||
if (Line > LineInfo[Filename].LastLine)
|
||||
LineInfo[Filename].LastLine = Line;
|
||||
LineInfo[Filename].Functions[Line - 1].push_back(Function);
|
||||
}
|
||||
|
||||
void setRunCount(uint32_t Runs) { RunCount = Runs; }
|
||||
void setProgramCount(uint32_t Programs) { ProgramCount = Programs; }
|
||||
void print(raw_ostream &OS, StringRef MainFilename, StringRef GCNOFile,
|
||||
StringRef GCDAFile);
|
||||
|
||||
private:
|
||||
std::string getCoveragePath(StringRef Filename, StringRef MainFilename);
|
||||
std::unique_ptr<raw_ostream> openCoveragePath(StringRef CoveragePath);
|
||||
void printFunctionSummary(raw_ostream &OS, const FunctionVector &Funcs) const;
|
||||
void printBlockInfo(raw_ostream &OS, const GCOVBlock &Block,
|
||||
uint32_t LineIndex, uint32_t &BlockNo) const;
|
||||
void printBranchInfo(raw_ostream &OS, const GCOVBlock &Block,
|
||||
GCOVCoverage &Coverage, uint32_t &EdgeNo);
|
||||
void printUncondBranchInfo(raw_ostream &OS, uint32_t &EdgeNo,
|
||||
uint64_t Count) const;
|
||||
|
||||
void printCoverage(raw_ostream &OS, const GCOVCoverage &Coverage) const;
|
||||
void printFuncCoverage(raw_ostream &OS) const;
|
||||
void printFileCoverage(raw_ostream &OS) const;
|
||||
|
||||
const GCOV::Options &Options;
|
||||
StringMap<LineData> LineInfo;
|
||||
uint32_t RunCount = 0;
|
||||
uint32_t ProgramCount = 0;
|
||||
|
||||
using FileCoverageList = SmallVector<std::pair<std::string, GCOVCoverage>, 4>;
|
||||
using FuncCoverageMap = MapVector<const GCOVFunction *, GCOVCoverage>;
|
||||
|
||||
FileCoverageList FileCoverages;
|
||||
FuncCoverageMap FuncCoverages;
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_SUPPORT_GCOV_H
|
@ -1,70 +0,0 @@
|
||||
//===-- CostTable.h - Instruction Cost Table handling -----------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief Cost tables and simple lookup functions
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_TARGET_COSTTABLE_H_
|
||||
#define LLVM_TARGET_COSTTABLE_H_
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/CodeGen/MachineValueType.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// Cost Table Entry
|
||||
struct CostTblEntry {
|
||||
int ISD;
|
||||
MVT::SimpleValueType Type;
|
||||
unsigned Cost;
|
||||
};
|
||||
|
||||
/// Find in cost table, TypeTy must be comparable to CompareTy by ==
|
||||
inline const CostTblEntry *CostTableLookup(ArrayRef<CostTblEntry> Tbl,
|
||||
int ISD, MVT Ty) {
|
||||
auto I = find_if(Tbl, [=](const CostTblEntry &Entry) {
|
||||
return ISD == Entry.ISD && Ty == Entry.Type;
|
||||
});
|
||||
if (I != Tbl.end())
|
||||
return I;
|
||||
|
||||
// Could not find an entry.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Type Conversion Cost Table
|
||||
struct TypeConversionCostTblEntry {
|
||||
int ISD;
|
||||
MVT::SimpleValueType Dst;
|
||||
MVT::SimpleValueType Src;
|
||||
unsigned Cost;
|
||||
};
|
||||
|
||||
/// Find in type conversion cost table, TypeTy must be comparable to CompareTy
|
||||
/// by ==
|
||||
inline const TypeConversionCostTblEntry *
|
||||
ConvertCostTableLookup(ArrayRef<TypeConversionCostTblEntry> Tbl,
|
||||
int ISD, MVT Dst, MVT Src) {
|
||||
auto I = find_if(Tbl, [=](const TypeConversionCostTblEntry &Entry) {
|
||||
return ISD == Entry.ISD && Src == Entry.Src && Dst == Entry.Dst;
|
||||
});
|
||||
if (I != Tbl.end())
|
||||
return I;
|
||||
|
||||
// Could not find an entry.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
|
||||
#endif /* LLVM_TARGET_COSTTABLE_H_ */
|
@ -1,204 +0,0 @@
|
||||
//===-- llvm/Target/TargetCallingConv.h - Calling Convention ----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines types for working with calling-convention information.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_TARGET_TARGETCALLINGCONV_H
|
||||
#define LLVM_TARGET_TARGETCALLINGCONV_H
|
||||
|
||||
#include "llvm/CodeGen/MachineValueType.h"
|
||||
#include "llvm/CodeGen/ValueTypes.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
|
||||
namespace llvm {
|
||||
namespace ISD {
|
||||
|
||||
struct ArgFlagsTy {
|
||||
private:
|
||||
unsigned IsZExt : 1; ///< Zero extended
|
||||
unsigned IsSExt : 1; ///< Sign extended
|
||||
unsigned IsInReg : 1; ///< Passed in register
|
||||
unsigned IsSRet : 1; ///< Hidden struct-ret ptr
|
||||
unsigned IsByVal : 1; ///< Struct passed by value
|
||||
unsigned IsNest : 1; ///< Nested fn static chain
|
||||
unsigned IsReturned : 1; ///< Always returned
|
||||
unsigned IsSplit : 1;
|
||||
unsigned IsInAlloca : 1; ///< Passed with inalloca
|
||||
unsigned IsSplitEnd : 1; ///< Last part of a split
|
||||
unsigned IsSwiftSelf : 1; ///< Swift self parameter
|
||||
unsigned IsSwiftError : 1; ///< Swift error parameter
|
||||
unsigned IsHva : 1; ///< HVA field for
|
||||
unsigned IsHvaStart : 1; ///< HVA structure start
|
||||
unsigned IsSecArgPass : 1; ///< Second argument
|
||||
unsigned ByValAlign : 4; ///< Log 2 of byval alignment
|
||||
unsigned OrigAlign : 5; ///< Log 2 of original alignment
|
||||
unsigned IsInConsecutiveRegsLast : 1;
|
||||
unsigned IsInConsecutiveRegs : 1;
|
||||
unsigned IsCopyElisionCandidate : 1; ///< Argument copy elision candidate
|
||||
|
||||
unsigned ByValSize; ///< Byval struct size
|
||||
|
||||
public:
|
||||
ArgFlagsTy()
|
||||
: IsZExt(0), IsSExt(0), IsInReg(0), IsSRet(0), IsByVal(0), IsNest(0),
|
||||
IsReturned(0), IsSplit(0), IsInAlloca(0), IsSplitEnd(0),
|
||||
IsSwiftSelf(0), IsSwiftError(0), IsHva(0), IsHvaStart(0),
|
||||
IsSecArgPass(0), ByValAlign(0), OrigAlign(0),
|
||||
IsInConsecutiveRegsLast(0), IsInConsecutiveRegs(0),
|
||||
IsCopyElisionCandidate(0), ByValSize(0) {
|
||||
static_assert(sizeof(*this) == 2 * sizeof(unsigned), "flags are too big");
|
||||
}
|
||||
|
||||
bool isZExt() const { return IsZExt; }
|
||||
void setZExt() { IsZExt = 1; }
|
||||
|
||||
bool isSExt() const { return IsSExt; }
|
||||
void setSExt() { IsSExt = 1; }
|
||||
|
||||
bool isInReg() const { return IsInReg; }
|
||||
void setInReg() { IsInReg = 1; }
|
||||
|
||||
bool isSRet() const { return IsSRet; }
|
||||
void setSRet() { IsSRet = 1; }
|
||||
|
||||
bool isByVal() const { return IsByVal; }
|
||||
void setByVal() { IsByVal = 1; }
|
||||
|
||||
bool isInAlloca() const { return IsInAlloca; }
|
||||
void setInAlloca() { IsInAlloca = 1; }
|
||||
|
||||
bool isSwiftSelf() const { return IsSwiftSelf; }
|
||||
void setSwiftSelf() { IsSwiftSelf = 1; }
|
||||
|
||||
bool isSwiftError() const { return IsSwiftError; }
|
||||
void setSwiftError() { IsSwiftError = 1; }
|
||||
|
||||
bool isHva() const { return IsHva; }
|
||||
void setHva() { IsHva = 1; }
|
||||
|
||||
bool isHvaStart() const { return IsHvaStart; }
|
||||
void setHvaStart() { IsHvaStart = 1; }
|
||||
|
||||
bool isSecArgPass() const { return IsSecArgPass; }
|
||||
void setSecArgPass() { IsSecArgPass = 1; }
|
||||
|
||||
bool isNest() const { return IsNest; }
|
||||
void setNest() { IsNest = 1; }
|
||||
|
||||
bool isReturned() const { return IsReturned; }
|
||||
void setReturned() { IsReturned = 1; }
|
||||
|
||||
bool isInConsecutiveRegs() const { return IsInConsecutiveRegs; }
|
||||
void setInConsecutiveRegs() { IsInConsecutiveRegs = 1; }
|
||||
|
||||
bool isInConsecutiveRegsLast() const { return IsInConsecutiveRegsLast; }
|
||||
void setInConsecutiveRegsLast() { IsInConsecutiveRegsLast = 1; }
|
||||
|
||||
bool isSplit() const { return IsSplit; }
|
||||
void setSplit() { IsSplit = 1; }
|
||||
|
||||
bool isSplitEnd() const { return IsSplitEnd; }
|
||||
void setSplitEnd() { IsSplitEnd = 1; }
|
||||
|
||||
bool isCopyElisionCandidate() const { return IsCopyElisionCandidate; }
|
||||
void setCopyElisionCandidate() { IsCopyElisionCandidate = 1; }
|
||||
|
||||
unsigned getByValAlign() const { return (1U << ByValAlign) / 2; }
|
||||
void setByValAlign(unsigned A) {
|
||||
ByValAlign = Log2_32(A) + 1;
|
||||
assert(getByValAlign() == A && "bitfield overflow");
|
||||
}
|
||||
|
||||
unsigned getOrigAlign() const { return (1U << OrigAlign) / 2; }
|
||||
void setOrigAlign(unsigned A) {
|
||||
OrigAlign = Log2_32(A) + 1;
|
||||
assert(getOrigAlign() == A && "bitfield overflow");
|
||||
}
|
||||
|
||||
unsigned getByValSize() const { return ByValSize; }
|
||||
void setByValSize(unsigned S) { ByValSize = S; }
|
||||
};
|
||||
|
||||
/// InputArg - This struct carries flags and type information about a
|
||||
/// single incoming (formal) argument or incoming (from the perspective
|
||||
/// of the caller) return value virtual register.
|
||||
///
|
||||
struct InputArg {
|
||||
ArgFlagsTy Flags;
|
||||
MVT VT = MVT::Other;
|
||||
EVT ArgVT;
|
||||
bool Used = false;
|
||||
|
||||
/// Index original Function's argument.
|
||||
unsigned OrigArgIndex;
|
||||
/// Sentinel value for implicit machine-level input arguments.
|
||||
static const unsigned NoArgIndex = UINT_MAX;
|
||||
|
||||
/// Offset in bytes of current input value relative to the beginning of
|
||||
/// original argument. E.g. if argument was splitted into four 32 bit
|
||||
/// registers, we got 4 InputArgs with PartOffsets 0, 4, 8 and 12.
|
||||
unsigned PartOffset;
|
||||
|
||||
InputArg() = default;
|
||||
InputArg(ArgFlagsTy flags, EVT vt, EVT argvt, bool used,
|
||||
unsigned origIdx, unsigned partOffs)
|
||||
: Flags(flags), Used(used), OrigArgIndex(origIdx), PartOffset(partOffs) {
|
||||
VT = vt.getSimpleVT();
|
||||
ArgVT = argvt;
|
||||
}
|
||||
|
||||
bool isOrigArg() const {
|
||||
return OrigArgIndex != NoArgIndex;
|
||||
}
|
||||
|
||||
unsigned getOrigArgIndex() const {
|
||||
assert(OrigArgIndex != NoArgIndex && "Implicit machine-level argument");
|
||||
return OrigArgIndex;
|
||||
}
|
||||
};
|
||||
|
||||
/// OutputArg - This struct carries flags and a value for a
|
||||
/// single outgoing (actual) argument or outgoing (from the perspective
|
||||
/// of the caller) return value virtual register.
|
||||
///
|
||||
struct OutputArg {
|
||||
ArgFlagsTy Flags;
|
||||
MVT VT;
|
||||
EVT ArgVT;
|
||||
|
||||
/// IsFixed - Is this a "fixed" value, ie not passed through a vararg "...".
|
||||
bool IsFixed = false;
|
||||
|
||||
/// Index original Function's argument.
|
||||
unsigned OrigArgIndex;
|
||||
|
||||
/// Offset in bytes of current output value relative to the beginning of
|
||||
/// original argument. E.g. if argument was splitted into four 32 bit
|
||||
/// registers, we got 4 OutputArgs with PartOffsets 0, 4, 8 and 12.
|
||||
unsigned PartOffset;
|
||||
|
||||
OutputArg() = default;
|
||||
OutputArg(ArgFlagsTy flags, EVT vt, EVT argvt, bool isfixed,
|
||||
unsigned origIdx, unsigned partOffs)
|
||||
: Flags(flags), IsFixed(isfixed), OrigArgIndex(origIdx),
|
||||
PartOffset(partOffs) {
|
||||
VT = vt.getSimpleVT();
|
||||
ArgVT = argvt;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace ISD
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_TARGET_TARGETCALLINGCONV_H
|
@ -1,346 +0,0 @@
|
||||
//===-- llvm/Target/TargetFrameLowering.h ---------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Interface to describe the layout of a stack frame on the target machine.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_TARGET_TARGETFRAMELOWERING_H
|
||||
#define LLVM_TARGET_TARGETFRAMELOWERING_H
|
||||
|
||||
#include "llvm/CodeGen/MachineBasicBlock.h"
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
class BitVector;
|
||||
class CalleeSavedInfo;
|
||||
class MachineFunction;
|
||||
class RegScavenger;
|
||||
|
||||
/// Information about stack frame layout on the target. It holds the direction
|
||||
/// of stack growth, the known stack alignment on entry to each function, and
|
||||
/// the offset to the locals area.
|
||||
///
|
||||
/// The offset to the local area is the offset from the stack pointer on
|
||||
/// function entry to the first location where function data (local variables,
|
||||
/// spill locations) can be stored.
|
||||
class TargetFrameLowering {
|
||||
public:
|
||||
enum StackDirection {
|
||||
StackGrowsUp, // Adding to the stack increases the stack address
|
||||
StackGrowsDown // Adding to the stack decreases the stack address
|
||||
};
|
||||
|
||||
// Maps a callee saved register to a stack slot with a fixed offset.
|
||||
struct SpillSlot {
|
||||
unsigned Reg;
|
||||
int Offset; // Offset relative to stack pointer on function entry.
|
||||
};
|
||||
private:
|
||||
StackDirection StackDir;
|
||||
unsigned StackAlignment;
|
||||
unsigned TransientStackAlignment;
|
||||
int LocalAreaOffset;
|
||||
bool StackRealignable;
|
||||
public:
|
||||
TargetFrameLowering(StackDirection D, unsigned StackAl, int LAO,
|
||||
unsigned TransAl = 1, bool StackReal = true)
|
||||
: StackDir(D), StackAlignment(StackAl), TransientStackAlignment(TransAl),
|
||||
LocalAreaOffset(LAO), StackRealignable(StackReal) {}
|
||||
|
||||
virtual ~TargetFrameLowering();
|
||||
|
||||
// These methods return information that describes the abstract stack layout
|
||||
// of the target machine.
|
||||
|
||||
/// getStackGrowthDirection - Return the direction the stack grows
|
||||
///
|
||||
StackDirection getStackGrowthDirection() const { return StackDir; }
|
||||
|
||||
/// getStackAlignment - This method returns the number of bytes to which the
|
||||
/// stack pointer must be aligned on entry to a function. Typically, this
|
||||
/// is the largest alignment for any data object in the target.
|
||||
///
|
||||
unsigned getStackAlignment() const { return StackAlignment; }
|
||||
|
||||
/// alignSPAdjust - This method aligns the stack adjustment to the correct
|
||||
/// alignment.
|
||||
///
|
||||
int alignSPAdjust(int SPAdj) const {
|
||||
if (SPAdj < 0) {
|
||||
SPAdj = -alignTo(-SPAdj, StackAlignment);
|
||||
} else {
|
||||
SPAdj = alignTo(SPAdj, StackAlignment);
|
||||
}
|
||||
return SPAdj;
|
||||
}
|
||||
|
||||
/// getTransientStackAlignment - This method returns the number of bytes to
|
||||
/// which the stack pointer must be aligned at all times, even between
|
||||
/// calls.
|
||||
///
|
||||
unsigned getTransientStackAlignment() const {
|
||||
return TransientStackAlignment;
|
||||
}
|
||||
|
||||
/// isStackRealignable - This method returns whether the stack can be
|
||||
/// realigned.
|
||||
bool isStackRealignable() const {
|
||||
return StackRealignable;
|
||||
}
|
||||
|
||||
/// Return the skew that has to be applied to stack alignment under
|
||||
/// certain conditions (e.g. stack was adjusted before function \p MF
|
||||
/// was called).
|
||||
virtual unsigned getStackAlignmentSkew(const MachineFunction &MF) const;
|
||||
|
||||
/// getOffsetOfLocalArea - This method returns the offset of the local area
|
||||
/// from the stack pointer on entrance to a function.
|
||||
///
|
||||
int getOffsetOfLocalArea() const { return LocalAreaOffset; }
|
||||
|
||||
/// isFPCloseToIncomingSP - Return true if the frame pointer is close to
|
||||
/// the incoming stack pointer, false if it is close to the post-prologue
|
||||
/// stack pointer.
|
||||
virtual bool isFPCloseToIncomingSP() const { return true; }
|
||||
|
||||
/// assignCalleeSavedSpillSlots - Allows target to override spill slot
|
||||
/// assignment logic. If implemented, assignCalleeSavedSpillSlots() should
|
||||
/// assign frame slots to all CSI entries and return true. If this method
|
||||
/// returns false, spill slots will be assigned using generic implementation.
|
||||
/// assignCalleeSavedSpillSlots() may add, delete or rearrange elements of
|
||||
/// CSI.
|
||||
virtual bool
|
||||
assignCalleeSavedSpillSlots(MachineFunction &MF,
|
||||
const TargetRegisterInfo *TRI,
|
||||
std::vector<CalleeSavedInfo> &CSI) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// getCalleeSavedSpillSlots - This method returns a pointer to an array of
|
||||
/// pairs, that contains an entry for each callee saved register that must be
|
||||
/// spilled to a particular stack location if it is spilled.
|
||||
///
|
||||
/// Each entry in this array contains a <register,offset> pair, indicating the
|
||||
/// fixed offset from the incoming stack pointer that each register should be
|
||||
/// spilled at. If a register is not listed here, the code generator is
|
||||
/// allowed to spill it anywhere it chooses.
|
||||
///
|
||||
virtual const SpillSlot *
|
||||
getCalleeSavedSpillSlots(unsigned &NumEntries) const {
|
||||
NumEntries = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// targetHandlesStackFrameRounding - Returns true if the target is
|
||||
/// responsible for rounding up the stack frame (probably at emitPrologue
|
||||
/// time).
|
||||
virtual bool targetHandlesStackFrameRounding() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Returns true if the target will correctly handle shrink wrapping.
|
||||
virtual bool enableShrinkWrapping(const MachineFunction &MF) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Returns true if the stack slot holes in the fixed and callee-save stack
|
||||
/// area should be used when allocating other stack locations to reduce stack
|
||||
/// size.
|
||||
virtual bool enableStackSlotScavenging(const MachineFunction &MF) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// emitProlog/emitEpilog - These methods insert prolog and epilog code into
|
||||
/// the function.
|
||||
virtual void emitPrologue(MachineFunction &MF,
|
||||
MachineBasicBlock &MBB) const = 0;
|
||||
virtual void emitEpilogue(MachineFunction &MF,
|
||||
MachineBasicBlock &MBB) const = 0;
|
||||
|
||||
/// Replace a StackProbe stub (if any) with the actual probe code inline
|
||||
virtual void inlineStackProbe(MachineFunction &MF,
|
||||
MachineBasicBlock &PrologueMBB) const {}
|
||||
|
||||
/// Adjust the prologue to have the function use segmented stacks. This works
|
||||
/// by adding a check even before the "normal" function prologue.
|
||||
virtual void adjustForSegmentedStacks(MachineFunction &MF,
|
||||
MachineBasicBlock &PrologueMBB) const {}
|
||||
|
||||
/// Adjust the prologue to add Erlang Run-Time System (ERTS) specific code in
|
||||
/// the assembly prologue to explicitly handle the stack.
|
||||
virtual void adjustForHiPEPrologue(MachineFunction &MF,
|
||||
MachineBasicBlock &PrologueMBB) const {}
|
||||
|
||||
/// spillCalleeSavedRegisters - Issues instruction(s) to spill all callee
|
||||
/// saved registers and returns true if it isn't possible / profitable to do
|
||||
/// so by issuing a series of store instructions via
|
||||
/// storeRegToStackSlot(). Returns false otherwise.
|
||||
virtual bool spillCalleeSavedRegisters(MachineBasicBlock &MBB,
|
||||
MachineBasicBlock::iterator MI,
|
||||
const std::vector<CalleeSavedInfo> &CSI,
|
||||
const TargetRegisterInfo *TRI) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// restoreCalleeSavedRegisters - Issues instruction(s) to restore all callee
|
||||
/// saved registers and returns true if it isn't possible / profitable to do
|
||||
/// so by issuing a series of load instructions via loadRegToStackSlot().
|
||||
/// Returns false otherwise.
|
||||
virtual bool restoreCalleeSavedRegisters(MachineBasicBlock &MBB,
|
||||
MachineBasicBlock::iterator MI,
|
||||
const std::vector<CalleeSavedInfo> &CSI,
|
||||
const TargetRegisterInfo *TRI) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Return true if the target needs to disable frame pointer elimination.
|
||||
virtual bool noFramePointerElim(const MachineFunction &MF) const;
|
||||
|
||||
/// hasFP - Return true if the specified function should have a dedicated
|
||||
/// frame pointer register. For most targets this is true only if the function
|
||||
/// has variable sized allocas or if frame pointer elimination is disabled.
|
||||
virtual bool hasFP(const MachineFunction &MF) const = 0;
|
||||
|
||||
/// hasReservedCallFrame - Under normal circumstances, when a frame pointer is
|
||||
/// not required, we reserve argument space for call sites in the function
|
||||
/// immediately on entry to the current function. This eliminates the need for
|
||||
/// add/sub sp brackets around call sites. Returns true if the call frame is
|
||||
/// included as part of the stack frame.
|
||||
virtual bool hasReservedCallFrame(const MachineFunction &MF) const {
|
||||
return !hasFP(MF);
|
||||
}
|
||||
|
||||
/// canSimplifyCallFramePseudos - When possible, it's best to simplify the
|
||||
/// call frame pseudo ops before doing frame index elimination. This is
|
||||
/// possible only when frame index references between the pseudos won't
|
||||
/// need adjusting for the call frame adjustments. Normally, that's true
|
||||
/// if the function has a reserved call frame or a frame pointer. Some
|
||||
/// targets (Thumb2, for example) may have more complicated criteria,
|
||||
/// however, and can override this behavior.
|
||||
virtual bool canSimplifyCallFramePseudos(const MachineFunction &MF) const {
|
||||
return hasReservedCallFrame(MF) || hasFP(MF);
|
||||
}
|
||||
|
||||
// needsFrameIndexResolution - Do we need to perform FI resolution for
|
||||
// this function. Normally, this is required only when the function
|
||||
// has any stack objects. However, targets may want to override this.
|
||||
virtual bool needsFrameIndexResolution(const MachineFunction &MF) const;
|
||||
|
||||
/// getFrameIndexReference - This method should return the base register
|
||||
/// and offset used to reference a frame index location. The offset is
|
||||
/// returned directly, and the base register is returned via FrameReg.
|
||||
virtual int getFrameIndexReference(const MachineFunction &MF, int FI,
|
||||
unsigned &FrameReg) const;
|
||||
|
||||
/// Same as \c getFrameIndexReference, except that the stack pointer (as
|
||||
/// opposed to the frame pointer) will be the preferred value for \p
|
||||
/// FrameReg. This is generally used for emitting statepoint or EH tables that
|
||||
/// use offsets from RSP. If \p IgnoreSPUpdates is true, the returned
|
||||
/// offset is only guaranteed to be valid with respect to the value of SP at
|
||||
/// the end of the prologue.
|
||||
virtual int getFrameIndexReferencePreferSP(const MachineFunction &MF, int FI,
|
||||
unsigned &FrameReg,
|
||||
bool IgnoreSPUpdates) const {
|
||||
// Always safe to dispatch to getFrameIndexReference.
|
||||
return getFrameIndexReference(MF, FI, FrameReg);
|
||||
}
|
||||
|
||||
/// This method determines which of the registers reported by
|
||||
/// TargetRegisterInfo::getCalleeSavedRegs() should actually get saved.
|
||||
/// The default implementation checks populates the \p SavedRegs bitset with
|
||||
/// all registers which are modified in the function, targets may override
|
||||
/// this function to save additional registers.
|
||||
/// This method also sets up the register scavenger ensuring there is a free
|
||||
/// register or a frameindex available.
|
||||
virtual void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs,
|
||||
RegScavenger *RS = nullptr) const;
|
||||
|
||||
/// processFunctionBeforeFrameFinalized - This method is called immediately
|
||||
/// before the specified function's frame layout (MF.getFrameInfo()) is
|
||||
/// finalized. Once the frame is finalized, MO_FrameIndex operands are
|
||||
/// replaced with direct constants. This method is optional.
|
||||
///
|
||||
virtual void processFunctionBeforeFrameFinalized(MachineFunction &MF,
|
||||
RegScavenger *RS = nullptr) const {
|
||||
}
|
||||
|
||||
virtual unsigned getWinEHParentFrameOffset(const MachineFunction &MF) const {
|
||||
report_fatal_error("WinEH not implemented for this target");
|
||||
}
|
||||
|
||||
/// This method is called during prolog/epilog code insertion to eliminate
|
||||
/// call frame setup and destroy pseudo instructions (but only if the Target
|
||||
/// is using them). It is responsible for eliminating these instructions,
|
||||
/// replacing them with concrete instructions. This method need only be
|
||||
/// implemented if using call frame setup/destroy pseudo instructions.
|
||||
/// Returns an iterator pointing to the instruction after the replaced one.
|
||||
virtual MachineBasicBlock::iterator
|
||||
eliminateCallFramePseudoInstr(MachineFunction &MF,
|
||||
MachineBasicBlock &MBB,
|
||||
MachineBasicBlock::iterator MI) const {
|
||||
llvm_unreachable("Call Frame Pseudo Instructions do not exist on this "
|
||||
"target!");
|
||||
}
|
||||
|
||||
|
||||
/// Order the symbols in the local stack frame.
|
||||
/// The list of objects that we want to order is in \p objectsToAllocate as
|
||||
/// indices into the MachineFrameInfo. The array can be reordered in any way
|
||||
/// upon return. The contents of the array, however, may not be modified (i.e.
|
||||
/// only their order may be changed).
|
||||
/// By default, just maintain the original order.
|
||||
virtual void
|
||||
orderFrameObjects(const MachineFunction &MF,
|
||||
SmallVectorImpl<int> &objectsToAllocate) const {
|
||||
}
|
||||
|
||||
/// Check whether or not the given \p MBB can be used as a prologue
|
||||
/// for the target.
|
||||
/// The prologue will be inserted first in this basic block.
|
||||
/// This method is used by the shrink-wrapping pass to decide if
|
||||
/// \p MBB will be correctly handled by the target.
|
||||
/// As soon as the target enable shrink-wrapping without overriding
|
||||
/// this method, we assume that each basic block is a valid
|
||||
/// prologue.
|
||||
virtual bool canUseAsPrologue(const MachineBasicBlock &MBB) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Check whether or not the given \p MBB can be used as a epilogue
|
||||
/// for the target.
|
||||
/// The epilogue will be inserted before the first terminator of that block.
|
||||
/// This method is used by the shrink-wrapping pass to decide if
|
||||
/// \p MBB will be correctly handled by the target.
|
||||
/// As soon as the target enable shrink-wrapping without overriding
|
||||
/// this method, we assume that each basic block is a valid
|
||||
/// epilogue.
|
||||
virtual bool canUseAsEpilogue(const MachineBasicBlock &MBB) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Check if given function is safe for not having callee saved registers.
|
||||
/// This is used when interprocedural register allocation is enabled.
|
||||
static bool isSafeForNoCSROpt(const Function *F) {
|
||||
if (!F->hasLocalLinkage() || F->hasAddressTaken() ||
|
||||
!F->hasFnAttribute(Attribute::NoRecurse))
|
||||
return false;
|
||||
// Function should not be optimized as tail call.
|
||||
for (const User *U : F->users())
|
||||
if (auto CS = ImmutableCallSite(U))
|
||||
if (CS.isTailCall())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // End llvm namespace
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,435 +0,0 @@
|
||||
//===-- llvm/Target/TargetOpcodes.def - Target Indep Opcodes ------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the target independent instruction opcodes.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// NOTE: NO INCLUDE GUARD DESIRED!
|
||||
|
||||
/// HANDLE_TARGET_OPCODE defines an opcode and its associated enum value.
|
||||
///
|
||||
#ifndef HANDLE_TARGET_OPCODE
|
||||
#define HANDLE_TARGET_OPCODE(OPC, NUM)
|
||||
#endif
|
||||
|
||||
/// HANDLE_TARGET_OPCODE_MARKER defines an alternative identifier for an opcode.
|
||||
///
|
||||
#ifndef HANDLE_TARGET_OPCODE_MARKER
|
||||
#define HANDLE_TARGET_OPCODE_MARKER(IDENT, OPC)
|
||||
#endif
|
||||
|
||||
/// Every instruction defined here must also appear in Target.td.
|
||||
///
|
||||
HANDLE_TARGET_OPCODE(PHI)
|
||||
HANDLE_TARGET_OPCODE(INLINEASM)
|
||||
HANDLE_TARGET_OPCODE(CFI_INSTRUCTION)
|
||||
HANDLE_TARGET_OPCODE(EH_LABEL)
|
||||
HANDLE_TARGET_OPCODE(GC_LABEL)
|
||||
|
||||
/// KILL - This instruction is a noop that is used only to adjust the
|
||||
/// liveness of registers. This can be useful when dealing with
|
||||
/// sub-registers.
|
||||
HANDLE_TARGET_OPCODE(KILL)
|
||||
|
||||
/// EXTRACT_SUBREG - This instruction takes two operands: a register
|
||||
/// that has subregisters, and a subregister index. It returns the
|
||||
/// extracted subregister value. This is commonly used to implement
|
||||
/// truncation operations on target architectures which support it.
|
||||
HANDLE_TARGET_OPCODE(EXTRACT_SUBREG)
|
||||
|
||||
/// INSERT_SUBREG - This instruction takes three operands: a register that
|
||||
/// has subregisters, a register providing an insert value, and a
|
||||
/// subregister index. It returns the value of the first register with the
|
||||
/// value of the second register inserted. The first register is often
|
||||
/// defined by an IMPLICIT_DEF, because it is commonly used to implement
|
||||
/// anyext operations on target architectures which support it.
|
||||
HANDLE_TARGET_OPCODE(INSERT_SUBREG)
|
||||
|
||||
/// IMPLICIT_DEF - This is the MachineInstr-level equivalent of undef.
|
||||
HANDLE_TARGET_OPCODE(IMPLICIT_DEF)
|
||||
|
||||
/// SUBREG_TO_REG - Assert the value of bits in a super register.
|
||||
/// The result of this instruction is the value of the second operand inserted
|
||||
/// into the subregister specified by the third operand. All other bits are
|
||||
/// assumed to be equal to the bits in the immediate integer constant in the
|
||||
/// first operand. This instruction just communicates information; No code
|
||||
/// should be generated.
|
||||
/// This is typically used after an instruction where the write to a subregister
|
||||
/// implicitly cleared the bits in the super registers.
|
||||
HANDLE_TARGET_OPCODE(SUBREG_TO_REG)
|
||||
|
||||
/// COPY_TO_REGCLASS - This instruction is a placeholder for a plain
|
||||
/// register-to-register copy into a specific register class. This is only
|
||||
/// used between instruction selection and MachineInstr creation, before
|
||||
/// virtual registers have been created for all the instructions, and it's
|
||||
/// only needed in cases where the register classes implied by the
|
||||
/// instructions are insufficient. It is emitted as a COPY MachineInstr.
|
||||
HANDLE_TARGET_OPCODE(COPY_TO_REGCLASS)
|
||||
|
||||
/// DBG_VALUE - a mapping of the llvm.dbg.value intrinsic
|
||||
HANDLE_TARGET_OPCODE(DBG_VALUE)
|
||||
|
||||
/// REG_SEQUENCE - This variadic instruction is used to form a register that
|
||||
/// represents a consecutive sequence of sub-registers. It's used as a
|
||||
/// register coalescing / allocation aid and must be eliminated before code
|
||||
/// emission.
|
||||
// In SDNode form, the first operand encodes the register class created by
|
||||
// the REG_SEQUENCE, while each subsequent pair names a vreg + subreg index
|
||||
// pair. Once it has been lowered to a MachineInstr, the regclass operand
|
||||
// is no longer present.
|
||||
/// e.g. v1027 = REG_SEQUENCE v1024, 3, v1025, 4, v1026, 5
|
||||
/// After register coalescing references of v1024 should be replace with
|
||||
/// v1027:3, v1025 with v1027:4, etc.
|
||||
HANDLE_TARGET_OPCODE(REG_SEQUENCE)
|
||||
|
||||
/// COPY - Target-independent register copy. This instruction can also be
|
||||
/// used to copy between subregisters of virtual registers.
|
||||
HANDLE_TARGET_OPCODE(COPY)
|
||||
|
||||
/// BUNDLE - This instruction represents an instruction bundle. Instructions
|
||||
/// which immediately follow a BUNDLE instruction which are marked with
|
||||
/// 'InsideBundle' flag are inside the bundle.
|
||||
HANDLE_TARGET_OPCODE(BUNDLE)
|
||||
|
||||
/// Lifetime markers.
|
||||
HANDLE_TARGET_OPCODE(LIFETIME_START)
|
||||
HANDLE_TARGET_OPCODE(LIFETIME_END)
|
||||
|
||||
/// A Stackmap instruction captures the location of live variables at its
|
||||
/// position in the instruction stream. It is followed by a shadow of bytes
|
||||
/// that must lie within the function and not contain another stackmap.
|
||||
HANDLE_TARGET_OPCODE(STACKMAP)
|
||||
|
||||
/// FEntry all - This is a marker instruction which gets translated into a raw fentry call.
|
||||
HANDLE_TARGET_OPCODE(FENTRY_CALL)
|
||||
|
||||
/// Patchable call instruction - this instruction represents a call to a
|
||||
/// constant address, followed by a series of NOPs. It is intended to
|
||||
/// support optimizations for dynamic languages (such as javascript) that
|
||||
/// rewrite calls to runtimes with more efficient code sequences.
|
||||
/// This also implies a stack map.
|
||||
HANDLE_TARGET_OPCODE(PATCHPOINT)
|
||||
|
||||
/// This pseudo-instruction loads the stack guard value. Targets which need
|
||||
/// to prevent the stack guard value or address from being spilled to the
|
||||
/// stack should override TargetLowering::emitLoadStackGuardNode and
|
||||
/// additionally expand this pseudo after register allocation.
|
||||
HANDLE_TARGET_OPCODE(LOAD_STACK_GUARD)
|
||||
|
||||
/// Call instruction with associated vm state for deoptimization and list
|
||||
/// of live pointers for relocation by the garbage collector. It is
|
||||
/// intended to support garbage collection with fully precise relocating
|
||||
/// collectors and deoptimizations in either the callee or caller.
|
||||
HANDLE_TARGET_OPCODE(STATEPOINT)
|
||||
|
||||
/// Instruction that records the offset of a local stack allocation passed to
|
||||
/// llvm.localescape. It has two arguments: the symbol for the label and the
|
||||
/// frame index of the local stack allocation.
|
||||
HANDLE_TARGET_OPCODE(LOCAL_ESCAPE)
|
||||
|
||||
/// Wraps a machine instruction which can fault, bundled with associated
|
||||
/// information on how to handle such a fault.
|
||||
/// For example loading instruction that may page fault, bundled with associated
|
||||
/// information on how to handle such a page fault. It is intended to support
|
||||
/// "zero cost" null checks in managed languages by allowing LLVM to fold
|
||||
/// comparisons into existing memory operations.
|
||||
HANDLE_TARGET_OPCODE(FAULTING_OP)
|
||||
|
||||
/// Wraps a machine instruction to add patchability constraints. An
|
||||
/// instruction wrapped in PATCHABLE_OP has to either have a minimum
|
||||
/// size or be preceded with a nop of that size. The first operand is
|
||||
/// an immediate denoting the minimum size of the instruction, the
|
||||
/// second operand is an immediate denoting the opcode of the original
|
||||
/// instruction. The rest of the operands are the operands of the
|
||||
/// original instruction.
|
||||
HANDLE_TARGET_OPCODE(PATCHABLE_OP)
|
||||
|
||||
/// This is a marker instruction which gets translated into a nop sled, useful
|
||||
/// for inserting instrumentation instructions at runtime.
|
||||
HANDLE_TARGET_OPCODE(PATCHABLE_FUNCTION_ENTER)
|
||||
|
||||
/// Wraps a return instruction and its operands to enable adding nop sleds
|
||||
/// either before or after the return. The nop sleds are useful for inserting
|
||||
/// instrumentation instructions at runtime.
|
||||
/// The patch here replaces the return instruction.
|
||||
HANDLE_TARGET_OPCODE(PATCHABLE_RET)
|
||||
|
||||
/// This is a marker instruction which gets translated into a nop sled, useful
|
||||
/// for inserting instrumentation instructions at runtime.
|
||||
/// The patch here prepends the return instruction.
|
||||
/// The same thing as in x86_64 is not possible for ARM because it has multiple
|
||||
/// return instructions. Furthermore, CPU allows parametrized and even
|
||||
/// conditional return instructions. In the current ARM implementation we are
|
||||
/// making use of the fact that currently LLVM doesn't seem to generate
|
||||
/// conditional return instructions.
|
||||
/// On ARM, the same instruction can be used for popping multiple registers
|
||||
/// from the stack and returning (it just pops pc register too), and LLVM
|
||||
/// generates it sometimes. So we can't insert the sled between this stack
|
||||
/// adjustment and the return without splitting the original instruction into 2
|
||||
/// instructions. So on ARM, rather than jumping into the exit trampoline, we
|
||||
/// call it, it does the tracing, preserves the stack and returns.
|
||||
HANDLE_TARGET_OPCODE(PATCHABLE_FUNCTION_EXIT)
|
||||
|
||||
/// Wraps a tail call instruction and its operands to enable adding nop sleds
|
||||
/// either before or after the tail exit. We use this as a disambiguation from
|
||||
/// PATCHABLE_RET which specifically only works for return instructions.
|
||||
HANDLE_TARGET_OPCODE(PATCHABLE_TAIL_CALL)
|
||||
|
||||
/// Wraps a logging call and its arguments with nop sleds. At runtime, this can be
|
||||
/// patched to insert instrumentation instructions.
|
||||
HANDLE_TARGET_OPCODE(PATCHABLE_EVENT_CALL)
|
||||
|
||||
/// The following generic opcodes are not supposed to appear after ISel.
|
||||
/// This is something we might want to relax, but for now, this is convenient
|
||||
/// to produce diagnostics.
|
||||
|
||||
/// Generic ADD instruction. This is an integer add.
|
||||
HANDLE_TARGET_OPCODE(G_ADD)
|
||||
HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPCODE_START, G_ADD)
|
||||
|
||||
/// Generic SUB instruction. This is an integer sub.
|
||||
HANDLE_TARGET_OPCODE(G_SUB)
|
||||
|
||||
// Generic multiply instruction.
|
||||
HANDLE_TARGET_OPCODE(G_MUL)
|
||||
|
||||
// Generic signed division instruction.
|
||||
HANDLE_TARGET_OPCODE(G_SDIV)
|
||||
|
||||
// Generic unsigned division instruction.
|
||||
HANDLE_TARGET_OPCODE(G_UDIV)
|
||||
|
||||
// Generic signed remainder instruction.
|
||||
HANDLE_TARGET_OPCODE(G_SREM)
|
||||
|
||||
// Generic unsigned remainder instruction.
|
||||
HANDLE_TARGET_OPCODE(G_UREM)
|
||||
|
||||
/// Generic bitwise and instruction.
|
||||
HANDLE_TARGET_OPCODE(G_AND)
|
||||
|
||||
/// Generic bitwise or instruction.
|
||||
HANDLE_TARGET_OPCODE(G_OR)
|
||||
|
||||
/// Generic bitwise exclusive-or instruction.
|
||||
HANDLE_TARGET_OPCODE(G_XOR)
|
||||
|
||||
|
||||
HANDLE_TARGET_OPCODE(G_IMPLICIT_DEF)
|
||||
|
||||
/// Generic instruction to materialize the address of an alloca or other
|
||||
/// stack-based object.
|
||||
HANDLE_TARGET_OPCODE(G_FRAME_INDEX)
|
||||
|
||||
/// Generic reference to global value.
|
||||
HANDLE_TARGET_OPCODE(G_GLOBAL_VALUE)
|
||||
|
||||
/// Generic instruction to extract blocks of bits from the register given
|
||||
/// (typically a sub-register COPY after instruction selection).
|
||||
HANDLE_TARGET_OPCODE(G_EXTRACT)
|
||||
|
||||
HANDLE_TARGET_OPCODE(G_UNMERGE_VALUES)
|
||||
|
||||
/// Generic instruction to insert blocks of bits from the registers given into
|
||||
/// the source.
|
||||
HANDLE_TARGET_OPCODE(G_INSERT)
|
||||
|
||||
/// Generic instruction to paste a variable number of components together into a
|
||||
/// larger register.
|
||||
HANDLE_TARGET_OPCODE(G_MERGE_VALUES)
|
||||
|
||||
/// Generic pointer to int conversion.
|
||||
HANDLE_TARGET_OPCODE(G_PTRTOINT)
|
||||
|
||||
/// Generic int to pointer conversion.
|
||||
HANDLE_TARGET_OPCODE(G_INTTOPTR)
|
||||
|
||||
/// Generic bitcast. The source and destination types must be different, or a
|
||||
/// COPY is the relevant instruction.
|
||||
HANDLE_TARGET_OPCODE(G_BITCAST)
|
||||
|
||||
/// Generic load.
|
||||
HANDLE_TARGET_OPCODE(G_LOAD)
|
||||
|
||||
/// Generic store.
|
||||
HANDLE_TARGET_OPCODE(G_STORE)
|
||||
|
||||
/// Generic conditional branch instruction.
|
||||
HANDLE_TARGET_OPCODE(G_BRCOND)
|
||||
|
||||
/// Generic indirect branch instruction.
|
||||
HANDLE_TARGET_OPCODE(G_BRINDIRECT)
|
||||
|
||||
/// Generic intrinsic use (without side effects).
|
||||
HANDLE_TARGET_OPCODE(G_INTRINSIC)
|
||||
|
||||
/// Generic intrinsic use (with side effects).
|
||||
HANDLE_TARGET_OPCODE(G_INTRINSIC_W_SIDE_EFFECTS)
|
||||
|
||||
/// Generic extension allowing rubbish in high bits.
|
||||
HANDLE_TARGET_OPCODE(G_ANYEXT)
|
||||
|
||||
/// Generic instruction to discard the high bits of a register. This differs
|
||||
/// from (G_EXTRACT val, 0) on its action on vectors: G_TRUNC will truncate
|
||||
/// each element individually, G_EXTRACT will typically discard the high
|
||||
/// elements of the vector.
|
||||
HANDLE_TARGET_OPCODE(G_TRUNC)
|
||||
|
||||
/// Generic integer constant.
|
||||
HANDLE_TARGET_OPCODE(G_CONSTANT)
|
||||
|
||||
/// Generic floating constant.
|
||||
HANDLE_TARGET_OPCODE(G_FCONSTANT)
|
||||
|
||||
/// Generic va_start instruction. Stores to its one pointer operand.
|
||||
HANDLE_TARGET_OPCODE(G_VASTART)
|
||||
|
||||
/// Generic va_start instruction. Stores to its one pointer operand.
|
||||
HANDLE_TARGET_OPCODE(G_VAARG)
|
||||
|
||||
// Generic sign extend
|
||||
HANDLE_TARGET_OPCODE(G_SEXT)
|
||||
|
||||
// Generic zero extend
|
||||
HANDLE_TARGET_OPCODE(G_ZEXT)
|
||||
|
||||
// Generic left-shift
|
||||
HANDLE_TARGET_OPCODE(G_SHL)
|
||||
|
||||
// Generic logical right-shift
|
||||
HANDLE_TARGET_OPCODE(G_LSHR)
|
||||
|
||||
// Generic arithmetic right-shift
|
||||
HANDLE_TARGET_OPCODE(G_ASHR)
|
||||
|
||||
/// Generic integer-base comparison, also applicable to vectors of integers.
|
||||
HANDLE_TARGET_OPCODE(G_ICMP)
|
||||
|
||||
/// Generic floating-point comparison, also applicable to vectors.
|
||||
HANDLE_TARGET_OPCODE(G_FCMP)
|
||||
|
||||
/// Generic select.
|
||||
HANDLE_TARGET_OPCODE(G_SELECT)
|
||||
|
||||
/// Generic unsigned add instruction, consuming the normal operands plus a carry
|
||||
/// flag, and similarly producing the result and a carry flag.
|
||||
HANDLE_TARGET_OPCODE(G_UADDE)
|
||||
|
||||
/// Generic unsigned subtract instruction, consuming the normal operands plus a
|
||||
/// carry flag, and similarly producing the result and a carry flag.
|
||||
HANDLE_TARGET_OPCODE(G_USUBE)
|
||||
|
||||
/// Generic signed add instruction, producing the result and a signed overflow
|
||||
/// flag.
|
||||
HANDLE_TARGET_OPCODE(G_SADDO)
|
||||
|
||||
/// Generic signed subtract instruction, producing the result and a signed
|
||||
/// overflow flag.
|
||||
HANDLE_TARGET_OPCODE(G_SSUBO)
|
||||
|
||||
/// Generic unsigned multiply instruction, producing the result and a signed
|
||||
/// overflow flag.
|
||||
HANDLE_TARGET_OPCODE(G_UMULO)
|
||||
|
||||
/// Generic signed multiply instruction, producing the result and a signed
|
||||
/// overflow flag.
|
||||
HANDLE_TARGET_OPCODE(G_SMULO)
|
||||
|
||||
// Multiply two numbers at twice the incoming bit width (unsigned) and return
|
||||
// the high half of the result.
|
||||
HANDLE_TARGET_OPCODE(G_UMULH)
|
||||
|
||||
// Multiply two numbers at twice the incoming bit width (signed) and return
|
||||
// the high half of the result.
|
||||
HANDLE_TARGET_OPCODE(G_SMULH)
|
||||
|
||||
/// Generic FP addition.
|
||||
HANDLE_TARGET_OPCODE(G_FADD)
|
||||
|
||||
/// Generic FP subtraction.
|
||||
HANDLE_TARGET_OPCODE(G_FSUB)
|
||||
|
||||
/// Generic FP multiplication.
|
||||
HANDLE_TARGET_OPCODE(G_FMUL)
|
||||
|
||||
/// Generic FMA multiplication. Behaves like llvm fma intrinsic
|
||||
HANDLE_TARGET_OPCODE(G_FMA)
|
||||
|
||||
/// Generic FP division.
|
||||
HANDLE_TARGET_OPCODE(G_FDIV)
|
||||
|
||||
/// Generic FP remainder.
|
||||
HANDLE_TARGET_OPCODE(G_FREM)
|
||||
|
||||
/// Generic FP exponentiation.
|
||||
HANDLE_TARGET_OPCODE(G_FPOW)
|
||||
|
||||
/// Generic base-e exponential of a value.
|
||||
HANDLE_TARGET_OPCODE(G_FEXP)
|
||||
|
||||
/// Generic base-2 exponential of a value.
|
||||
HANDLE_TARGET_OPCODE(G_FEXP2)
|
||||
|
||||
/// Floating point base-e logarithm of a value.
|
||||
HANDLE_TARGET_OPCODE(G_FLOG)
|
||||
|
||||
/// Floating point base-2 logarithm of a value.
|
||||
HANDLE_TARGET_OPCODE(G_FLOG2)
|
||||
|
||||
/// Generic FP negation.
|
||||
HANDLE_TARGET_OPCODE(G_FNEG)
|
||||
|
||||
/// Generic FP extension.
|
||||
HANDLE_TARGET_OPCODE(G_FPEXT)
|
||||
|
||||
/// Generic float to signed-int conversion
|
||||
HANDLE_TARGET_OPCODE(G_FPTRUNC)
|
||||
|
||||
/// Generic float to signed-int conversion
|
||||
HANDLE_TARGET_OPCODE(G_FPTOSI)
|
||||
|
||||
/// Generic float to unsigned-int conversion
|
||||
HANDLE_TARGET_OPCODE(G_FPTOUI)
|
||||
|
||||
/// Generic signed-int to float conversion
|
||||
HANDLE_TARGET_OPCODE(G_SITOFP)
|
||||
|
||||
/// Generic unsigned-int to float conversion
|
||||
HANDLE_TARGET_OPCODE(G_UITOFP)
|
||||
|
||||
/// Generic pointer offset
|
||||
HANDLE_TARGET_OPCODE(G_GEP)
|
||||
|
||||
/// Clear the specified number of low bits in a pointer. This rounds the value
|
||||
/// *down* to the given alignment.
|
||||
HANDLE_TARGET_OPCODE(G_PTR_MASK)
|
||||
|
||||
/// Generic BRANCH instruction. This is an unconditional branch.
|
||||
HANDLE_TARGET_OPCODE(G_BR)
|
||||
|
||||
/// Generic insertelement.
|
||||
HANDLE_TARGET_OPCODE(G_INSERT_VECTOR_ELT)
|
||||
|
||||
/// Generic extractelement.
|
||||
HANDLE_TARGET_OPCODE(G_EXTRACT_VECTOR_ELT)
|
||||
|
||||
/// Generic shufflevector.
|
||||
HANDLE_TARGET_OPCODE(G_SHUFFLE_VECTOR)
|
||||
|
||||
// TODO: Add more generic opcodes as we move along.
|
||||
|
||||
/// Marker for the end of the generic opcode.
|
||||
/// This is used to check if an opcode is in the range of the
|
||||
/// generic opcodes.
|
||||
HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPCODE_END, G_SHUFFLE_VECTOR)
|
||||
|
||||
/// BUILTIN_OP_END - This must be the last enum value in this list.
|
||||
/// The target-specific post-isel opcode values start here.
|
||||
HANDLE_TARGET_OPCODE_MARKER(GENERIC_OP_END, PRE_ISEL_GENERIC_OPCODE_END)
|
@ -1,42 +0,0 @@
|
||||
//===-- llvm/Target/TargetOpcodes.h - Target Indep Opcodes ------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the target independent instruction opcodes.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_TARGET_TARGETOPCODES_H
|
||||
#define LLVM_TARGET_TARGETOPCODES_H
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// Invariant opcodes: All instruction sets have these as their low opcodes.
|
||||
///
|
||||
namespace TargetOpcode {
|
||||
enum {
|
||||
#define HANDLE_TARGET_OPCODE(OPC) OPC,
|
||||
#define HANDLE_TARGET_OPCODE_MARKER(IDENT, OPC) IDENT = OPC,
|
||||
#include "llvm/Target/TargetOpcodes.def"
|
||||
};
|
||||
} // end namespace TargetOpcode
|
||||
|
||||
/// Check whether the given Opcode is a generic opcode that is not supposed
|
||||
/// to appear after ISel.
|
||||
static inline bool isPreISelGenericOpcode(unsigned Opcode) {
|
||||
return Opcode >= TargetOpcode::PRE_ISEL_GENERIC_OPCODE_START &&
|
||||
Opcode <= TargetOpcode::PRE_ISEL_GENERIC_OPCODE_END;
|
||||
}
|
||||
|
||||
/// Check whether the given Opcode is a target-specific opcode.
|
||||
static inline bool isTargetSpecificOpcode(unsigned Opcode) {
|
||||
return Opcode > TargetOpcode::PRE_ISEL_GENERIC_OPCODE_END;
|
||||
}
|
||||
} // end namespace llvm
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -1,248 +0,0 @@
|
||||
//===- llvm/Target/TargetSubtargetInfo.h - Target Information ---*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file describes the subtarget options of a Target machine.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_TARGET_TARGETSUBTARGETINFO_H
|
||||
#define LLVM_TARGET_TARGETSUBTARGETINFO_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/CodeGen/PBQPRAConstraint.h"
|
||||
#include "llvm/CodeGen/ScheduleDAGMutation.h"
|
||||
#include "llvm/CodeGen/SchedulerRegistry.h"
|
||||
#include "llvm/MC/MCSubtargetInfo.h"
|
||||
#include "llvm/Support/CodeGen.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class CallLowering;
|
||||
class InstrItineraryData;
|
||||
struct InstrStage;
|
||||
class InstructionSelector;
|
||||
class LegalizerInfo;
|
||||
class MachineInstr;
|
||||
struct MachineSchedPolicy;
|
||||
struct MCReadAdvanceEntry;
|
||||
struct MCWriteLatencyEntry;
|
||||
struct MCWriteProcResEntry;
|
||||
class RegisterBankInfo;
|
||||
class SDep;
|
||||
class SelectionDAGTargetInfo;
|
||||
struct SubtargetFeatureKV;
|
||||
struct SubtargetInfoKV;
|
||||
class SUnit;
|
||||
class TargetFrameLowering;
|
||||
class TargetInstrInfo;
|
||||
class TargetLowering;
|
||||
class TargetRegisterClass;
|
||||
class TargetRegisterInfo;
|
||||
class TargetSchedModel;
|
||||
class Triple;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// TargetSubtargetInfo - Generic base class for all target subtargets. All
|
||||
/// Target-specific options that control code generation and printing should
|
||||
/// be exposed through a TargetSubtargetInfo-derived class.
|
||||
///
|
||||
class TargetSubtargetInfo : public MCSubtargetInfo {
|
||||
protected: // Can only create subclasses...
|
||||
TargetSubtargetInfo(const Triple &TT, StringRef CPU, StringRef FS,
|
||||
ArrayRef<SubtargetFeatureKV> PF,
|
||||
ArrayRef<SubtargetFeatureKV> PD,
|
||||
const SubtargetInfoKV *ProcSched,
|
||||
const MCWriteProcResEntry *WPR,
|
||||
const MCWriteLatencyEntry *WL,
|
||||
const MCReadAdvanceEntry *RA, const InstrStage *IS,
|
||||
const unsigned *OC, const unsigned *FP);
|
||||
|
||||
public:
|
||||
// AntiDepBreakMode - Type of anti-dependence breaking that should
|
||||
// be performed before post-RA scheduling.
|
||||
using AntiDepBreakMode = enum { ANTIDEP_NONE, ANTIDEP_CRITICAL, ANTIDEP_ALL };
|
||||
using RegClassVector = SmallVectorImpl<const TargetRegisterClass *>;
|
||||
|
||||
TargetSubtargetInfo() = delete;
|
||||
TargetSubtargetInfo(const TargetSubtargetInfo &) = delete;
|
||||
TargetSubtargetInfo &operator=(const TargetSubtargetInfo &) = delete;
|
||||
~TargetSubtargetInfo() override;
|
||||
|
||||
virtual bool isXRaySupported() const { return false; }
|
||||
|
||||
// Interfaces to the major aspects of target machine information:
|
||||
//
|
||||
// -- Instruction opcode and operand information
|
||||
// -- Pipelines and scheduling information
|
||||
// -- Stack frame information
|
||||
// -- Selection DAG lowering information
|
||||
// -- Call lowering information
|
||||
//
|
||||
// N.B. These objects may change during compilation. It's not safe to cache
|
||||
// them between functions.
|
||||
virtual const TargetInstrInfo *getInstrInfo() const { return nullptr; }
|
||||
virtual const TargetFrameLowering *getFrameLowering() const {
|
||||
return nullptr;
|
||||
}
|
||||
virtual const TargetLowering *getTargetLowering() const { return nullptr; }
|
||||
virtual const SelectionDAGTargetInfo *getSelectionDAGInfo() const {
|
||||
return nullptr;
|
||||
}
|
||||
virtual const CallLowering *getCallLowering() const { return nullptr; }
|
||||
|
||||
// FIXME: This lets targets specialize the selector by subtarget (which lets
|
||||
// us do things like a dedicated avx512 selector). However, we might want
|
||||
// to also specialize selectors by MachineFunction, which would let us be
|
||||
// aware of optsize/optnone and such.
|
||||
virtual const InstructionSelector *getInstructionSelector() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Target can subclass this hook to select a different DAG scheduler.
|
||||
virtual RegisterScheduler::FunctionPassCtor
|
||||
getDAGScheduler(CodeGenOpt::Level) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual const LegalizerInfo *getLegalizerInfo() const { return nullptr; }
|
||||
|
||||
/// getRegisterInfo - If register information is available, return it. If
|
||||
/// not, return null.
|
||||
virtual const TargetRegisterInfo *getRegisterInfo() const { return nullptr; }
|
||||
|
||||
/// If the information for the register banks is available, return it.
|
||||
/// Otherwise return nullptr.
|
||||
virtual const RegisterBankInfo *getRegBankInfo() const { return nullptr; }
|
||||
|
||||
/// getInstrItineraryData - Returns instruction itinerary data for the target
|
||||
/// or specific subtarget.
|
||||
virtual const InstrItineraryData *getInstrItineraryData() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Resolve a SchedClass at runtime, where SchedClass identifies an
|
||||
/// MCSchedClassDesc with the isVariant property. This may return the ID of
|
||||
/// another variant SchedClass, but repeated invocation must quickly terminate
|
||||
/// in a nonvariant SchedClass.
|
||||
virtual unsigned resolveSchedClass(unsigned SchedClass,
|
||||
const MachineInstr *MI,
|
||||
const TargetSchedModel *SchedModel) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \brief True if the subtarget should run MachineScheduler after aggressive
|
||||
/// coalescing.
|
||||
///
|
||||
/// This currently replaces the SelectionDAG scheduler with the "source" order
|
||||
/// scheduler (though see below for an option to turn this off and use the
|
||||
/// TargetLowering preference). It does not yet disable the postRA scheduler.
|
||||
virtual bool enableMachineScheduler() const;
|
||||
|
||||
/// \brief Support printing of [latency:throughput] comment in output .S file.
|
||||
virtual bool supportPrintSchedInfo() const { return false; }
|
||||
|
||||
/// \brief True if the machine scheduler should disable the TLI preference
|
||||
/// for preRA scheduling with the source level scheduler.
|
||||
virtual bool enableMachineSchedDefaultSched() const { return true; }
|
||||
|
||||
/// \brief True if the subtarget should enable joining global copies.
|
||||
///
|
||||
/// By default this is enabled if the machine scheduler is enabled, but
|
||||
/// can be overridden.
|
||||
virtual bool enableJoinGlobalCopies() const;
|
||||
|
||||
/// True if the subtarget should run a scheduler after register allocation.
|
||||
///
|
||||
/// By default this queries the PostRAScheduling bit in the scheduling model
|
||||
/// which is the preferred way to influence this.
|
||||
virtual bool enablePostRAScheduler() const;
|
||||
|
||||
/// \brief True if the subtarget should run the atomic expansion pass.
|
||||
virtual bool enableAtomicExpand() const;
|
||||
|
||||
/// \brief Override generic scheduling policy within a region.
|
||||
///
|
||||
/// This is a convenient way for targets that don't provide any custom
|
||||
/// scheduling heuristics (no custom MachineSchedStrategy) to make
|
||||
/// changes to the generic scheduling policy.
|
||||
virtual void overrideSchedPolicy(MachineSchedPolicy &Policy,
|
||||
unsigned NumRegionInstrs) const {}
|
||||
|
||||
// \brief Perform target specific adjustments to the latency of a schedule
|
||||
// dependency.
|
||||
virtual void adjustSchedDependency(SUnit *def, SUnit *use, SDep &dep) const {}
|
||||
|
||||
// For use with PostRAScheduling: get the anti-dependence breaking that should
|
||||
// be performed before post-RA scheduling.
|
||||
virtual AntiDepBreakMode getAntiDepBreakMode() const { return ANTIDEP_NONE; }
|
||||
|
||||
// For use with PostRAScheduling: in CriticalPathRCs, return any register
|
||||
// classes that should only be considered for anti-dependence breaking if they
|
||||
// are on the critical path.
|
||||
virtual void getCriticalPathRCs(RegClassVector &CriticalPathRCs) const {
|
||||
return CriticalPathRCs.clear();
|
||||
}
|
||||
|
||||
// \brief Provide an ordered list of schedule DAG mutations for the post-RA
|
||||
// scheduler.
|
||||
virtual void getPostRAMutations(
|
||||
std::vector<std::unique_ptr<ScheduleDAGMutation>> &Mutations) const {
|
||||
}
|
||||
|
||||
// \brief Provide an ordered list of schedule DAG mutations for the machine
|
||||
// pipeliner.
|
||||
virtual void getSMSMutations(
|
||||
std::vector<std::unique_ptr<ScheduleDAGMutation>> &Mutations) const {
|
||||
}
|
||||
|
||||
// For use with PostRAScheduling: get the minimum optimization level needed
|
||||
// to enable post-RA scheduling.
|
||||
virtual CodeGenOpt::Level getOptLevelToEnablePostRAScheduler() const {
|
||||
return CodeGenOpt::Default;
|
||||
}
|
||||
|
||||
/// \brief True if the subtarget should run the local reassignment
|
||||
/// heuristic of the register allocator.
|
||||
/// This heuristic may be compile time intensive, \p OptLevel provides
|
||||
/// a finer grain to tune the register allocator.
|
||||
virtual bool enableRALocalReassignment(CodeGenOpt::Level OptLevel) const;
|
||||
|
||||
/// \brief Enable use of alias analysis during code generation (during MI
|
||||
/// scheduling, DAGCombine, etc.).
|
||||
virtual bool useAA() const;
|
||||
|
||||
/// \brief Enable the use of the early if conversion pass.
|
||||
virtual bool enableEarlyIfConversion() const { return false; }
|
||||
|
||||
/// \brief Return PBQPConstraint(s) for the target.
|
||||
///
|
||||
/// Override to provide custom PBQP constraints.
|
||||
virtual std::unique_ptr<PBQPRAConstraint> getCustomPBQPConstraints() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Enable tracking of subregister liveness in register allocator.
|
||||
/// Please use MachineRegisterInfo::subRegLivenessEnabled() instead where
|
||||
/// possible.
|
||||
virtual bool enableSubRegLiveness() const { return false; }
|
||||
|
||||
/// Returns string representation of scheduler comment
|
||||
std::string getSchedInfoStr(const MachineInstr &MI) const override;
|
||||
std::string getSchedInfoStr(MCInst const &MCI) const override;
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_TARGET_TARGETSUBTARGETINFO_H
|
@ -1,31 +0,0 @@
|
||||
//===- Transforms/GCOVProfiler.h - GCOVProfiler pass ----------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// \file
|
||||
/// This file provides the interface for the GCOV style profiler pass.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_TRANSFORMS_GCOVPROFILER_H
|
||||
#define LLVM_TRANSFORMS_GCOVPROFILER_H
|
||||
|
||||
#include "llvm/IR/PassManager.h"
|
||||
#include "llvm/Transforms/Instrumentation.h"
|
||||
|
||||
namespace llvm {
|
||||
/// The gcov-style instrumentation pass
|
||||
class GCOVProfilerPass : public PassInfoMixin<GCOVProfilerPass> {
|
||||
public:
|
||||
GCOVProfilerPass(const GCOVOptions &Options = GCOVOptions::getDefault()) : GCOVOpts(Options) { }
|
||||
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
|
||||
|
||||
private:
|
||||
GCOVOptions GCOVOpts;
|
||||
};
|
||||
|
||||
} // End llvm namespace
|
||||
#endif
|
@ -1,124 +0,0 @@
|
||||
//===- Transforms/InstrProfiling.h - Instrumentation passes -----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// \file
|
||||
/// This file provides the interface for LLVM's PGO Instrumentation lowering
|
||||
/// pass.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_TRANSFORMS_INSTRPROFILING_H
|
||||
#define LLVM_TRANSFORMS_INSTRPROFILING_H
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
#include "llvm/IR/PassManager.h"
|
||||
#include "llvm/ProfileData/InstrProf.h"
|
||||
#include "llvm/Transforms/Instrumentation.h"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class TargetLibraryInfo;
|
||||
using LoadStorePair = std::pair<Instruction *, Instruction *>;
|
||||
|
||||
/// Instrumentation based profiling lowering pass. This pass lowers
|
||||
/// the profile instrumented code generated by FE or the IR based
|
||||
/// instrumentation pass.
|
||||
class InstrProfiling : public PassInfoMixin<InstrProfiling> {
|
||||
public:
|
||||
InstrProfiling() = default;
|
||||
InstrProfiling(const InstrProfOptions &Options) : Options(Options) {}
|
||||
|
||||
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
|
||||
bool run(Module &M, const TargetLibraryInfo &TLI);
|
||||
|
||||
private:
|
||||
InstrProfOptions Options;
|
||||
Module *M;
|
||||
Triple TT;
|
||||
const TargetLibraryInfo *TLI;
|
||||
struct PerFunctionProfileData {
|
||||
uint32_t NumValueSites[IPVK_Last + 1];
|
||||
GlobalVariable *RegionCounters = nullptr;
|
||||
GlobalVariable *DataVar = nullptr;
|
||||
|
||||
PerFunctionProfileData() {
|
||||
memset(NumValueSites, 0, sizeof(uint32_t) * (IPVK_Last + 1));
|
||||
}
|
||||
};
|
||||
DenseMap<GlobalVariable *, PerFunctionProfileData> ProfileDataMap;
|
||||
std::vector<GlobalValue *> UsedVars;
|
||||
std::vector<GlobalVariable *> ReferencedNames;
|
||||
GlobalVariable *NamesVar;
|
||||
size_t NamesSize;
|
||||
|
||||
// vector of counter load/store pairs to be register promoted.
|
||||
std::vector<LoadStorePair> PromotionCandidates;
|
||||
|
||||
// The start value of precise value profile range for memory intrinsic sizes.
|
||||
int64_t MemOPSizeRangeStart;
|
||||
// The end value of precise value profile range for memory intrinsic sizes.
|
||||
int64_t MemOPSizeRangeLast;
|
||||
|
||||
int64_t TotalCountersPromoted = 0;
|
||||
|
||||
/// Lower instrumentation intrinsics in the function. Returns true if there
|
||||
/// any lowering.
|
||||
bool lowerIntrinsics(Function *F);
|
||||
|
||||
/// Register-promote counter loads and stores in loops.
|
||||
void promoteCounterLoadStores(Function *F);
|
||||
|
||||
/// Returns true if profile counter update register promotion is enabled.
|
||||
bool isCounterPromotionEnabled() const;
|
||||
|
||||
/// Count the number of instrumented value sites for the function.
|
||||
void computeNumValueSiteCounts(InstrProfValueProfileInst *Ins);
|
||||
|
||||
/// Replace instrprof_value_profile with a call to runtime library.
|
||||
void lowerValueProfileInst(InstrProfValueProfileInst *Ins);
|
||||
|
||||
/// Replace instrprof_increment with an increment of the appropriate value.
|
||||
void lowerIncrement(InstrProfIncrementInst *Inc);
|
||||
|
||||
/// Force emitting of name vars for unused functions.
|
||||
void lowerCoverageData(GlobalVariable *CoverageNamesVar);
|
||||
|
||||
/// Get the region counters for an increment, creating them if necessary.
|
||||
///
|
||||
/// If the counter array doesn't yet exist, the profile data variables
|
||||
/// referring to them will also be created.
|
||||
GlobalVariable *getOrCreateRegionCounters(InstrProfIncrementInst *Inc);
|
||||
|
||||
/// Emit the section with compressed function names.
|
||||
void emitNameData();
|
||||
|
||||
/// Emit value nodes section for value profiling.
|
||||
void emitVNodes();
|
||||
|
||||
/// Emit runtime registration functions for each profile data variable.
|
||||
void emitRegistration();
|
||||
|
||||
/// Emit the necessary plumbing to pull in the runtime initialization.
|
||||
void emitRuntimeHook();
|
||||
|
||||
/// Add uses of our data variables and runtime hook.
|
||||
void emitUses();
|
||||
|
||||
/// Create a static initializer for our data, on platforms that need it,
|
||||
/// and for any profile output file that was specified.
|
||||
void emitInitialization();
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_TRANSFORMS_INSTRPROFILING_H
|
@ -1,61 +0,0 @@
|
||||
//===- Transforms/PGOInstrumentation.h - PGO gen/use passes ---*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// \file
|
||||
/// This file provides the interface for IR based instrumentation passes (
|
||||
/// (profile-gen, and profile-use).
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_TRANSFORMS_PGOINSTRUMENTATION_H
|
||||
#define LLVM_TRANSFORMS_PGOINSTRUMENTATION_H
|
||||
|
||||
#include "llvm/IR/PassManager.h"
|
||||
#include "llvm/Transforms/Instrumentation.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// The instrumentation (profile-instr-gen) pass for IR based PGO.
|
||||
class PGOInstrumentationGen : public PassInfoMixin<PGOInstrumentationGen> {
|
||||
public:
|
||||
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
|
||||
};
|
||||
|
||||
/// The profile annotation (profile-instr-use) pass for IR based PGO.
|
||||
class PGOInstrumentationUse : public PassInfoMixin<PGOInstrumentationUse> {
|
||||
public:
|
||||
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
|
||||
PGOInstrumentationUse(std::string Filename = "");
|
||||
|
||||
private:
|
||||
std::string ProfileFileName;
|
||||
};
|
||||
|
||||
/// The indirect function call promotion pass.
|
||||
class PGOIndirectCallPromotion : public PassInfoMixin<PGOIndirectCallPromotion> {
|
||||
public:
|
||||
PGOIndirectCallPromotion(bool IsInLTO = false, bool SamplePGO = false)
|
||||
: InLTO(IsInLTO), SamplePGO(SamplePGO) {}
|
||||
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
|
||||
|
||||
private:
|
||||
bool InLTO;
|
||||
bool SamplePGO;
|
||||
};
|
||||
|
||||
/// The profile size based optimization pass for memory intrinsics.
|
||||
class PGOMemOPSizeOpt : public PassInfoMixin<PGOMemOPSizeOpt> {
|
||||
public:
|
||||
PGOMemOPSizeOpt() {}
|
||||
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
|
||||
};
|
||||
|
||||
void setProfMetadata(Module *M, Instruction *TI, ArrayRef<uint64_t> EdgeCounts,
|
||||
uint64_t MaxCount);
|
||||
|
||||
} // End llvm namespace
|
||||
#endif
|
@ -1,31 +0,0 @@
|
||||
//===- Transforms/SampleProfile.h - SamplePGO pass--------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// \file
|
||||
/// This file provides the interface for the sampled PGO loader pass.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_TRANSFORMS_SAMPLEPROFILE_H
|
||||
#define LLVM_TRANSFORMS_SAMPLEPROFILE_H
|
||||
|
||||
#include "llvm/IR/PassManager.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// The sample profiler data loader pass.
|
||||
class SampleProfileLoaderPass : public PassInfoMixin<SampleProfileLoaderPass> {
|
||||
public:
|
||||
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
|
||||
SampleProfileLoaderPass(std::string File = "") : ProfileFileName(File) {}
|
||||
|
||||
private:
|
||||
std::string ProfileFileName;
|
||||
};
|
||||
|
||||
} // End llvm namespace
|
||||
#endif
|
@ -1,70 +0,0 @@
|
||||
//===-- CmpInstAnalysis.h - Utils to help fold compare insts ----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file holds routines to help analyse compare instructions
|
||||
// and fold them into constants or other compare instructions
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_TRANSFORMS_UTILS_CMPINSTANALYSIS_H
|
||||
#define LLVM_TRANSFORMS_UTILS_CMPINSTANALYSIS_H
|
||||
|
||||
#include "llvm/IR/InstrTypes.h"
|
||||
|
||||
namespace llvm {
|
||||
class ICmpInst;
|
||||
class Value;
|
||||
|
||||
/// Encode a icmp predicate into a three bit mask. These bits are carefully
|
||||
/// arranged to allow folding of expressions such as:
|
||||
///
|
||||
/// (A < B) | (A > B) --> (A != B)
|
||||
///
|
||||
/// Note that this is only valid if the first and second predicates have the
|
||||
/// same sign. It is illegal to do: (A u< B) | (A s> B)
|
||||
///
|
||||
/// Three bits are used to represent the condition, as follows:
|
||||
/// 0 A > B
|
||||
/// 1 A == B
|
||||
/// 2 A < B
|
||||
///
|
||||
/// <=> Value Definition
|
||||
/// 000 0 Always false
|
||||
/// 001 1 A > B
|
||||
/// 010 2 A == B
|
||||
/// 011 3 A >= B
|
||||
/// 100 4 A < B
|
||||
/// 101 5 A != B
|
||||
/// 110 6 A <= B
|
||||
/// 111 7 Always true
|
||||
///
|
||||
unsigned getICmpCode(const ICmpInst *ICI, bool InvertPred = false);
|
||||
|
||||
/// This is the complement of getICmpCode, which turns an opcode and two
|
||||
/// operands into either a constant true or false, or the predicate for a new
|
||||
/// ICmp instruction. The sign is passed in to determine which kind of
|
||||
/// predicate to use in the new icmp instruction.
|
||||
/// Non-NULL return value will be a true or false constant.
|
||||
/// NULL return means a new ICmp is needed. The predicate for which is output
|
||||
/// in NewICmpPred.
|
||||
Value *getICmpValue(bool Sign, unsigned Code, Value *LHS, Value *RHS,
|
||||
CmpInst::Predicate &NewICmpPred);
|
||||
|
||||
/// Return true if both predicates match sign or if at least one of them is an
|
||||
/// equality comparison (which is signless).
|
||||
bool PredicatesFoldable(CmpInst::Predicate p1, CmpInst::Predicate p2);
|
||||
|
||||
/// Decompose an icmp into the form ((X & Y) pred Z) if possible. The returned
|
||||
/// predicate is either == or !=. Returns false if decomposition fails.
|
||||
bool decomposeBitTestICmp(const ICmpInst *I, CmpInst::Predicate &Pred,
|
||||
Value *&X, Value *&Y, Value *&Z);
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif
|
@ -1,31 +0,0 @@
|
||||
//===- SimplifyInstructions.h - Remove redundant instructions ---*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This is a utility pass used for testing the InstructionSimplify analysis.
|
||||
// The analysis is applied to every instruction, and if it simplifies then the
|
||||
// instruction is replaced by the simplification. If you are looking for a pass
|
||||
// that performs serious instruction folding, use the instcombine pass instead.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_TRANSFORMS_UTILS_SIMPLIFYINSTRUCTIONS_H
|
||||
#define LLVM_TRANSFORMS_UTILS_SIMPLIFYINSTRUCTIONS_H
|
||||
|
||||
#include "llvm/IR/PassManager.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// This pass removes redundant instructions.
|
||||
class InstSimplifierPass : public PassInfoMixin<InstSimplifierPass> {
|
||||
public:
|
||||
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
|
||||
};
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_TRANSFORMS_UTILS_SIMPLIFYINSTRUCTIONS_H
|
@ -1,224 +0,0 @@
|
||||
//===- OptimizationDiagnosticInfo.cpp - Optimization Diagnostic -*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Optimization diagnostic interfaces. It's packaged as an analysis pass so
|
||||
// that by using this service passes become dependent on BFI as well. BFI is
|
||||
// used to compute the "hotness" of the diagnostic message.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Analysis/OptimizationDiagnosticInfo.h"
|
||||
#include "llvm/Analysis/BranchProbabilityInfo.h"
|
||||
#include "llvm/Analysis/LazyBlockFrequencyInfo.h"
|
||||
#include "llvm/Analysis/LoopInfo.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/DiagnosticInfo.h"
|
||||
#include "llvm/IR/Dominators.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
OptimizationRemarkEmitter::OptimizationRemarkEmitter(const Function *F)
|
||||
: F(F), BFI(nullptr) {
|
||||
if (!F->getContext().getDiagnosticsHotnessRequested())
|
||||
return;
|
||||
|
||||
// First create a dominator tree.
|
||||
DominatorTree DT;
|
||||
DT.recalculate(*const_cast<Function *>(F));
|
||||
|
||||
// Generate LoopInfo from it.
|
||||
LoopInfo LI;
|
||||
LI.analyze(DT);
|
||||
|
||||
// Then compute BranchProbabilityInfo.
|
||||
BranchProbabilityInfo BPI;
|
||||
BPI.calculate(*F, LI);
|
||||
|
||||
// Finally compute BFI.
|
||||
OwnedBFI = llvm::make_unique<BlockFrequencyInfo>(*F, BPI, LI);
|
||||
BFI = OwnedBFI.get();
|
||||
}
|
||||
|
||||
bool OptimizationRemarkEmitter::invalidate(
|
||||
Function &F, const PreservedAnalyses &PA,
|
||||
FunctionAnalysisManager::Invalidator &Inv) {
|
||||
// This analysis has no state and so can be trivially preserved but it needs
|
||||
// a fresh view of BFI if it was constructed with one.
|
||||
if (BFI && Inv.invalidate<BlockFrequencyAnalysis>(F, PA))
|
||||
return true;
|
||||
|
||||
// Otherwise this analysis result remains valid.
|
||||
return false;
|
||||
}
|
||||
|
||||
Optional<uint64_t> OptimizationRemarkEmitter::computeHotness(const Value *V) {
|
||||
if (!BFI)
|
||||
return None;
|
||||
|
||||
return BFI->getBlockProfileCount(cast<BasicBlock>(V));
|
||||
}
|
||||
|
||||
namespace llvm {
|
||||
namespace yaml {
|
||||
|
||||
void MappingTraits<DiagnosticInfoOptimizationBase *>::mapping(
|
||||
IO &io, DiagnosticInfoOptimizationBase *&OptDiag) {
|
||||
assert(io.outputting() && "input not yet implemented");
|
||||
|
||||
if (io.mapTag("!Passed",
|
||||
(OptDiag->getKind() == DK_OptimizationRemark ||
|
||||
OptDiag->getKind() == DK_MachineOptimizationRemark)))
|
||||
;
|
||||
else if (io.mapTag(
|
||||
"!Missed",
|
||||
(OptDiag->getKind() == DK_OptimizationRemarkMissed ||
|
||||
OptDiag->getKind() == DK_MachineOptimizationRemarkMissed)))
|
||||
;
|
||||
else if (io.mapTag(
|
||||
"!Analysis",
|
||||
(OptDiag->getKind() == DK_OptimizationRemarkAnalysis ||
|
||||
OptDiag->getKind() == DK_MachineOptimizationRemarkAnalysis)))
|
||||
;
|
||||
else if (io.mapTag("!AnalysisFPCommute",
|
||||
OptDiag->getKind() ==
|
||||
DK_OptimizationRemarkAnalysisFPCommute))
|
||||
;
|
||||
else if (io.mapTag("!AnalysisAliasing",
|
||||
OptDiag->getKind() ==
|
||||
DK_OptimizationRemarkAnalysisAliasing))
|
||||
;
|
||||
else if (io.mapTag("!Failure", OptDiag->getKind() == DK_OptimizationFailure))
|
||||
;
|
||||
else
|
||||
llvm_unreachable("Unknown remark type");
|
||||
|
||||
// These are read-only for now.
|
||||
DiagnosticLocation DL = OptDiag->getLocation();
|
||||
StringRef FN =
|
||||
GlobalValue::dropLLVMManglingEscape(OptDiag->getFunction().getName());
|
||||
|
||||
StringRef PassName(OptDiag->PassName);
|
||||
io.mapRequired("Pass", PassName);
|
||||
io.mapRequired("Name", OptDiag->RemarkName);
|
||||
if (!io.outputting() || DL.isValid())
|
||||
io.mapOptional("DebugLoc", DL);
|
||||
io.mapRequired("Function", FN);
|
||||
io.mapOptional("Hotness", OptDiag->Hotness);
|
||||
io.mapOptional("Args", OptDiag->Args);
|
||||
}
|
||||
|
||||
template <> struct MappingTraits<DiagnosticLocation> {
|
||||
static void mapping(IO &io, DiagnosticLocation &DL) {
|
||||
assert(io.outputting() && "input not yet implemented");
|
||||
|
||||
StringRef File = DL.getFilename();
|
||||
unsigned Line = DL.getLine();
|
||||
unsigned Col = DL.getColumn();
|
||||
|
||||
io.mapRequired("File", File);
|
||||
io.mapRequired("Line", Line);
|
||||
io.mapRequired("Column", Col);
|
||||
}
|
||||
|
||||
static const bool flow = true;
|
||||
};
|
||||
|
||||
// Implement this as a mapping for now to get proper quotation for the value.
|
||||
template <> struct MappingTraits<DiagnosticInfoOptimizationBase::Argument> {
|
||||
static void mapping(IO &io, DiagnosticInfoOptimizationBase::Argument &A) {
|
||||
assert(io.outputting() && "input not yet implemented");
|
||||
io.mapRequired(A.Key.data(), A.Val);
|
||||
if (A.Loc.isValid())
|
||||
io.mapOptional("DebugLoc", A.Loc);
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace yaml
|
||||
} // end namespace llvm
|
||||
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(DiagnosticInfoOptimizationBase::Argument)
|
||||
|
||||
void OptimizationRemarkEmitter::computeHotness(
|
||||
DiagnosticInfoIROptimization &OptDiag) {
|
||||
const Value *V = OptDiag.getCodeRegion();
|
||||
if (V)
|
||||
OptDiag.setHotness(computeHotness(V));
|
||||
}
|
||||
|
||||
void OptimizationRemarkEmitter::emit(
|
||||
DiagnosticInfoOptimizationBase &OptDiagBase) {
|
||||
auto &OptDiag = cast<DiagnosticInfoIROptimization>(OptDiagBase);
|
||||
computeHotness(OptDiag);
|
||||
// If a diagnostic has a hotness value, then only emit it if its hotness
|
||||
// meets the threshold.
|
||||
if (OptDiag.getHotness() &&
|
||||
*OptDiag.getHotness() <
|
||||
F->getContext().getDiagnosticsHotnessThreshold()) {
|
||||
return;
|
||||
}
|
||||
|
||||
yaml::Output *Out = F->getContext().getDiagnosticsOutputFile();
|
||||
if (Out) {
|
||||
auto *P = const_cast<DiagnosticInfoOptimizationBase *>(&OptDiagBase);
|
||||
*Out << P;
|
||||
}
|
||||
// FIXME: now that IsVerbose is part of DI, filtering for this will be moved
|
||||
// from here to clang.
|
||||
if (!OptDiag.isVerbose() || shouldEmitVerbose())
|
||||
F->getContext().diagnose(OptDiag);
|
||||
}
|
||||
|
||||
OptimizationRemarkEmitterWrapperPass::OptimizationRemarkEmitterWrapperPass()
|
||||
: FunctionPass(ID) {
|
||||
initializeOptimizationRemarkEmitterWrapperPassPass(
|
||||
*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
bool OptimizationRemarkEmitterWrapperPass::runOnFunction(Function &Fn) {
|
||||
BlockFrequencyInfo *BFI;
|
||||
|
||||
if (Fn.getContext().getDiagnosticsHotnessRequested())
|
||||
BFI = &getAnalysis<LazyBlockFrequencyInfoPass>().getBFI();
|
||||
else
|
||||
BFI = nullptr;
|
||||
|
||||
ORE = llvm::make_unique<OptimizationRemarkEmitter>(&Fn, BFI);
|
||||
return false;
|
||||
}
|
||||
|
||||
void OptimizationRemarkEmitterWrapperPass::getAnalysisUsage(
|
||||
AnalysisUsage &AU) const {
|
||||
LazyBlockFrequencyInfoPass::getLazyBFIAnalysisUsage(AU);
|
||||
AU.setPreservesAll();
|
||||
}
|
||||
|
||||
AnalysisKey OptimizationRemarkEmitterAnalysis::Key;
|
||||
|
||||
OptimizationRemarkEmitter
|
||||
OptimizationRemarkEmitterAnalysis::run(Function &F,
|
||||
FunctionAnalysisManager &AM) {
|
||||
BlockFrequencyInfo *BFI;
|
||||
|
||||
if (F.getContext().getDiagnosticsHotnessRequested())
|
||||
BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
|
||||
else
|
||||
BFI = nullptr;
|
||||
|
||||
return OptimizationRemarkEmitter(&F, BFI);
|
||||
}
|
||||
|
||||
char OptimizationRemarkEmitterWrapperPass::ID = 0;
|
||||
static const char ore_name[] = "Optimization Remark Emitter";
|
||||
#define ORE_NAME "opt-remark-emitter"
|
||||
|
||||
INITIALIZE_PASS_BEGIN(OptimizationRemarkEmitterWrapperPass, ORE_NAME, ore_name,
|
||||
false, true)
|
||||
INITIALIZE_PASS_DEPENDENCY(LazyBFIPass)
|
||||
INITIALIZE_PASS_END(OptimizationRemarkEmitterWrapperPass, ORE_NAME, ore_name,
|
||||
false, true)
|
@ -1,347 +0,0 @@
|
||||
//===- SparsePropagation.cpp - Sparse Conditional Property Propagation ----===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements an abstract sparse conditional propagation algorithm,
|
||||
// modeled after SCCP, but with a customizable lattice function.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Analysis/SparsePropagation.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "sparseprop"
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// AbstractLatticeFunction Implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
AbstractLatticeFunction::~AbstractLatticeFunction() {}
|
||||
|
||||
/// PrintValue - Render the specified lattice value to the specified stream.
|
||||
void AbstractLatticeFunction::PrintValue(LatticeVal V, raw_ostream &OS) {
|
||||
if (V == UndefVal)
|
||||
OS << "undefined";
|
||||
else if (V == OverdefinedVal)
|
||||
OS << "overdefined";
|
||||
else if (V == UntrackedVal)
|
||||
OS << "untracked";
|
||||
else
|
||||
OS << "unknown lattice value";
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SparseSolver Implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// getOrInitValueState - Return the LatticeVal object that corresponds to the
|
||||
/// value, initializing the value's state if it hasn't been entered into the
|
||||
/// map yet. This function is necessary because not all values should start
|
||||
/// out in the underdefined state... Arguments should be overdefined, and
|
||||
/// constants should be marked as constants.
|
||||
///
|
||||
SparseSolver::LatticeVal SparseSolver::getOrInitValueState(Value *V) {
|
||||
DenseMap<Value*, LatticeVal>::iterator I = ValueState.find(V);
|
||||
if (I != ValueState.end()) return I->second; // Common case, in the map
|
||||
|
||||
LatticeVal LV;
|
||||
if (LatticeFunc->IsUntrackedValue(V))
|
||||
return LatticeFunc->getUntrackedVal();
|
||||
else if (Constant *C = dyn_cast<Constant>(V))
|
||||
LV = LatticeFunc->ComputeConstant(C);
|
||||
else if (Argument *A = dyn_cast<Argument>(V))
|
||||
LV = LatticeFunc->ComputeArgument(A);
|
||||
else if (!isa<Instruction>(V))
|
||||
// All other non-instructions are overdefined.
|
||||
LV = LatticeFunc->getOverdefinedVal();
|
||||
else
|
||||
// All instructions are underdefined by default.
|
||||
LV = LatticeFunc->getUndefVal();
|
||||
|
||||
// If this value is untracked, don't add it to the map.
|
||||
if (LV == LatticeFunc->getUntrackedVal())
|
||||
return LV;
|
||||
return ValueState[V] = LV;
|
||||
}
|
||||
|
||||
/// UpdateState - When the state for some instruction is potentially updated,
|
||||
/// this function notices and adds I to the worklist if needed.
|
||||
void SparseSolver::UpdateState(Instruction &Inst, LatticeVal V) {
|
||||
DenseMap<Value*, LatticeVal>::iterator I = ValueState.find(&Inst);
|
||||
if (I != ValueState.end() && I->second == V)
|
||||
return; // No change.
|
||||
|
||||
// An update. Visit uses of I.
|
||||
ValueState[&Inst] = V;
|
||||
InstWorkList.push_back(&Inst);
|
||||
}
|
||||
|
||||
/// MarkBlockExecutable - This method can be used by clients to mark all of
|
||||
/// the blocks that are known to be intrinsically live in the processed unit.
|
||||
void SparseSolver::MarkBlockExecutable(BasicBlock *BB) {
|
||||
DEBUG(dbgs() << "Marking Block Executable: " << BB->getName() << "\n");
|
||||
BBExecutable.insert(BB); // Basic block is executable!
|
||||
BBWorkList.push_back(BB); // Add the block to the work list!
|
||||
}
|
||||
|
||||
/// markEdgeExecutable - Mark a basic block as executable, adding it to the BB
|
||||
/// work list if it is not already executable...
|
||||
void SparseSolver::markEdgeExecutable(BasicBlock *Source, BasicBlock *Dest) {
|
||||
if (!KnownFeasibleEdges.insert(Edge(Source, Dest)).second)
|
||||
return; // This edge is already known to be executable!
|
||||
|
||||
DEBUG(dbgs() << "Marking Edge Executable: " << Source->getName()
|
||||
<< " -> " << Dest->getName() << "\n");
|
||||
|
||||
if (BBExecutable.count(Dest)) {
|
||||
// The destination is already executable, but we just made an edge
|
||||
// feasible that wasn't before. Revisit the PHI nodes in the block
|
||||
// because they have potentially new operands.
|
||||
for (BasicBlock::iterator I = Dest->begin(); isa<PHINode>(I); ++I)
|
||||
visitPHINode(*cast<PHINode>(I));
|
||||
|
||||
} else {
|
||||
MarkBlockExecutable(Dest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// getFeasibleSuccessors - Return a vector of booleans to indicate which
|
||||
/// successors are reachable from a given terminator instruction.
|
||||
void SparseSolver::getFeasibleSuccessors(TerminatorInst &TI,
|
||||
SmallVectorImpl<bool> &Succs,
|
||||
bool AggressiveUndef) {
|
||||
Succs.resize(TI.getNumSuccessors());
|
||||
if (TI.getNumSuccessors() == 0) return;
|
||||
|
||||
if (BranchInst *BI = dyn_cast<BranchInst>(&TI)) {
|
||||
if (BI->isUnconditional()) {
|
||||
Succs[0] = true;
|
||||
return;
|
||||
}
|
||||
|
||||
LatticeVal BCValue;
|
||||
if (AggressiveUndef)
|
||||
BCValue = getOrInitValueState(BI->getCondition());
|
||||
else
|
||||
BCValue = getLatticeState(BI->getCondition());
|
||||
|
||||
if (BCValue == LatticeFunc->getOverdefinedVal() ||
|
||||
BCValue == LatticeFunc->getUntrackedVal()) {
|
||||
// Overdefined condition variables can branch either way.
|
||||
Succs[0] = Succs[1] = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// If undefined, neither is feasible yet.
|
||||
if (BCValue == LatticeFunc->getUndefVal())
|
||||
return;
|
||||
|
||||
Constant *C = LatticeFunc->GetConstant(BCValue, BI->getCondition(), *this);
|
||||
if (!C || !isa<ConstantInt>(C)) {
|
||||
// Non-constant values can go either way.
|
||||
Succs[0] = Succs[1] = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Constant condition variables mean the branch can only go a single way
|
||||
Succs[C->isNullValue()] = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isa<InvokeInst>(TI)) {
|
||||
// Invoke instructions successors are always executable.
|
||||
// TODO: Could ask the lattice function if the value can throw.
|
||||
Succs[0] = Succs[1] = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isa<IndirectBrInst>(TI)) {
|
||||
Succs.assign(Succs.size(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
SwitchInst &SI = cast<SwitchInst>(TI);
|
||||
LatticeVal SCValue;
|
||||
if (AggressiveUndef)
|
||||
SCValue = getOrInitValueState(SI.getCondition());
|
||||
else
|
||||
SCValue = getLatticeState(SI.getCondition());
|
||||
|
||||
if (SCValue == LatticeFunc->getOverdefinedVal() ||
|
||||
SCValue == LatticeFunc->getUntrackedVal()) {
|
||||
// All destinations are executable!
|
||||
Succs.assign(TI.getNumSuccessors(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
// If undefined, neither is feasible yet.
|
||||
if (SCValue == LatticeFunc->getUndefVal())
|
||||
return;
|
||||
|
||||
Constant *C = LatticeFunc->GetConstant(SCValue, SI.getCondition(), *this);
|
||||
if (!C || !isa<ConstantInt>(C)) {
|
||||
// All destinations are executable!
|
||||
Succs.assign(TI.getNumSuccessors(), true);
|
||||
return;
|
||||
}
|
||||
SwitchInst::CaseHandle Case = *SI.findCaseValue(cast<ConstantInt>(C));
|
||||
Succs[Case.getSuccessorIndex()] = true;
|
||||
}
|
||||
|
||||
|
||||
/// isEdgeFeasible - Return true if the control flow edge from the 'From'
|
||||
/// basic block to the 'To' basic block is currently feasible...
|
||||
bool SparseSolver::isEdgeFeasible(BasicBlock *From, BasicBlock *To,
|
||||
bool AggressiveUndef) {
|
||||
SmallVector<bool, 16> SuccFeasible;
|
||||
TerminatorInst *TI = From->getTerminator();
|
||||
getFeasibleSuccessors(*TI, SuccFeasible, AggressiveUndef);
|
||||
|
||||
for (unsigned i = 0, e = TI->getNumSuccessors(); i != e; ++i)
|
||||
if (TI->getSuccessor(i) == To && SuccFeasible[i])
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SparseSolver::visitTerminatorInst(TerminatorInst &TI) {
|
||||
SmallVector<bool, 16> SuccFeasible;
|
||||
getFeasibleSuccessors(TI, SuccFeasible, true);
|
||||
|
||||
BasicBlock *BB = TI.getParent();
|
||||
|
||||
// Mark all feasible successors executable...
|
||||
for (unsigned i = 0, e = SuccFeasible.size(); i != e; ++i)
|
||||
if (SuccFeasible[i])
|
||||
markEdgeExecutable(BB, TI.getSuccessor(i));
|
||||
}
|
||||
|
||||
void SparseSolver::visitPHINode(PHINode &PN) {
|
||||
// The lattice function may store more information on a PHINode than could be
|
||||
// computed from its incoming values. For example, SSI form stores its sigma
|
||||
// functions as PHINodes with a single incoming value.
|
||||
if (LatticeFunc->IsSpecialCasedPHI(&PN)) {
|
||||
LatticeVal IV = LatticeFunc->ComputeInstructionState(PN, *this);
|
||||
if (IV != LatticeFunc->getUntrackedVal())
|
||||
UpdateState(PN, IV);
|
||||
return;
|
||||
}
|
||||
|
||||
LatticeVal PNIV = getOrInitValueState(&PN);
|
||||
LatticeVal Overdefined = LatticeFunc->getOverdefinedVal();
|
||||
|
||||
// If this value is already overdefined (common) just return.
|
||||
if (PNIV == Overdefined || PNIV == LatticeFunc->getUntrackedVal())
|
||||
return; // Quick exit
|
||||
|
||||
// Super-extra-high-degree PHI nodes are unlikely to ever be interesting,
|
||||
// and slow us down a lot. Just mark them overdefined.
|
||||
if (PN.getNumIncomingValues() > 64) {
|
||||
UpdateState(PN, Overdefined);
|
||||
return;
|
||||
}
|
||||
|
||||
// Look at all of the executable operands of the PHI node. If any of them
|
||||
// are overdefined, the PHI becomes overdefined as well. Otherwise, ask the
|
||||
// transfer function to give us the merge of the incoming values.
|
||||
for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i) {
|
||||
// If the edge is not yet known to be feasible, it doesn't impact the PHI.
|
||||
if (!isEdgeFeasible(PN.getIncomingBlock(i), PN.getParent(), true))
|
||||
continue;
|
||||
|
||||
// Merge in this value.
|
||||
LatticeVal OpVal = getOrInitValueState(PN.getIncomingValue(i));
|
||||
if (OpVal != PNIV)
|
||||
PNIV = LatticeFunc->MergeValues(PNIV, OpVal);
|
||||
|
||||
if (PNIV == Overdefined)
|
||||
break; // Rest of input values don't matter.
|
||||
}
|
||||
|
||||
// Update the PHI with the compute value, which is the merge of the inputs.
|
||||
UpdateState(PN, PNIV);
|
||||
}
|
||||
|
||||
|
||||
void SparseSolver::visitInst(Instruction &I) {
|
||||
// PHIs are handled by the propagation logic, they are never passed into the
|
||||
// transfer functions.
|
||||
if (PHINode *PN = dyn_cast<PHINode>(&I))
|
||||
return visitPHINode(*PN);
|
||||
|
||||
// Otherwise, ask the transfer function what the result is. If this is
|
||||
// something that we care about, remember it.
|
||||
LatticeVal IV = LatticeFunc->ComputeInstructionState(I, *this);
|
||||
if (IV != LatticeFunc->getUntrackedVal())
|
||||
UpdateState(I, IV);
|
||||
|
||||
if (TerminatorInst *TI = dyn_cast<TerminatorInst>(&I))
|
||||
visitTerminatorInst(*TI);
|
||||
}
|
||||
|
||||
void SparseSolver::Solve(Function &F) {
|
||||
MarkBlockExecutable(&F.getEntryBlock());
|
||||
|
||||
// Process the work lists until they are empty!
|
||||
while (!BBWorkList.empty() || !InstWorkList.empty()) {
|
||||
// Process the instruction work list.
|
||||
while (!InstWorkList.empty()) {
|
||||
Instruction *I = InstWorkList.back();
|
||||
InstWorkList.pop_back();
|
||||
|
||||
DEBUG(dbgs() << "\nPopped off I-WL: " << *I << "\n");
|
||||
|
||||
// "I" got into the work list because it made a transition. See if any
|
||||
// users are both live and in need of updating.
|
||||
for (User *U : I->users()) {
|
||||
Instruction *UI = cast<Instruction>(U);
|
||||
if (BBExecutable.count(UI->getParent())) // Inst is executable?
|
||||
visitInst(*UI);
|
||||
}
|
||||
}
|
||||
|
||||
// Process the basic block work list.
|
||||
while (!BBWorkList.empty()) {
|
||||
BasicBlock *BB = BBWorkList.back();
|
||||
BBWorkList.pop_back();
|
||||
|
||||
DEBUG(dbgs() << "\nPopped off BBWL: " << *BB);
|
||||
|
||||
// Notify all instructions in this basic block that they are newly
|
||||
// executable.
|
||||
for (Instruction &I : *BB)
|
||||
visitInst(I);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SparseSolver::Print(Function &F, raw_ostream &OS) const {
|
||||
OS << "\nFUNCTION: " << F.getName() << "\n";
|
||||
for (auto &BB : F) {
|
||||
if (!BBExecutable.count(&BB))
|
||||
OS << "INFEASIBLE: ";
|
||||
OS << "\t";
|
||||
if (BB.hasName())
|
||||
OS << BB.getName() << ":\n";
|
||||
else
|
||||
OS << "; anon bb\n";
|
||||
for (auto &I : BB) {
|
||||
LatticeFunc->PrintValue(getLatticeState(&I), OS);
|
||||
OS << I << "\n";
|
||||
}
|
||||
|
||||
OS << "\n";
|
||||
}
|
||||
}
|
||||
|
@ -1,287 +0,0 @@
|
||||
//=-- llvm/CodeGen/DwarfAccelTable.cpp - Dwarf Accelerator Tables -*- C++ -*-=//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains support for writing dwarf accelerator tables.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "DwarfAccelTable.h"
|
||||
#include "DwarfCompileUnit.h"
|
||||
#include "DwarfDebug.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/CodeGen/AsmPrinter.h"
|
||||
#include "llvm/CodeGen/DIE.h"
|
||||
#include "llvm/MC/MCExpr.h"
|
||||
#include "llvm/MC/MCStreamer.h"
|
||||
#include "llvm/MC/MCSymbol.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
// The length of the header data is always going to be 4 + 4 + 4*NumAtoms.
|
||||
DwarfAccelTable::DwarfAccelTable(ArrayRef<DwarfAccelTable::Atom> atomList)
|
||||
: Header(8 + (atomList.size() * 4)), HeaderData(atomList),
|
||||
Entries(Allocator) {}
|
||||
|
||||
void DwarfAccelTable::AddName(DwarfStringPoolEntryRef Name, const DIE *die,
|
||||
char Flags) {
|
||||
assert(Data.empty() && "Already finalized!");
|
||||
// If the string is in the list already then add this die to the list
|
||||
// otherwise add a new one.
|
||||
DataArray &DIEs = Entries[Name.getString()];
|
||||
assert(!DIEs.Name || DIEs.Name == Name);
|
||||
DIEs.Name = Name;
|
||||
DIEs.Values.push_back(new (Allocator) HashDataContents(die, Flags));
|
||||
}
|
||||
|
||||
void DwarfAccelTable::ComputeBucketCount() {
|
||||
// First get the number of unique hashes.
|
||||
std::vector<uint32_t> uniques(Data.size());
|
||||
for (size_t i = 0, e = Data.size(); i < e; ++i)
|
||||
uniques[i] = Data[i]->HashValue;
|
||||
array_pod_sort(uniques.begin(), uniques.end());
|
||||
std::vector<uint32_t>::iterator p =
|
||||
std::unique(uniques.begin(), uniques.end());
|
||||
uint32_t num = std::distance(uniques.begin(), p);
|
||||
|
||||
// Then compute the bucket size, minimum of 1 bucket.
|
||||
if (num > 1024)
|
||||
Header.bucket_count = num / 4;
|
||||
else if (num > 16)
|
||||
Header.bucket_count = num / 2;
|
||||
else
|
||||
Header.bucket_count = num > 0 ? num : 1;
|
||||
|
||||
Header.hashes_count = num;
|
||||
}
|
||||
|
||||
// compareDIEs - comparison predicate that sorts DIEs by their offset.
|
||||
static bool compareDIEs(const DwarfAccelTable::HashDataContents *A,
|
||||
const DwarfAccelTable::HashDataContents *B) {
|
||||
return A->Die->getOffset() < B->Die->getOffset();
|
||||
}
|
||||
|
||||
void DwarfAccelTable::FinalizeTable(AsmPrinter *Asm, StringRef Prefix) {
|
||||
// Create the individual hash data outputs.
|
||||
Data.reserve(Entries.size());
|
||||
for (StringMap<DataArray>::iterator EI = Entries.begin(), EE = Entries.end();
|
||||
EI != EE; ++EI) {
|
||||
|
||||
// Unique the entries.
|
||||
std::stable_sort(EI->second.Values.begin(), EI->second.Values.end(), compareDIEs);
|
||||
EI->second.Values.erase(
|
||||
std::unique(EI->second.Values.begin(), EI->second.Values.end()),
|
||||
EI->second.Values.end());
|
||||
|
||||
HashData *Entry = new (Allocator) HashData(EI->getKey(), EI->second);
|
||||
Data.push_back(Entry);
|
||||
}
|
||||
|
||||
// Figure out how many buckets we need, then compute the bucket
|
||||
// contents and the final ordering. We'll emit the hashes and offsets
|
||||
// by doing a walk during the emission phase. We add temporary
|
||||
// symbols to the data so that we can reference them during the offset
|
||||
// later, we'll emit them when we emit the data.
|
||||
ComputeBucketCount();
|
||||
|
||||
// Compute bucket contents and final ordering.
|
||||
Buckets.resize(Header.bucket_count);
|
||||
for (size_t i = 0, e = Data.size(); i < e; ++i) {
|
||||
uint32_t bucket = Data[i]->HashValue % Header.bucket_count;
|
||||
Buckets[bucket].push_back(Data[i]);
|
||||
Data[i]->Sym = Asm->createTempSymbol(Prefix);
|
||||
}
|
||||
|
||||
// Sort the contents of the buckets by hash value so that hash
|
||||
// collisions end up together. Stable sort makes testing easier and
|
||||
// doesn't cost much more.
|
||||
for (size_t i = 0; i < Buckets.size(); ++i)
|
||||
std::stable_sort(Buckets[i].begin(), Buckets[i].end(),
|
||||
[] (HashData *LHS, HashData *RHS) {
|
||||
return LHS->HashValue < RHS->HashValue;
|
||||
});
|
||||
}
|
||||
|
||||
// Emits the header for the table via the AsmPrinter.
|
||||
void DwarfAccelTable::EmitHeader(AsmPrinter *Asm) {
|
||||
Asm->OutStreamer->AddComment("Header Magic");
|
||||
Asm->EmitInt32(Header.magic);
|
||||
Asm->OutStreamer->AddComment("Header Version");
|
||||
Asm->EmitInt16(Header.version);
|
||||
Asm->OutStreamer->AddComment("Header Hash Function");
|
||||
Asm->EmitInt16(Header.hash_function);
|
||||
Asm->OutStreamer->AddComment("Header Bucket Count");
|
||||
Asm->EmitInt32(Header.bucket_count);
|
||||
Asm->OutStreamer->AddComment("Header Hash Count");
|
||||
Asm->EmitInt32(Header.hashes_count);
|
||||
Asm->OutStreamer->AddComment("Header Data Length");
|
||||
Asm->EmitInt32(Header.header_data_len);
|
||||
Asm->OutStreamer->AddComment("HeaderData Die Offset Base");
|
||||
Asm->EmitInt32(HeaderData.die_offset_base);
|
||||
Asm->OutStreamer->AddComment("HeaderData Atom Count");
|
||||
Asm->EmitInt32(HeaderData.Atoms.size());
|
||||
for (size_t i = 0; i < HeaderData.Atoms.size(); i++) {
|
||||
Atom A = HeaderData.Atoms[i];
|
||||
Asm->OutStreamer->AddComment(dwarf::AtomTypeString(A.type));
|
||||
Asm->EmitInt16(A.type);
|
||||
Asm->OutStreamer->AddComment(dwarf::FormEncodingString(A.form));
|
||||
Asm->EmitInt16(A.form);
|
||||
}
|
||||
}
|
||||
|
||||
// Walk through and emit the buckets for the table. Each index is
|
||||
// an offset into the list of hashes.
|
||||
void DwarfAccelTable::EmitBuckets(AsmPrinter *Asm) {
|
||||
unsigned index = 0;
|
||||
for (size_t i = 0, e = Buckets.size(); i < e; ++i) {
|
||||
Asm->OutStreamer->AddComment("Bucket " + Twine(i));
|
||||
if (Buckets[i].size() != 0)
|
||||
Asm->EmitInt32(index);
|
||||
else
|
||||
Asm->EmitInt32(UINT32_MAX);
|
||||
// Buckets point in the list of hashes, not to the data. Do not
|
||||
// increment the index multiple times in case of hash collisions.
|
||||
uint64_t PrevHash = UINT64_MAX;
|
||||
for (auto *HD : Buckets[i]) {
|
||||
uint32_t HashValue = HD->HashValue;
|
||||
if (PrevHash != HashValue)
|
||||
++index;
|
||||
PrevHash = HashValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Walk through the buckets and emit the individual hashes for each
|
||||
// bucket.
|
||||
void DwarfAccelTable::EmitHashes(AsmPrinter *Asm) {
|
||||
uint64_t PrevHash = UINT64_MAX;
|
||||
for (size_t i = 0, e = Buckets.size(); i < e; ++i) {
|
||||
for (HashList::const_iterator HI = Buckets[i].begin(),
|
||||
HE = Buckets[i].end();
|
||||
HI != HE; ++HI) {
|
||||
uint32_t HashValue = (*HI)->HashValue;
|
||||
if (PrevHash == HashValue)
|
||||
continue;
|
||||
Asm->OutStreamer->AddComment("Hash in Bucket " + Twine(i));
|
||||
Asm->EmitInt32(HashValue);
|
||||
PrevHash = HashValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Walk through the buckets and emit the individual offsets for each
|
||||
// element in each bucket. This is done via a symbol subtraction from the
|
||||
// beginning of the section. The non-section symbol will be output later
|
||||
// when we emit the actual data.
|
||||
void DwarfAccelTable::emitOffsets(AsmPrinter *Asm, const MCSymbol *SecBegin) {
|
||||
uint64_t PrevHash = UINT64_MAX;
|
||||
for (size_t i = 0, e = Buckets.size(); i < e; ++i) {
|
||||
for (HashList::const_iterator HI = Buckets[i].begin(),
|
||||
HE = Buckets[i].end();
|
||||
HI != HE; ++HI) {
|
||||
uint32_t HashValue = (*HI)->HashValue;
|
||||
if (PrevHash == HashValue)
|
||||
continue;
|
||||
PrevHash = HashValue;
|
||||
Asm->OutStreamer->AddComment("Offset in Bucket " + Twine(i));
|
||||
MCContext &Context = Asm->OutStreamer->getContext();
|
||||
const MCExpr *Sub = MCBinaryExpr::createSub(
|
||||
MCSymbolRefExpr::create((*HI)->Sym, Context),
|
||||
MCSymbolRefExpr::create(SecBegin, Context), Context);
|
||||
Asm->OutStreamer->EmitValue(Sub, sizeof(uint32_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Walk through the buckets and emit the full data for each element in
|
||||
// the bucket. For the string case emit the dies and the various offsets.
|
||||
// Terminate each HashData bucket with 0.
|
||||
void DwarfAccelTable::EmitData(AsmPrinter *Asm, DwarfDebug *D) {
|
||||
for (size_t i = 0, e = Buckets.size(); i < e; ++i) {
|
||||
uint64_t PrevHash = UINT64_MAX;
|
||||
for (HashList::const_iterator HI = Buckets[i].begin(),
|
||||
HE = Buckets[i].end();
|
||||
HI != HE; ++HI) {
|
||||
// Terminate the previous entry if there is no hash collision
|
||||
// with the current one.
|
||||
if (PrevHash != UINT64_MAX && PrevHash != (*HI)->HashValue)
|
||||
Asm->EmitInt32(0);
|
||||
// Remember to emit the label for our offset.
|
||||
Asm->OutStreamer->EmitLabel((*HI)->Sym);
|
||||
Asm->OutStreamer->AddComment((*HI)->Str);
|
||||
Asm->emitDwarfStringOffset((*HI)->Data.Name);
|
||||
Asm->OutStreamer->AddComment("Num DIEs");
|
||||
Asm->EmitInt32((*HI)->Data.Values.size());
|
||||
for (HashDataContents *HD : (*HI)->Data.Values) {
|
||||
// Emit the DIE offset
|
||||
Asm->EmitInt32(HD->Die->getDebugSectionOffset());
|
||||
// If we have multiple Atoms emit that info too.
|
||||
// FIXME: A bit of a hack, we either emit only one atom or all info.
|
||||
if (HeaderData.Atoms.size() > 1) {
|
||||
Asm->EmitInt16(HD->Die->getTag());
|
||||
Asm->EmitInt8(HD->Flags);
|
||||
}
|
||||
}
|
||||
PrevHash = (*HI)->HashValue;
|
||||
}
|
||||
// Emit the final end marker for the bucket.
|
||||
if (!Buckets[i].empty())
|
||||
Asm->EmitInt32(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit the entire data structure to the output file.
|
||||
void DwarfAccelTable::emit(AsmPrinter *Asm, const MCSymbol *SecBegin,
|
||||
DwarfDebug *D) {
|
||||
// Emit the header.
|
||||
EmitHeader(Asm);
|
||||
|
||||
// Emit the buckets.
|
||||
EmitBuckets(Asm);
|
||||
|
||||
// Emit the hashes.
|
||||
EmitHashes(Asm);
|
||||
|
||||
// Emit the offsets.
|
||||
emitOffsets(Asm, SecBegin);
|
||||
|
||||
// Emit the hash data.
|
||||
EmitData(Asm, D);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void DwarfAccelTable::print(raw_ostream &O) {
|
||||
|
||||
Header.print(O);
|
||||
HeaderData.print(O);
|
||||
|
||||
O << "Entries: \n";
|
||||
for (StringMap<DataArray>::const_iterator EI = Entries.begin(),
|
||||
EE = Entries.end();
|
||||
EI != EE; ++EI) {
|
||||
O << "Name: " << EI->getKeyData() << "\n";
|
||||
for (HashDataContents *HD : EI->second.Values)
|
||||
HD->print(O);
|
||||
}
|
||||
|
||||
O << "Buckets and Hashes: \n";
|
||||
for (size_t i = 0, e = Buckets.size(); i < e; ++i)
|
||||
for (HashList::const_iterator HI = Buckets[i].begin(),
|
||||
HE = Buckets[i].end();
|
||||
HI != HE; ++HI)
|
||||
(*HI)->print(O);
|
||||
|
||||
O << "Data: \n";
|
||||
for (std::vector<HashData *>::const_iterator DI = Data.begin(),
|
||||
DE = Data.end();
|
||||
DI != DE; ++DI)
|
||||
(*DI)->print(O);
|
||||
}
|
||||
#endif
|
@ -1,255 +0,0 @@
|
||||
//==-- llvm/CodeGen/DwarfAccelTable.h - Dwarf Accelerator Tables -*- C++ -*-==//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains support for writing dwarf accelerator tables.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_CODEGEN_ASMPRINTER_DWARFACCELTABLE_H
|
||||
#define LLVM_LIB_CODEGEN_ASMPRINTER_DWARFACCELTABLE_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/BinaryFormat/Dwarf.h"
|
||||
#include "llvm/CodeGen/DIE.h"
|
||||
#include "llvm/MC/MCSymbol.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/DataTypes.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
#include "llvm/Support/FormattedStream.h"
|
||||
#include <vector>
|
||||
|
||||
// The dwarf accelerator tables are an indirect hash table optimized
|
||||
// for null lookup rather than access to known data. They are output into
|
||||
// an on-disk format that looks like this:
|
||||
//
|
||||
// .-------------.
|
||||
// | HEADER |
|
||||
// |-------------|
|
||||
// | BUCKETS |
|
||||
// |-------------|
|
||||
// | HASHES |
|
||||
// |-------------|
|
||||
// | OFFSETS |
|
||||
// |-------------|
|
||||
// | DATA |
|
||||
// `-------------'
|
||||
//
|
||||
// where the header contains a magic number, version, type of hash function,
|
||||
// the number of buckets, total number of hashes, and room for a special
|
||||
// struct of data and the length of that struct.
|
||||
//
|
||||
// The buckets contain an index (e.g. 6) into the hashes array. The hashes
|
||||
// section contains all of the 32-bit hash values in contiguous memory, and
|
||||
// the offsets contain the offset into the data area for the particular
|
||||
// hash.
|
||||
//
|
||||
// For a lookup example, we could hash a function name and take it modulo the
|
||||
// number of buckets giving us our bucket. From there we take the bucket value
|
||||
// as an index into the hashes table and look at each successive hash as long
|
||||
// as the hash value is still the same modulo result (bucket value) as earlier.
|
||||
// If we have a match we look at that same entry in the offsets table and
|
||||
// grab the offset in the data for our final match.
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class AsmPrinter;
|
||||
class DwarfDebug;
|
||||
|
||||
class DwarfAccelTable {
|
||||
|
||||
static uint32_t HashDJB(StringRef Str) {
|
||||
uint32_t h = 5381;
|
||||
for (unsigned i = 0, e = Str.size(); i != e; ++i)
|
||||
h = ((h << 5) + h) + Str[i];
|
||||
return h;
|
||||
}
|
||||
|
||||
// Helper function to compute the number of buckets needed based on
|
||||
// the number of unique hashes.
|
||||
void ComputeBucketCount(void);
|
||||
|
||||
struct TableHeader {
|
||||
uint32_t magic; // 'HASH' magic value to allow endian detection
|
||||
uint16_t version; // Version number.
|
||||
uint16_t hash_function; // The hash function enumeration that was used.
|
||||
uint32_t bucket_count; // The number of buckets in this hash table.
|
||||
uint32_t hashes_count; // The total number of unique hash values
|
||||
// and hash data offsets in this table.
|
||||
uint32_t header_data_len; // The bytes to skip to get to the hash
|
||||
// indexes (buckets) for correct alignment.
|
||||
// Also written to disk is the implementation specific header data.
|
||||
|
||||
static const uint32_t MagicHash = 0x48415348;
|
||||
|
||||
TableHeader(uint32_t data_len)
|
||||
: magic(MagicHash), version(1),
|
||||
hash_function(dwarf::DW_hash_function_djb), bucket_count(0),
|
||||
hashes_count(0), header_data_len(data_len) {}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void print(raw_ostream &O) {
|
||||
O << "Magic: " << format("0x%x", magic) << "\n"
|
||||
<< "Version: " << version << "\n"
|
||||
<< "Hash Function: " << hash_function << "\n"
|
||||
<< "Bucket Count: " << bucket_count << "\n"
|
||||
<< "Header Data Length: " << header_data_len << "\n";
|
||||
}
|
||||
void dump() { print(dbgs()); }
|
||||
#endif
|
||||
};
|
||||
|
||||
public:
|
||||
// The HeaderData describes the form of each set of data. In general this
|
||||
// is as a list of atoms (atom_count) where each atom contains a type
|
||||
// (AtomType type) of data, and an encoding form (form). In the case of
|
||||
// data that is referenced via DW_FORM_ref_* the die_offset_base is
|
||||
// used to describe the offset for all forms in the list of atoms.
|
||||
// This also serves as a public interface of sorts.
|
||||
// When written to disk this will have the form:
|
||||
//
|
||||
// uint32_t die_offset_base
|
||||
// uint32_t atom_count
|
||||
// atom_count Atoms
|
||||
|
||||
// Make these public so that they can be used as a general interface to
|
||||
// the class.
|
||||
struct Atom {
|
||||
uint16_t type; // enum AtomType
|
||||
uint16_t form; // DWARF DW_FORM_ defines
|
||||
|
||||
constexpr Atom(uint16_t type, uint16_t form) : type(type), form(form) {}
|
||||
#ifndef NDEBUG
|
||||
void print(raw_ostream &O) {
|
||||
O << "Type: " << dwarf::AtomTypeString(type) << "\n"
|
||||
<< "Form: " << dwarf::FormEncodingString(form) << "\n";
|
||||
}
|
||||
void dump() { print(dbgs()); }
|
||||
#endif
|
||||
};
|
||||
|
||||
private:
|
||||
struct TableHeaderData {
|
||||
uint32_t die_offset_base;
|
||||
SmallVector<Atom, 3> Atoms;
|
||||
|
||||
TableHeaderData(ArrayRef<Atom> AtomList, uint32_t offset = 0)
|
||||
: die_offset_base(offset), Atoms(AtomList.begin(), AtomList.end()) {}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void print(raw_ostream &O) {
|
||||
O << "die_offset_base: " << die_offset_base << "\n";
|
||||
for (size_t i = 0; i < Atoms.size(); i++)
|
||||
Atoms[i].print(O);
|
||||
}
|
||||
void dump() { print(dbgs()); }
|
||||
#endif
|
||||
};
|
||||
|
||||
// The data itself consists of a str_offset, a count of the DIEs in the
|
||||
// hash and the offsets to the DIEs themselves.
|
||||
// On disk each data section is ended with a 0 KeyType as the end of the
|
||||
// hash chain.
|
||||
// On output this looks like:
|
||||
// uint32_t str_offset
|
||||
// uint32_t hash_data_count
|
||||
// HashData[hash_data_count]
|
||||
public:
|
||||
struct HashDataContents {
|
||||
const DIE *Die; // Offsets
|
||||
char Flags; // Specific flags to output
|
||||
|
||||
HashDataContents(const DIE *D, char Flags) : Die(D), Flags(Flags) {}
|
||||
#ifndef NDEBUG
|
||||
void print(raw_ostream &O) const {
|
||||
O << " Offset: " << Die->getOffset() << "\n";
|
||||
O << " Tag: " << dwarf::TagString(Die->getTag()) << "\n";
|
||||
O << " Flags: " << Flags << "\n";
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
private:
|
||||
// String Data
|
||||
struct DataArray {
|
||||
DwarfStringPoolEntryRef Name;
|
||||
std::vector<HashDataContents *> Values;
|
||||
};
|
||||
friend struct HashData;
|
||||
struct HashData {
|
||||
StringRef Str;
|
||||
uint32_t HashValue;
|
||||
MCSymbol *Sym;
|
||||
DwarfAccelTable::DataArray &Data; // offsets
|
||||
HashData(StringRef S, DwarfAccelTable::DataArray &Data)
|
||||
: Str(S), Data(Data) {
|
||||
HashValue = DwarfAccelTable::HashDJB(S);
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
void print(raw_ostream &O) {
|
||||
O << "Name: " << Str << "\n";
|
||||
O << " Hash Value: " << format("0x%x", HashValue) << "\n";
|
||||
O << " Symbol: ";
|
||||
if (Sym)
|
||||
O << *Sym;
|
||||
else
|
||||
O << "<none>";
|
||||
O << "\n";
|
||||
for (HashDataContents *C : Data.Values) {
|
||||
O << " Offset: " << C->Die->getOffset() << "\n";
|
||||
O << " Tag: " << dwarf::TagString(C->Die->getTag()) << "\n";
|
||||
O << " Flags: " << C->Flags << "\n";
|
||||
}
|
||||
}
|
||||
void dump() { print(dbgs()); }
|
||||
#endif
|
||||
};
|
||||
|
||||
DwarfAccelTable(const DwarfAccelTable &) = delete;
|
||||
void operator=(const DwarfAccelTable &) = delete;
|
||||
|
||||
// Internal Functions
|
||||
void EmitHeader(AsmPrinter *);
|
||||
void EmitBuckets(AsmPrinter *);
|
||||
void EmitHashes(AsmPrinter *);
|
||||
void emitOffsets(AsmPrinter *, const MCSymbol *);
|
||||
void EmitData(AsmPrinter *, DwarfDebug *D);
|
||||
|
||||
// Allocator for HashData and HashDataContents.
|
||||
BumpPtrAllocator Allocator;
|
||||
|
||||
// Output Variables
|
||||
TableHeader Header;
|
||||
TableHeaderData HeaderData;
|
||||
std::vector<HashData *> Data;
|
||||
|
||||
typedef StringMap<DataArray, BumpPtrAllocator &> StringEntries;
|
||||
StringEntries Entries;
|
||||
|
||||
// Buckets/Hashes/Offsets
|
||||
typedef std::vector<HashData *> HashList;
|
||||
typedef std::vector<HashList> BucketList;
|
||||
BucketList Buckets;
|
||||
HashList Hashes;
|
||||
|
||||
// Public Implementation
|
||||
public:
|
||||
DwarfAccelTable(ArrayRef<DwarfAccelTable::Atom>);
|
||||
void AddName(DwarfStringPoolEntryRef Name, const DIE *Die, char Flags = 0);
|
||||
void FinalizeTable(AsmPrinter *, StringRef);
|
||||
void emit(AsmPrinter *, const MCSymbol *, DwarfDebug *);
|
||||
#ifndef NDEBUG
|
||||
void print(raw_ostream &O);
|
||||
void dump() { print(dbgs()); }
|
||||
#endif
|
||||
};
|
||||
}
|
||||
#endif
|
@ -1,758 +0,0 @@
|
||||
//===-- CoalesceBranches.cpp - Coalesce blocks with the same condition ---===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// Coalesce basic blocks guarded by the same branch condition into a single
|
||||
/// basic block.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ADT/BitVector.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/CodeGen/MachineDominators.h"
|
||||
#include "llvm/CodeGen/MachineFunctionPass.h"
|
||||
#include "llvm/CodeGen/MachinePostDominators.h"
|
||||
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
||||
#include "llvm/CodeGen/Passes.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Target/TargetFrameLowering.h"
|
||||
#include "llvm/Target/TargetInstrInfo.h"
|
||||
#include "llvm/Target/TargetSubtargetInfo.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "branch-coalescing"
|
||||
|
||||
static cl::opt<cl::boolOrDefault>
|
||||
EnableBranchCoalescing("enable-branch-coalesce", cl::Hidden,
|
||||
cl::desc("enable coalescing of duplicate branches"));
|
||||
|
||||
STATISTIC(NumBlocksCoalesced, "Number of blocks coalesced");
|
||||
STATISTIC(NumPHINotMoved, "Number of PHI Nodes that cannot be merged");
|
||||
STATISTIC(NumBlocksNotCoalesced, "Number of blocks not coalesced");
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// BranchCoalescing
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// Improve scheduling by coalescing branches that depend on the same condition.
|
||||
/// This pass looks for blocks that are guarded by the same branch condition
|
||||
/// and attempts to merge the blocks together. Such opportunities arise from
|
||||
/// the expansion of select statements in the IR.
|
||||
///
|
||||
/// For example, consider the following LLVM IR:
|
||||
///
|
||||
/// %test = icmp eq i32 %x 0
|
||||
/// %tmp1 = select i1 %test, double %a, double 2.000000e-03
|
||||
/// %tmp2 = select i1 %test, double %b, double 5.000000e-03
|
||||
///
|
||||
/// This IR expands to the following machine code on PowerPC:
|
||||
///
|
||||
/// BB#0: derived from LLVM BB %entry
|
||||
/// Live Ins: %F1 %F3 %X6
|
||||
/// <SNIP1>
|
||||
/// %vreg0<def> = COPY %F1; F8RC:%vreg0
|
||||
/// %vreg5<def> = CMPLWI %vreg4<kill>, 0; CRRC:%vreg5 GPRC:%vreg4
|
||||
/// %vreg8<def> = LXSDX %ZERO8, %vreg7<kill>, %RM<imp-use>;
|
||||
/// mem:LD8[ConstantPool] F8RC:%vreg8 G8RC:%vreg7
|
||||
/// BCC 76, %vreg5, <BB#2>; CRRC:%vreg5
|
||||
/// Successors according to CFG: BB#1(?%) BB#2(?%)
|
||||
///
|
||||
/// BB#1: derived from LLVM BB %entry
|
||||
/// Predecessors according to CFG: BB#0
|
||||
/// Successors according to CFG: BB#2(?%)
|
||||
///
|
||||
/// BB#2: derived from LLVM BB %entry
|
||||
/// Predecessors according to CFG: BB#0 BB#1
|
||||
/// %vreg9<def> = PHI %vreg8, <BB#1>, %vreg0, <BB#0>;
|
||||
/// F8RC:%vreg9,%vreg8,%vreg0
|
||||
/// <SNIP2>
|
||||
/// BCC 76, %vreg5, <BB#4>; CRRC:%vreg5
|
||||
/// Successors according to CFG: BB#3(?%) BB#4(?%)
|
||||
///
|
||||
/// BB#3: derived from LLVM BB %entry
|
||||
/// Predecessors according to CFG: BB#2
|
||||
/// Successors according to CFG: BB#4(?%)
|
||||
///
|
||||
/// BB#4: derived from LLVM BB %entry
|
||||
/// Predecessors according to CFG: BB#2 BB#3
|
||||
/// %vreg13<def> = PHI %vreg12, <BB#3>, %vreg2, <BB#2>;
|
||||
/// F8RC:%vreg13,%vreg12,%vreg2
|
||||
/// <SNIP3>
|
||||
/// BLR8 %LR8<imp-use>, %RM<imp-use>, %F1<imp-use>
|
||||
///
|
||||
/// When this pattern is detected, branch coalescing will try to collapse
|
||||
/// it by moving code in BB#2 to BB#0 and/or BB#4 and removing BB#3.
|
||||
///
|
||||
/// If all conditions are meet, IR should collapse to:
|
||||
///
|
||||
/// BB#0: derived from LLVM BB %entry
|
||||
/// Live Ins: %F1 %F3 %X6
|
||||
/// <SNIP1>
|
||||
/// %vreg0<def> = COPY %F1; F8RC:%vreg0
|
||||
/// %vreg5<def> = CMPLWI %vreg4<kill>, 0; CRRC:%vreg5 GPRC:%vreg4
|
||||
/// %vreg8<def> = LXSDX %ZERO8, %vreg7<kill>, %RM<imp-use>;
|
||||
/// mem:LD8[ConstantPool] F8RC:%vreg8 G8RC:%vreg7
|
||||
/// <SNIP2>
|
||||
/// BCC 76, %vreg5, <BB#4>; CRRC:%vreg5
|
||||
/// Successors according to CFG: BB#1(0x2aaaaaaa / 0x80000000 = 33.33%)
|
||||
/// BB#4(0x55555554 / 0x80000000 = 66.67%)
|
||||
///
|
||||
/// BB#1: derived from LLVM BB %entry
|
||||
/// Predecessors according to CFG: BB#0
|
||||
/// Successors according to CFG: BB#4(0x40000000 / 0x80000000 = 50.00%)
|
||||
///
|
||||
/// BB#4: derived from LLVM BB %entry
|
||||
/// Predecessors according to CFG: BB#0 BB#1
|
||||
/// %vreg9<def> = PHI %vreg8, <BB#1>, %vreg0, <BB#0>;
|
||||
/// F8RC:%vreg9,%vreg8,%vreg0
|
||||
/// %vreg13<def> = PHI %vreg12, <BB#1>, %vreg2, <BB#0>;
|
||||
/// F8RC:%vreg13,%vreg12,%vreg2
|
||||
/// <SNIP3>
|
||||
/// BLR8 %LR8<imp-use>, %RM<imp-use>, %F1<imp-use>
|
||||
///
|
||||
/// Branch Coalescing does not split blocks, it moves everything in the same
|
||||
/// direction ensuring it does not break use/definition semantics.
|
||||
///
|
||||
/// PHI nodes and its corresponding use instructions are moved to its successor
|
||||
/// block if there are no uses within the successor block PHI nodes. PHI
|
||||
/// node ordering cannot be assumed.
|
||||
///
|
||||
/// Non-PHI can be moved up to the predecessor basic block or down to the
|
||||
/// successor basic block following any PHI instructions. Whether it moves
|
||||
/// up or down depends on whether the register(s) defined in the instructions
|
||||
/// are used in current block or in any PHI instructions at the beginning of
|
||||
/// the successor block.
|
||||
|
||||
namespace {
|
||||
|
||||
class BranchCoalescing : public MachineFunctionPass {
|
||||
struct CoalescingCandidateInfo {
|
||||
MachineBasicBlock *BranchBlock; // Block containing the branch
|
||||
MachineBasicBlock *BranchTargetBlock; // Block branched to
|
||||
MachineBasicBlock *FallThroughBlock; // Fall-through if branch not taken
|
||||
SmallVector<MachineOperand, 4> Cond;
|
||||
bool MustMoveDown;
|
||||
bool MustMoveUp;
|
||||
|
||||
CoalescingCandidateInfo();
|
||||
void clear();
|
||||
};
|
||||
|
||||
MachineDominatorTree *MDT;
|
||||
MachinePostDominatorTree *MPDT;
|
||||
const TargetInstrInfo *TII;
|
||||
MachineRegisterInfo *MRI;
|
||||
|
||||
void initialize(MachineFunction &F);
|
||||
bool canCoalesceBranch(CoalescingCandidateInfo &Cand);
|
||||
bool identicalOperands(ArrayRef<MachineOperand> OperandList1,
|
||||
ArrayRef<MachineOperand> OperandList2) const;
|
||||
bool validateCandidates(CoalescingCandidateInfo &SourceRegion,
|
||||
CoalescingCandidateInfo &TargetRegion) const;
|
||||
|
||||
static bool isBranchCoalescingEnabled() {
|
||||
return EnableBranchCoalescing == cl::BOU_TRUE;
|
||||
}
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
BranchCoalescing() : MachineFunctionPass(ID) {
|
||||
initializeBranchCoalescingPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.addRequired<MachineDominatorTree>();
|
||||
AU.addRequired<MachinePostDominatorTree>();
|
||||
MachineFunctionPass::getAnalysisUsage(AU);
|
||||
}
|
||||
|
||||
StringRef getPassName() const override { return "Branch Coalescing"; }
|
||||
|
||||
bool mergeCandidates(CoalescingCandidateInfo &SourceRegion,
|
||||
CoalescingCandidateInfo &TargetRegion);
|
||||
bool canMoveToBeginning(const MachineInstr &MI,
|
||||
const MachineBasicBlock &MBB) const;
|
||||
bool canMoveToEnd(const MachineInstr &MI,
|
||||
const MachineBasicBlock &MBB) const;
|
||||
bool canMerge(CoalescingCandidateInfo &SourceRegion,
|
||||
CoalescingCandidateInfo &TargetRegion) const;
|
||||
void moveAndUpdatePHIs(MachineBasicBlock *SourceRegionMBB,
|
||||
MachineBasicBlock *TargetRegionMBB);
|
||||
bool runOnMachineFunction(MachineFunction &MF) override;
|
||||
};
|
||||
} // End anonymous namespace.
|
||||
|
||||
char BranchCoalescing::ID = 0;
|
||||
char &llvm::BranchCoalescingID = BranchCoalescing::ID;
|
||||
|
||||
INITIALIZE_PASS_BEGIN(BranchCoalescing, DEBUG_TYPE,
|
||||
"Branch Coalescing", false, false)
|
||||
INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree)
|
||||
INITIALIZE_PASS_DEPENDENCY(MachinePostDominatorTree)
|
||||
INITIALIZE_PASS_END(BranchCoalescing, DEBUG_TYPE, "Branch Coalescing",
|
||||
false, false)
|
||||
|
||||
BranchCoalescing::CoalescingCandidateInfo::CoalescingCandidateInfo()
|
||||
: BranchBlock(nullptr), BranchTargetBlock(nullptr),
|
||||
FallThroughBlock(nullptr), MustMoveDown(false), MustMoveUp(false) {}
|
||||
|
||||
void BranchCoalescing::CoalescingCandidateInfo::clear() {
|
||||
BranchBlock = nullptr;
|
||||
BranchTargetBlock = nullptr;
|
||||
FallThroughBlock = nullptr;
|
||||
Cond.clear();
|
||||
MustMoveDown = false;
|
||||
MustMoveUp = false;
|
||||
}
|
||||
|
||||
void BranchCoalescing::initialize(MachineFunction &MF) {
|
||||
MDT = &getAnalysis<MachineDominatorTree>();
|
||||
MPDT = &getAnalysis<MachinePostDominatorTree>();
|
||||
TII = MF.getSubtarget().getInstrInfo();
|
||||
MRI = &MF.getRegInfo();
|
||||
}
|
||||
|
||||
///
|
||||
/// Analyze the branch statement to determine if it can be coalesced. This
|
||||
/// method analyses the branch statement for the given candidate to determine
|
||||
/// if it can be coalesced. If the branch can be coalesced, then the
|
||||
/// BranchTargetBlock and the FallThroughBlock are recorded in the specified
|
||||
/// Candidate.
|
||||
///
|
||||
///\param[in,out] Cand The coalescing candidate to analyze
|
||||
///\return true if and only if the branch can be coalesced, false otherwise
|
||||
///
|
||||
bool BranchCoalescing::canCoalesceBranch(CoalescingCandidateInfo &Cand) {
|
||||
DEBUG(dbgs() << "Determine if branch block " << Cand.BranchBlock->getNumber()
|
||||
<< " can be coalesced:");
|
||||
MachineBasicBlock *FalseMBB = nullptr;
|
||||
|
||||
if (TII->analyzeBranch(*Cand.BranchBlock, Cand.BranchTargetBlock, FalseMBB,
|
||||
Cand.Cond)) {
|
||||
DEBUG(dbgs() << "TII unable to Analyze Branch - skip\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto &I : Cand.BranchBlock->terminators()) {
|
||||
DEBUG(dbgs() << "Looking at terminator : " << I << "\n");
|
||||
if (!I.isBranch())
|
||||
continue;
|
||||
|
||||
if (I.getNumOperands() != I.getNumExplicitOperands()) {
|
||||
DEBUG(dbgs() << "Terminator contains implicit operands - skip : " << I
|
||||
<< "\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Cand.BranchBlock->isEHPad() || Cand.BranchBlock->hasEHPadSuccessor()) {
|
||||
DEBUG(dbgs() << "EH Pad - skip\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// For now only consider triangles (i.e, BranchTargetBlock is set,
|
||||
// FalseMBB is null, and BranchTargetBlock is a successor to BranchBlock)
|
||||
if (!Cand.BranchTargetBlock || FalseMBB ||
|
||||
!Cand.BranchBlock->isSuccessor(Cand.BranchTargetBlock)) {
|
||||
DEBUG(dbgs() << "Does not form a triangle - skip\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure there are only two successors
|
||||
if (Cand.BranchBlock->succ_size() != 2) {
|
||||
DEBUG(dbgs() << "Does not have 2 successors - skip\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sanity check - the block must be able to fall through
|
||||
assert(Cand.BranchBlock->canFallThrough() &&
|
||||
"Expecting the block to fall through!");
|
||||
|
||||
// We have already ensured there are exactly two successors to
|
||||
// BranchBlock and that BranchTargetBlock is a successor to BranchBlock.
|
||||
// Ensure the single fall though block is empty.
|
||||
MachineBasicBlock *Succ =
|
||||
(*Cand.BranchBlock->succ_begin() == Cand.BranchTargetBlock)
|
||||
? *Cand.BranchBlock->succ_rbegin()
|
||||
: *Cand.BranchBlock->succ_begin();
|
||||
|
||||
assert(Succ && "Expecting a valid fall-through block\n");
|
||||
|
||||
if (!Succ->empty()) {
|
||||
DEBUG(dbgs() << "Fall-through block contains code -- skip\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Succ->isSuccessor(Cand.BranchTargetBlock)) {
|
||||
DEBUG(dbgs()
|
||||
<< "Successor of fall through block is not branch taken block\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
Cand.FallThroughBlock = Succ;
|
||||
DEBUG(dbgs() << "Valid Candidate\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
/// Determine if the two operand lists are identical
|
||||
///
|
||||
/// \param[in] OpList1 operand list
|
||||
/// \param[in] OpList2 operand list
|
||||
/// \return true if and only if the operands lists are identical
|
||||
///
|
||||
bool BranchCoalescing::identicalOperands(
|
||||
ArrayRef<MachineOperand> OpList1, ArrayRef<MachineOperand> OpList2) const {
|
||||
|
||||
if (OpList1.size() != OpList2.size()) {
|
||||
DEBUG(dbgs() << "Operand list is different size\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < OpList1.size(); ++i) {
|
||||
const MachineOperand &Op1 = OpList1[i];
|
||||
const MachineOperand &Op2 = OpList2[i];
|
||||
|
||||
DEBUG(dbgs() << "Op1: " << Op1 << "\n"
|
||||
<< "Op2: " << Op2 << "\n");
|
||||
|
||||
if (Op1.isIdenticalTo(Op2)) {
|
||||
DEBUG(dbgs() << "Op1 and Op2 are identical!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the operands are not identical, but are registers, check to see if the
|
||||
// definition of the register produces the same value. If they produce the
|
||||
// same value, consider them to be identical.
|
||||
if (Op1.isReg() && Op2.isReg() &&
|
||||
TargetRegisterInfo::isVirtualRegister(Op1.getReg()) &&
|
||||
TargetRegisterInfo::isVirtualRegister(Op2.getReg())) {
|
||||
MachineInstr *Op1Def = MRI->getVRegDef(Op1.getReg());
|
||||
MachineInstr *Op2Def = MRI->getVRegDef(Op2.getReg());
|
||||
if (TII->produceSameValue(*Op1Def, *Op2Def, MRI)) {
|
||||
DEBUG(dbgs() << "Op1Def: " << *Op1Def << " and " << *Op2Def
|
||||
<< " produce the same value!\n");
|
||||
} else {
|
||||
DEBUG(dbgs() << "Operands produce different values\n");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
DEBUG(dbgs() << "The operands are not provably identical.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
/// Moves ALL PHI instructions in SourceMBB to beginning of TargetMBB
|
||||
/// and update them to refer to the new block. PHI node ordering
|
||||
/// cannot be assumed so it does not matter where the PHI instructions
|
||||
/// are moved to in TargetMBB.
|
||||
///
|
||||
/// \param[in] SourceMBB block to move PHI instructions from
|
||||
/// \param[in] TargetMBB block to move PHI instructions to
|
||||
///
|
||||
void BranchCoalescing::moveAndUpdatePHIs(MachineBasicBlock *SourceMBB,
|
||||
MachineBasicBlock *TargetMBB) {
|
||||
|
||||
MachineBasicBlock::iterator MI = SourceMBB->begin();
|
||||
MachineBasicBlock::iterator ME = SourceMBB->getFirstNonPHI();
|
||||
|
||||
if (MI == ME) {
|
||||
DEBUG(dbgs() << "SourceMBB contains no PHI instructions.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update all PHI instructions in SourceMBB and move to top of TargetMBB
|
||||
for (MachineBasicBlock::iterator Iter = MI; Iter != ME; Iter++) {
|
||||
MachineInstr &PHIInst = *Iter;
|
||||
for (unsigned i = 2, e = PHIInst.getNumOperands() + 1; i != e; i += 2) {
|
||||
MachineOperand &MO = PHIInst.getOperand(i);
|
||||
if (MO.getMBB() == SourceMBB)
|
||||
MO.setMBB(TargetMBB);
|
||||
}
|
||||
}
|
||||
TargetMBB->splice(TargetMBB->begin(), SourceMBB, MI, ME);
|
||||
}
|
||||
|
||||
///
|
||||
/// This function checks if MI can be moved to the beginning of the TargetMBB
|
||||
/// following PHI instructions. A MI instruction can be moved to beginning of
|
||||
/// the TargetMBB if there are no uses of it within the TargetMBB PHI nodes.
|
||||
///
|
||||
/// \param[in] MI the machine instruction to move.
|
||||
/// \param[in] TargetMBB the machine basic block to move to
|
||||
/// \return true if it is safe to move MI to beginning of TargetMBB,
|
||||
/// false otherwise.
|
||||
///
|
||||
bool BranchCoalescing::canMoveToBeginning(const MachineInstr &MI,
|
||||
const MachineBasicBlock &TargetMBB
|
||||
) const {
|
||||
|
||||
DEBUG(dbgs() << "Checking if " << MI << " can move to beginning of "
|
||||
<< TargetMBB.getNumber() << "\n");
|
||||
|
||||
for (auto &Def : MI.defs()) { // Looking at Def
|
||||
for (auto &Use : MRI->use_instructions(Def.getReg())) {
|
||||
if (Use.isPHI() && Use.getParent() == &TargetMBB) {
|
||||
DEBUG(dbgs() << " *** used in a PHI -- cannot move ***\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG(dbgs() << " Safe to move to the beginning.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
/// This function checks if MI can be moved to the end of the TargetMBB,
|
||||
/// immediately before the first terminator. A MI instruction can be moved
|
||||
/// to then end of the TargetMBB if no PHI node defines what MI uses within
|
||||
/// it's own MBB.
|
||||
///
|
||||
/// \param[in] MI the machine instruction to move.
|
||||
/// \param[in] TargetMBB the machine basic block to move to
|
||||
/// \return true if it is safe to move MI to end of TargetMBB,
|
||||
/// false otherwise.
|
||||
///
|
||||
bool BranchCoalescing::canMoveToEnd(const MachineInstr &MI,
|
||||
const MachineBasicBlock &TargetMBB
|
||||
) const {
|
||||
|
||||
DEBUG(dbgs() << "Checking if " << MI << " can move to end of "
|
||||
<< TargetMBB.getNumber() << "\n");
|
||||
|
||||
for (auto &Use : MI.uses()) {
|
||||
if (Use.isReg() && TargetRegisterInfo::isVirtualRegister(Use.getReg())) {
|
||||
MachineInstr *DefInst = MRI->getVRegDef(Use.getReg());
|
||||
if (DefInst->isPHI() && DefInst->getParent() == MI.getParent()) {
|
||||
DEBUG(dbgs() << " *** Cannot move this instruction ***\n");
|
||||
return false;
|
||||
} else {
|
||||
DEBUG(dbgs() << " *** def is in another block -- safe to move!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG(dbgs() << " Safe to move to the end.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
/// This method checks to ensure the two coalescing candidates follows the
|
||||
/// expected pattern required for coalescing.
|
||||
///
|
||||
/// \param[in] SourceRegion The candidate to move statements from
|
||||
/// \param[in] TargetRegion The candidate to move statements to
|
||||
/// \return true if all instructions in SourceRegion.BranchBlock can be merged
|
||||
/// into a block in TargetRegion; false otherwise.
|
||||
///
|
||||
bool BranchCoalescing::validateCandidates(
|
||||
CoalescingCandidateInfo &SourceRegion,
|
||||
CoalescingCandidateInfo &TargetRegion) const {
|
||||
|
||||
if (TargetRegion.BranchTargetBlock != SourceRegion.BranchBlock)
|
||||
llvm_unreachable("Expecting SourceRegion to immediately follow TargetRegion");
|
||||
else if (!MDT->dominates(TargetRegion.BranchBlock, SourceRegion.BranchBlock))
|
||||
llvm_unreachable("Expecting TargetRegion to dominate SourceRegion");
|
||||
else if (!MPDT->dominates(SourceRegion.BranchBlock, TargetRegion.BranchBlock))
|
||||
llvm_unreachable("Expecting SourceRegion to post-dominate TargetRegion");
|
||||
else if (!TargetRegion.FallThroughBlock->empty() ||
|
||||
!SourceRegion.FallThroughBlock->empty())
|
||||
llvm_unreachable("Expecting fall-through blocks to be empty");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
/// This method determines whether the two coalescing candidates can be merged.
|
||||
/// In order to be merged, all instructions must be able to
|
||||
/// 1. Move to the beginning of the SourceRegion.BranchTargetBlock;
|
||||
/// 2. Move to the end of the TargetRegion.BranchBlock.
|
||||
/// Merging involves moving the instructions in the
|
||||
/// TargetRegion.BranchTargetBlock (also SourceRegion.BranchBlock).
|
||||
///
|
||||
/// This function first try to move instructions from the
|
||||
/// TargetRegion.BranchTargetBlock down, to the beginning of the
|
||||
/// SourceRegion.BranchTargetBlock. This is not possible if any register defined
|
||||
/// in TargetRegion.BranchTargetBlock is used in a PHI node in the
|
||||
/// SourceRegion.BranchTargetBlock. In this case, check whether the statement
|
||||
/// can be moved up, to the end of the TargetRegion.BranchBlock (immediately
|
||||
/// before the branch statement). If it cannot move, then these blocks cannot
|
||||
/// be merged.
|
||||
///
|
||||
/// Note that there is no analysis for moving instructions past the fall-through
|
||||
/// blocks because they are confirmed to be empty. An assert is thrown if they
|
||||
/// are not.
|
||||
///
|
||||
/// \param[in] SourceRegion The candidate to move statements from
|
||||
/// \param[in] TargetRegion The candidate to move statements to
|
||||
/// \return true if all instructions in SourceRegion.BranchBlock can be merged
|
||||
/// into a block in TargetRegion, false otherwise.
|
||||
///
|
||||
bool BranchCoalescing::canMerge(CoalescingCandidateInfo &SourceRegion,
|
||||
CoalescingCandidateInfo &TargetRegion) const {
|
||||
if (!validateCandidates(SourceRegion, TargetRegion))
|
||||
return false;
|
||||
|
||||
// Walk through PHI nodes first and see if they force the merge into the
|
||||
// SourceRegion.BranchTargetBlock.
|
||||
for (MachineBasicBlock::iterator
|
||||
I = SourceRegion.BranchBlock->instr_begin(),
|
||||
E = SourceRegion.BranchBlock->getFirstNonPHI();
|
||||
I != E; ++I) {
|
||||
for (auto &Def : I->defs())
|
||||
for (auto &Use : MRI->use_instructions(Def.getReg())) {
|
||||
if (Use.isPHI() && Use.getParent() == SourceRegion.BranchTargetBlock) {
|
||||
DEBUG(dbgs() << "PHI " << *I << " defines register used in another "
|
||||
"PHI within branch target block -- can't merge\n");
|
||||
NumPHINotMoved++;
|
||||
return false;
|
||||
}
|
||||
if (Use.getParent() == SourceRegion.BranchBlock) {
|
||||
DEBUG(dbgs() << "PHI " << *I
|
||||
<< " defines register used in this "
|
||||
"block -- all must move down\n");
|
||||
SourceRegion.MustMoveDown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Walk through the MI to see if they should be merged into
|
||||
// TargetRegion.BranchBlock (up) or SourceRegion.BranchTargetBlock (down)
|
||||
for (MachineBasicBlock::iterator
|
||||
I = SourceRegion.BranchBlock->getFirstNonPHI(),
|
||||
E = SourceRegion.BranchBlock->end();
|
||||
I != E; ++I) {
|
||||
if (!canMoveToBeginning(*I, *SourceRegion.BranchTargetBlock)) {
|
||||
DEBUG(dbgs() << "Instruction " << *I
|
||||
<< " cannot move down - must move up!\n");
|
||||
SourceRegion.MustMoveUp = true;
|
||||
}
|
||||
if (!canMoveToEnd(*I, *TargetRegion.BranchBlock)) {
|
||||
DEBUG(dbgs() << "Instruction " << *I
|
||||
<< " cannot move up - must move down!\n");
|
||||
SourceRegion.MustMoveDown = true;
|
||||
}
|
||||
}
|
||||
|
||||
return (SourceRegion.MustMoveUp && SourceRegion.MustMoveDown) ? false : true;
|
||||
}
|
||||
|
||||
/// Merge the instructions from SourceRegion.BranchBlock,
|
||||
/// SourceRegion.BranchTargetBlock, and SourceRegion.FallThroughBlock into
|
||||
/// TargetRegion.BranchBlock, TargetRegion.BranchTargetBlock and
|
||||
/// TargetRegion.FallThroughBlock respectively.
|
||||
///
|
||||
/// The successors for blocks in TargetRegion will be updated to use the
|
||||
/// successors from blocks in SourceRegion. Finally, the blocks in SourceRegion
|
||||
/// will be removed from the function.
|
||||
///
|
||||
/// A region consists of a BranchBlock, a FallThroughBlock, and a
|
||||
/// BranchTargetBlock. Branch coalesce works on patterns where the
|
||||
/// TargetRegion's BranchTargetBlock must also be the SourceRegions's
|
||||
/// BranchBlock.
|
||||
///
|
||||
/// Before mergeCandidates:
|
||||
///
|
||||
/// +---------------------------+
|
||||
/// | TargetRegion.BranchBlock |
|
||||
/// +---------------------------+
|
||||
/// / |
|
||||
/// / +--------------------------------+
|
||||
/// | | TargetRegion.FallThroughBlock |
|
||||
/// \ +--------------------------------+
|
||||
/// \ |
|
||||
/// +----------------------------------+
|
||||
/// | TargetRegion.BranchTargetBlock |
|
||||
/// | SourceRegion.BranchBlock |
|
||||
/// +----------------------------------+
|
||||
/// / |
|
||||
/// / +--------------------------------+
|
||||
/// | | SourceRegion.FallThroughBlock |
|
||||
/// \ +--------------------------------+
|
||||
/// \ |
|
||||
/// +----------------------------------+
|
||||
/// | SourceRegion.BranchTargetBlock |
|
||||
/// +----------------------------------+
|
||||
///
|
||||
/// After mergeCandidates:
|
||||
///
|
||||
/// +-----------------------------+
|
||||
/// | TargetRegion.BranchBlock |
|
||||
/// | SourceRegion.BranchBlock |
|
||||
/// +-----------------------------+
|
||||
/// / |
|
||||
/// / +---------------------------------+
|
||||
/// | | TargetRegion.FallThroughBlock |
|
||||
/// | | SourceRegion.FallThroughBlock |
|
||||
/// \ +---------------------------------+
|
||||
/// \ |
|
||||
/// +----------------------------------+
|
||||
/// | SourceRegion.BranchTargetBlock |
|
||||
/// +----------------------------------+
|
||||
///
|
||||
/// \param[in] SourceRegion The candidate to move blocks from
|
||||
/// \param[in] TargetRegion The candidate to move blocks to
|
||||
///
|
||||
bool BranchCoalescing::mergeCandidates(CoalescingCandidateInfo &SourceRegion,
|
||||
CoalescingCandidateInfo &TargetRegion) {
|
||||
|
||||
if (SourceRegion.MustMoveUp && SourceRegion.MustMoveDown) {
|
||||
llvm_unreachable("Cannot have both MustMoveDown and MustMoveUp set!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!validateCandidates(SourceRegion, TargetRegion))
|
||||
return false;
|
||||
|
||||
// Start the merging process by first handling the BranchBlock.
|
||||
// Move any PHIs in SourceRegion.BranchBlock down to the branch-taken block
|
||||
moveAndUpdatePHIs(SourceRegion.BranchBlock, SourceRegion.BranchTargetBlock);
|
||||
|
||||
// Move remaining instructions in SourceRegion.BranchBlock into
|
||||
// TargetRegion.BranchBlock
|
||||
MachineBasicBlock::iterator firstInstr =
|
||||
SourceRegion.BranchBlock->getFirstNonPHI();
|
||||
MachineBasicBlock::iterator lastInstr =
|
||||
SourceRegion.BranchBlock->getFirstTerminator();
|
||||
|
||||
MachineBasicBlock *Source = SourceRegion.MustMoveDown
|
||||
? SourceRegion.BranchTargetBlock
|
||||
: TargetRegion.BranchBlock;
|
||||
|
||||
MachineBasicBlock::iterator Target =
|
||||
SourceRegion.MustMoveDown
|
||||
? SourceRegion.BranchTargetBlock->getFirstNonPHI()
|
||||
: TargetRegion.BranchBlock->getFirstTerminator();
|
||||
|
||||
Source->splice(Target, SourceRegion.BranchBlock, firstInstr, lastInstr);
|
||||
|
||||
// Once PHI and instructions have been moved we need to clean up the
|
||||
// control flow.
|
||||
|
||||
// Remove SourceRegion.FallThroughBlock before transferring successors of
|
||||
// SourceRegion.BranchBlock to TargetRegion.BranchBlock.
|
||||
SourceRegion.BranchBlock->removeSuccessor(SourceRegion.FallThroughBlock);
|
||||
TargetRegion.BranchBlock->transferSuccessorsAndUpdatePHIs(
|
||||
SourceRegion.BranchBlock);
|
||||
// Update branch in TargetRegion.BranchBlock to jump to
|
||||
// SourceRegion.BranchTargetBlock
|
||||
// In this case, TargetRegion.BranchTargetBlock == SourceRegion.BranchBlock.
|
||||
TargetRegion.BranchBlock->ReplaceUsesOfBlockWith(
|
||||
SourceRegion.BranchBlock, SourceRegion.BranchTargetBlock);
|
||||
// Remove the branch statement(s) in SourceRegion.BranchBlock
|
||||
MachineBasicBlock::iterator I =
|
||||
SourceRegion.BranchBlock->terminators().begin();
|
||||
while (I != SourceRegion.BranchBlock->terminators().end()) {
|
||||
MachineInstr &CurrInst = *I;
|
||||
++I;
|
||||
if (CurrInst.isBranch())
|
||||
CurrInst.eraseFromParent();
|
||||
}
|
||||
|
||||
// Fall-through block should be empty since this is part of the condition
|
||||
// to coalesce the branches.
|
||||
assert(TargetRegion.FallThroughBlock->empty() &&
|
||||
"FallThroughBlocks should be empty!");
|
||||
|
||||
// Transfer successor information and move PHIs down to the
|
||||
// branch-taken block.
|
||||
TargetRegion.FallThroughBlock->transferSuccessorsAndUpdatePHIs(
|
||||
SourceRegion.FallThroughBlock);
|
||||
TargetRegion.FallThroughBlock->removeSuccessor(SourceRegion.BranchBlock);
|
||||
|
||||
// Remove the blocks from the function.
|
||||
assert(SourceRegion.BranchBlock->empty() &&
|
||||
"Expecting branch block to be empty!");
|
||||
SourceRegion.BranchBlock->eraseFromParent();
|
||||
|
||||
assert(SourceRegion.FallThroughBlock->empty() &&
|
||||
"Expecting fall-through block to be empty!\n");
|
||||
SourceRegion.FallThroughBlock->eraseFromParent();
|
||||
|
||||
NumBlocksCoalesced++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BranchCoalescing::runOnMachineFunction(MachineFunction &MF) {
|
||||
|
||||
if (skipFunction(*MF.getFunction()) || MF.empty() ||
|
||||
!isBranchCoalescingEnabled())
|
||||
return false;
|
||||
|
||||
bool didSomething = false;
|
||||
|
||||
DEBUG(dbgs() << "******** Branch Coalescing ********\n");
|
||||
initialize(MF);
|
||||
|
||||
DEBUG(dbgs() << "Function: "; MF.dump(); dbgs() << "\n");
|
||||
|
||||
CoalescingCandidateInfo Cand1, Cand2;
|
||||
// Walk over blocks and find candidates to merge
|
||||
// Continue trying to merge with the first candidate found, as long as merging
|
||||
// is successfull.
|
||||
for (MachineBasicBlock &MBB : MF) {
|
||||
bool MergedCandidates = false;
|
||||
do {
|
||||
MergedCandidates = false;
|
||||
Cand1.clear();
|
||||
Cand2.clear();
|
||||
|
||||
Cand1.BranchBlock = &MBB;
|
||||
|
||||
// If unable to coalesce the branch, then continue to next block
|
||||
if (!canCoalesceBranch(Cand1))
|
||||
break;
|
||||
|
||||
Cand2.BranchBlock = Cand1.BranchTargetBlock;
|
||||
if (!canCoalesceBranch(Cand2))
|
||||
break;
|
||||
|
||||
// Sanity check
|
||||
// The branch-taken block of the second candidate should post-dominate the
|
||||
// first candidate
|
||||
assert(MPDT->dominates(Cand2.BranchTargetBlock, Cand1.BranchBlock) &&
|
||||
"Branch-taken block should post-dominate first candidate");
|
||||
|
||||
if (!identicalOperands(Cand1.Cond, Cand2.Cond)) {
|
||||
DEBUG(dbgs() << "Blocks " << Cand1.BranchBlock->getNumber() << " and "
|
||||
<< Cand2.BranchBlock->getNumber()
|
||||
<< " have different branches\n");
|
||||
break;
|
||||
}
|
||||
if (!canMerge(Cand2, Cand1)) {
|
||||
DEBUG(dbgs() << "Cannot merge blocks " << Cand1.BranchBlock->getNumber()
|
||||
<< " and " << Cand2.BranchBlock->getNumber() << "\n");
|
||||
NumBlocksNotCoalesced++;
|
||||
continue;
|
||||
}
|
||||
DEBUG(dbgs() << "Merging blocks " << Cand1.BranchBlock->getNumber()
|
||||
<< " and " << Cand1.BranchTargetBlock->getNumber() << "\n");
|
||||
MergedCandidates = mergeCandidates(Cand2, Cand1);
|
||||
if (MergedCandidates)
|
||||
didSomething = true;
|
||||
|
||||
DEBUG(dbgs() << "Function after merging: "; MF.dump(); dbgs() << "\n");
|
||||
} while (MergedCandidates);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Verify MF is still valid after branch coalescing
|
||||
if (didSomething)
|
||||
MF.verify(nullptr, "Error in code produced by branch coalescing");
|
||||
#endif // NDEBUG
|
||||
|
||||
DEBUG(dbgs() << "Finished Branch Coalescing\n");
|
||||
return didSomething;
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
//===- CountingFunctionInserter.cpp - Insert mcount-like function calls ---===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Insert calls to counter functions, such as mcount, intended to be called
|
||||
// once per function, at the beginning of each function.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Analysis/GlobalsModRef.h"
|
||||
#include "llvm/CodeGen/Passes.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Type.h"
|
||||
#include "llvm/Pass.h"
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
struct CountingFunctionInserter : public FunctionPass {
|
||||
static char ID; // Pass identification, replacement for typeid
|
||||
CountingFunctionInserter() : FunctionPass(ID) {
|
||||
initializeCountingFunctionInserterPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.addPreserved<GlobalsAAWrapperPass>();
|
||||
}
|
||||
|
||||
bool runOnFunction(Function &F) override {
|
||||
std::string CountingFunctionName =
|
||||
F.getFnAttribute("counting-function").getValueAsString();
|
||||
if (CountingFunctionName.empty())
|
||||
return false;
|
||||
|
||||
Type *VoidTy = Type::getVoidTy(F.getContext());
|
||||
Constant *CountingFn =
|
||||
F.getParent()->getOrInsertFunction(CountingFunctionName,
|
||||
VoidTy);
|
||||
CallInst::Create(CountingFn, "", &*F.begin()->getFirstInsertionPt());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
char CountingFunctionInserter::ID = 0;
|
||||
}
|
||||
|
||||
INITIALIZE_PASS(CountingFunctionInserter, "cfinserter",
|
||||
"Inserts calls to mcount-like functions", false, false)
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// CountingFunctionInserter - Give any unnamed non-void instructions "tmp" names.
|
||||
//
|
||||
FunctionPass *llvm::createCountingFunctionInserterPass() {
|
||||
return new CountingFunctionInserter();
|
||||
}
|
@ -1,755 +0,0 @@
|
||||
//===- ExecutionDepsFix.cpp - Fix execution dependecy issues ----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/CodeGen/ExecutionDepsFix.h"
|
||||
|
||||
#include "llvm/ADT/PostOrderIterator.h"
|
||||
#include "llvm/ADT/iterator_range.h"
|
||||
#include "llvm/CodeGen/LivePhysRegs.h"
|
||||
#include "llvm/CodeGen/MachineFunctionPass.h"
|
||||
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
||||
#include "llvm/CodeGen/RegisterClassInfo.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Target/TargetInstrInfo.h"
|
||||
#include "llvm/Target/TargetSubtargetInfo.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "execution-deps-fix"
|
||||
|
||||
/// Translate TRI register number to a list of indices into our smaller tables
|
||||
/// of interesting registers.
|
||||
iterator_range<SmallVectorImpl<int>::const_iterator>
|
||||
ExecutionDepsFix::regIndices(unsigned Reg) const {
|
||||
assert(Reg < AliasMap.size() && "Invalid register");
|
||||
const auto &Entry = AliasMap[Reg];
|
||||
return make_range(Entry.begin(), Entry.end());
|
||||
}
|
||||
|
||||
DomainValue *ExecutionDepsFix::alloc(int domain) {
|
||||
DomainValue *dv = Avail.empty() ?
|
||||
new(Allocator.Allocate()) DomainValue :
|
||||
Avail.pop_back_val();
|
||||
if (domain >= 0)
|
||||
dv->addDomain(domain);
|
||||
assert(dv->Refs == 0 && "Reference count wasn't cleared");
|
||||
assert(!dv->Next && "Chained DomainValue shouldn't have been recycled");
|
||||
return dv;
|
||||
}
|
||||
|
||||
/// Release a reference to DV. When the last reference is released,
|
||||
/// collapse if needed.
|
||||
void ExecutionDepsFix::release(DomainValue *DV) {
|
||||
while (DV) {
|
||||
assert(DV->Refs && "Bad DomainValue");
|
||||
if (--DV->Refs)
|
||||
return;
|
||||
|
||||
// There are no more DV references. Collapse any contained instructions.
|
||||
if (DV->AvailableDomains && !DV->isCollapsed())
|
||||
collapse(DV, DV->getFirstDomain());
|
||||
|
||||
DomainValue *Next = DV->Next;
|
||||
DV->clear();
|
||||
Avail.push_back(DV);
|
||||
// Also release the next DomainValue in the chain.
|
||||
DV = Next;
|
||||
}
|
||||
}
|
||||
|
||||
/// Follow the chain of dead DomainValues until a live DomainValue is reached.
|
||||
/// Update the referenced pointer when necessary.
|
||||
DomainValue *ExecutionDepsFix::resolve(DomainValue *&DVRef) {
|
||||
DomainValue *DV = DVRef;
|
||||
if (!DV || !DV->Next)
|
||||
return DV;
|
||||
|
||||
// DV has a chain. Find the end.
|
||||
do DV = DV->Next;
|
||||
while (DV->Next);
|
||||
|
||||
// Update DVRef to point to DV.
|
||||
retain(DV);
|
||||
release(DVRef);
|
||||
DVRef = DV;
|
||||
return DV;
|
||||
}
|
||||
|
||||
/// Set LiveRegs[rx] = dv, updating reference counts.
|
||||
void ExecutionDepsFix::setLiveReg(int rx, DomainValue *dv) {
|
||||
assert(unsigned(rx) < NumRegs && "Invalid index");
|
||||
assert(LiveRegs && "Must enter basic block first.");
|
||||
|
||||
if (LiveRegs[rx].Value == dv)
|
||||
return;
|
||||
if (LiveRegs[rx].Value)
|
||||
release(LiveRegs[rx].Value);
|
||||
LiveRegs[rx].Value = retain(dv);
|
||||
}
|
||||
|
||||
// Kill register rx, recycle or collapse any DomainValue.
|
||||
void ExecutionDepsFix::kill(int rx) {
|
||||
assert(unsigned(rx) < NumRegs && "Invalid index");
|
||||
assert(LiveRegs && "Must enter basic block first.");
|
||||
if (!LiveRegs[rx].Value)
|
||||
return;
|
||||
|
||||
release(LiveRegs[rx].Value);
|
||||
LiveRegs[rx].Value = nullptr;
|
||||
}
|
||||
|
||||
/// Force register rx into domain.
|
||||
void ExecutionDepsFix::force(int rx, unsigned domain) {
|
||||
assert(unsigned(rx) < NumRegs && "Invalid index");
|
||||
assert(LiveRegs && "Must enter basic block first.");
|
||||
if (DomainValue *dv = LiveRegs[rx].Value) {
|
||||
if (dv->isCollapsed())
|
||||
dv->addDomain(domain);
|
||||
else if (dv->hasDomain(domain))
|
||||
collapse(dv, domain);
|
||||
else {
|
||||
// This is an incompatible open DomainValue. Collapse it to whatever and
|
||||
// force the new value into domain. This costs a domain crossing.
|
||||
collapse(dv, dv->getFirstDomain());
|
||||
assert(LiveRegs[rx].Value && "Not live after collapse?");
|
||||
LiveRegs[rx].Value->addDomain(domain);
|
||||
}
|
||||
} else {
|
||||
// Set up basic collapsed DomainValue.
|
||||
setLiveReg(rx, alloc(domain));
|
||||
}
|
||||
}
|
||||
|
||||
/// Collapse open DomainValue into given domain. If there are multiple
|
||||
/// registers using dv, they each get a unique collapsed DomainValue.
|
||||
void ExecutionDepsFix::collapse(DomainValue *dv, unsigned domain) {
|
||||
assert(dv->hasDomain(domain) && "Cannot collapse");
|
||||
|
||||
// Collapse all the instructions.
|
||||
while (!dv->Instrs.empty())
|
||||
TII->setExecutionDomain(*dv->Instrs.pop_back_val(), domain);
|
||||
dv->setSingleDomain(domain);
|
||||
|
||||
// If there are multiple users, give them new, unique DomainValues.
|
||||
if (LiveRegs && dv->Refs > 1)
|
||||
for (unsigned rx = 0; rx != NumRegs; ++rx)
|
||||
if (LiveRegs[rx].Value == dv)
|
||||
setLiveReg(rx, alloc(domain));
|
||||
}
|
||||
|
||||
/// All instructions and registers in B are moved to A, and B is released.
|
||||
bool ExecutionDepsFix::merge(DomainValue *A, DomainValue *B) {
|
||||
assert(!A->isCollapsed() && "Cannot merge into collapsed");
|
||||
assert(!B->isCollapsed() && "Cannot merge from collapsed");
|
||||
if (A == B)
|
||||
return true;
|
||||
// Restrict to the domains that A and B have in common.
|
||||
unsigned common = A->getCommonDomains(B->AvailableDomains);
|
||||
if (!common)
|
||||
return false;
|
||||
A->AvailableDomains = common;
|
||||
A->Instrs.append(B->Instrs.begin(), B->Instrs.end());
|
||||
|
||||
// Clear the old DomainValue so we won't try to swizzle instructions twice.
|
||||
B->clear();
|
||||
// All uses of B are referred to A.
|
||||
B->Next = retain(A);
|
||||
|
||||
for (unsigned rx = 0; rx != NumRegs; ++rx) {
|
||||
assert(LiveRegs && "no space allocated for live registers");
|
||||
if (LiveRegs[rx].Value == B)
|
||||
setLiveReg(rx, A);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Set up LiveRegs by merging predecessor live-out values.
|
||||
void ExecutionDepsFix::enterBasicBlock(MachineBasicBlock *MBB) {
|
||||
// Reset instruction counter in each basic block.
|
||||
CurInstr = 0;
|
||||
|
||||
// Set up UndefReads to track undefined register reads.
|
||||
UndefReads.clear();
|
||||
LiveRegSet.clear();
|
||||
|
||||
// Set up LiveRegs to represent registers entering MBB.
|
||||
if (!LiveRegs)
|
||||
LiveRegs = new LiveReg[NumRegs];
|
||||
|
||||
// Default values are 'nothing happened a long time ago'.
|
||||
for (unsigned rx = 0; rx != NumRegs; ++rx) {
|
||||
LiveRegs[rx].Value = nullptr;
|
||||
LiveRegs[rx].Def = -(1 << 20);
|
||||
}
|
||||
|
||||
// This is the entry block.
|
||||
if (MBB->pred_empty()) {
|
||||
for (const auto &LI : MBB->liveins()) {
|
||||
for (int rx : regIndices(LI.PhysReg)) {
|
||||
// Treat function live-ins as if they were defined just before the first
|
||||
// instruction. Usually, function arguments are set up immediately
|
||||
// before the call.
|
||||
LiveRegs[rx].Def = -1;
|
||||
}
|
||||
}
|
||||
DEBUG(dbgs() << "BB#" << MBB->getNumber() << ": entry\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to coalesce live-out registers from predecessors.
|
||||
for (MachineBasicBlock::const_pred_iterator pi = MBB->pred_begin(),
|
||||
pe = MBB->pred_end(); pi != pe; ++pi) {
|
||||
auto fi = MBBInfos.find(*pi);
|
||||
assert(fi != MBBInfos.end() &&
|
||||
"Should have pre-allocated MBBInfos for all MBBs");
|
||||
LiveReg *Incoming = fi->second.OutRegs;
|
||||
// Incoming is null if this is a backedge from a BB
|
||||
// we haven't processed yet
|
||||
if (Incoming == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (unsigned rx = 0; rx != NumRegs; ++rx) {
|
||||
// Use the most recent predecessor def for each register.
|
||||
LiveRegs[rx].Def = std::max(LiveRegs[rx].Def, Incoming[rx].Def);
|
||||
|
||||
DomainValue *pdv = resolve(Incoming[rx].Value);
|
||||
if (!pdv)
|
||||
continue;
|
||||
if (!LiveRegs[rx].Value) {
|
||||
setLiveReg(rx, pdv);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have a live DomainValue from more than one predecessor.
|
||||
if (LiveRegs[rx].Value->isCollapsed()) {
|
||||
// We are already collapsed, but predecessor is not. Force it.
|
||||
unsigned Domain = LiveRegs[rx].Value->getFirstDomain();
|
||||
if (!pdv->isCollapsed() && pdv->hasDomain(Domain))
|
||||
collapse(pdv, Domain);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Currently open, merge in predecessor.
|
||||
if (!pdv->isCollapsed())
|
||||
merge(LiveRegs[rx].Value, pdv);
|
||||
else
|
||||
force(rx, pdv->getFirstDomain());
|
||||
}
|
||||
}
|
||||
DEBUG(
|
||||
dbgs() << "BB#" << MBB->getNumber()
|
||||
<< (!isBlockDone(MBB) ? ": incomplete\n" : ": all preds known\n"));
|
||||
}
|
||||
|
||||
void ExecutionDepsFix::leaveBasicBlock(MachineBasicBlock *MBB) {
|
||||
assert(LiveRegs && "Must enter basic block first.");
|
||||
LiveReg *OldOutRegs = MBBInfos[MBB].OutRegs;
|
||||
// Save register clearances at end of MBB - used by enterBasicBlock().
|
||||
MBBInfos[MBB].OutRegs = LiveRegs;
|
||||
|
||||
// While processing the basic block, we kept `Def` relative to the start
|
||||
// of the basic block for convenience. However, future use of this information
|
||||
// only cares about the clearance from the end of the block, so adjust
|
||||
// everything to be relative to the end of the basic block.
|
||||
for (unsigned i = 0, e = NumRegs; i != e; ++i)
|
||||
LiveRegs[i].Def -= CurInstr;
|
||||
if (OldOutRegs) {
|
||||
// This must be the second pass.
|
||||
// Release all the DomainValues instead of keeping them.
|
||||
for (unsigned i = 0, e = NumRegs; i != e; ++i)
|
||||
release(OldOutRegs[i].Value);
|
||||
delete[] OldOutRegs;
|
||||
}
|
||||
LiveRegs = nullptr;
|
||||
}
|
||||
|
||||
bool ExecutionDepsFix::visitInstr(MachineInstr *MI) {
|
||||
// Update instructions with explicit execution domains.
|
||||
std::pair<uint16_t, uint16_t> DomP = TII->getExecutionDomain(*MI);
|
||||
if (DomP.first) {
|
||||
if (DomP.second)
|
||||
visitSoftInstr(MI, DomP.second);
|
||||
else
|
||||
visitHardInstr(MI, DomP.first);
|
||||
}
|
||||
|
||||
return !DomP.first;
|
||||
}
|
||||
|
||||
/// \brief Helps avoid false dependencies on undef registers by updating the
|
||||
/// machine instructions' undef operand to use a register that the instruction
|
||||
/// is truly dependent on, or use a register with clearance higher than Pref.
|
||||
/// Returns true if it was able to find a true dependency, thus not requiring
|
||||
/// a dependency breaking instruction regardless of clearance.
|
||||
bool ExecutionDepsFix::pickBestRegisterForUndef(MachineInstr *MI,
|
||||
unsigned OpIdx, unsigned Pref) {
|
||||
MachineOperand &MO = MI->getOperand(OpIdx);
|
||||
assert(MO.isUndef() && "Expected undef machine operand");
|
||||
|
||||
unsigned OriginalReg = MO.getReg();
|
||||
|
||||
// Update only undef operands that are mapped to one register.
|
||||
if (AliasMap[OriginalReg].size() != 1)
|
||||
return false;
|
||||
|
||||
// Get the undef operand's register class
|
||||
const TargetRegisterClass *OpRC =
|
||||
TII->getRegClass(MI->getDesc(), OpIdx, TRI, *MF);
|
||||
|
||||
// If the instruction has a true dependency, we can hide the false depdency
|
||||
// behind it.
|
||||
for (MachineOperand &CurrMO : MI->operands()) {
|
||||
if (!CurrMO.isReg() || CurrMO.isDef() || CurrMO.isUndef() ||
|
||||
!OpRC->contains(CurrMO.getReg()))
|
||||
continue;
|
||||
// We found a true dependency - replace the undef register with the true
|
||||
// dependency.
|
||||
MO.setReg(CurrMO.getReg());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Go over all registers in the register class and find the register with
|
||||
// max clearance or clearance higher than Pref.
|
||||
unsigned MaxClearance = 0;
|
||||
unsigned MaxClearanceReg = OriginalReg;
|
||||
ArrayRef<MCPhysReg> Order = RegClassInfo.getOrder(OpRC);
|
||||
for (auto Reg : Order) {
|
||||
assert(AliasMap[Reg].size() == 1 &&
|
||||
"Reg is expected to be mapped to a single index");
|
||||
int RCrx = *regIndices(Reg).begin();
|
||||
unsigned Clearance = CurInstr - LiveRegs[RCrx].Def;
|
||||
if (Clearance <= MaxClearance)
|
||||
continue;
|
||||
MaxClearance = Clearance;
|
||||
MaxClearanceReg = Reg;
|
||||
|
||||
if (MaxClearance > Pref)
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the operand if we found a register with better clearance.
|
||||
if (MaxClearanceReg != OriginalReg)
|
||||
MO.setReg(MaxClearanceReg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Return true to if it makes sense to break dependence on a partial def
|
||||
/// or undef use.
|
||||
bool ExecutionDepsFix::shouldBreakDependence(MachineInstr *MI, unsigned OpIdx,
|
||||
unsigned Pref) {
|
||||
unsigned reg = MI->getOperand(OpIdx).getReg();
|
||||
for (int rx : regIndices(reg)) {
|
||||
unsigned Clearance = CurInstr - LiveRegs[rx].Def;
|
||||
DEBUG(dbgs() << "Clearance: " << Clearance << ", want " << Pref);
|
||||
|
||||
if (Pref > Clearance) {
|
||||
DEBUG(dbgs() << ": Break dependency.\n");
|
||||
continue;
|
||||
}
|
||||
DEBUG(dbgs() << ": OK .\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Update def-ages for registers defined by MI.
|
||||
// If Kill is set, also kill off DomainValues clobbered by the defs.
|
||||
//
|
||||
// Also break dependencies on partial defs and undef uses.
|
||||
void ExecutionDepsFix::processDefs(MachineInstr *MI, bool breakDependency,
|
||||
bool Kill) {
|
||||
assert(!MI->isDebugValue() && "Won't process debug values");
|
||||
|
||||
// Break dependence on undef uses. Do this before updating LiveRegs below.
|
||||
unsigned OpNum;
|
||||
if (breakDependency) {
|
||||
unsigned Pref = TII->getUndefRegClearance(*MI, OpNum, TRI);
|
||||
if (Pref) {
|
||||
bool HadTrueDependency = pickBestRegisterForUndef(MI, OpNum, Pref);
|
||||
// We don't need to bother trying to break a dependency if this
|
||||
// instruction has a true dependency on that register through another
|
||||
// operand - we'll have to wait for it to be available regardless.
|
||||
if (!HadTrueDependency && shouldBreakDependence(MI, OpNum, Pref))
|
||||
UndefReads.push_back(std::make_pair(MI, OpNum));
|
||||
}
|
||||
}
|
||||
const MCInstrDesc &MCID = MI->getDesc();
|
||||
for (unsigned i = 0,
|
||||
e = MI->isVariadic() ? MI->getNumOperands() : MCID.getNumDefs();
|
||||
i != e; ++i) {
|
||||
MachineOperand &MO = MI->getOperand(i);
|
||||
if (!MO.isReg())
|
||||
continue;
|
||||
if (MO.isUse())
|
||||
continue;
|
||||
for (int rx : regIndices(MO.getReg())) {
|
||||
// This instruction explicitly defines rx.
|
||||
DEBUG(dbgs() << TRI->getName(RC->getRegister(rx)) << ":\t" << CurInstr
|
||||
<< '\t' << *MI);
|
||||
|
||||
if (breakDependency) {
|
||||
// Check clearance before partial register updates.
|
||||
// Call breakDependence before setting LiveRegs[rx].Def.
|
||||
unsigned Pref = TII->getPartialRegUpdateClearance(*MI, i, TRI);
|
||||
if (Pref && shouldBreakDependence(MI, i, Pref))
|
||||
TII->breakPartialRegDependency(*MI, i, TRI);
|
||||
}
|
||||
|
||||
// How many instructions since rx was last written?
|
||||
LiveRegs[rx].Def = CurInstr;
|
||||
|
||||
// Kill off domains redefined by generic instructions.
|
||||
if (Kill)
|
||||
kill(rx);
|
||||
}
|
||||
}
|
||||
++CurInstr;
|
||||
}
|
||||
|
||||
/// \break Break false dependencies on undefined register reads.
|
||||
///
|
||||
/// Walk the block backward computing precise liveness. This is expensive, so we
|
||||
/// only do it on demand. Note that the occurrence of undefined register reads
|
||||
/// that should be broken is very rare, but when they occur we may have many in
|
||||
/// a single block.
|
||||
void ExecutionDepsFix::processUndefReads(MachineBasicBlock *MBB) {
|
||||
if (UndefReads.empty())
|
||||
return;
|
||||
|
||||
// Collect this block's live out register units.
|
||||
LiveRegSet.init(*TRI);
|
||||
// We do not need to care about pristine registers as they are just preserved
|
||||
// but not actually used in the function.
|
||||
LiveRegSet.addLiveOutsNoPristines(*MBB);
|
||||
|
||||
MachineInstr *UndefMI = UndefReads.back().first;
|
||||
unsigned OpIdx = UndefReads.back().second;
|
||||
|
||||
for (MachineInstr &I : make_range(MBB->rbegin(), MBB->rend())) {
|
||||
// Update liveness, including the current instruction's defs.
|
||||
LiveRegSet.stepBackward(I);
|
||||
|
||||
if (UndefMI == &I) {
|
||||
if (!LiveRegSet.contains(UndefMI->getOperand(OpIdx).getReg()))
|
||||
TII->breakPartialRegDependency(*UndefMI, OpIdx, TRI);
|
||||
|
||||
UndefReads.pop_back();
|
||||
if (UndefReads.empty())
|
||||
return;
|
||||
|
||||
UndefMI = UndefReads.back().first;
|
||||
OpIdx = UndefReads.back().second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A hard instruction only works in one domain. All input registers will be
|
||||
// forced into that domain.
|
||||
void ExecutionDepsFix::visitHardInstr(MachineInstr *mi, unsigned domain) {
|
||||
// Collapse all uses.
|
||||
for (unsigned i = mi->getDesc().getNumDefs(),
|
||||
e = mi->getDesc().getNumOperands(); i != e; ++i) {
|
||||
MachineOperand &mo = mi->getOperand(i);
|
||||
if (!mo.isReg()) continue;
|
||||
for (int rx : regIndices(mo.getReg())) {
|
||||
force(rx, domain);
|
||||
}
|
||||
}
|
||||
|
||||
// Kill all defs and force them.
|
||||
for (unsigned i = 0, e = mi->getDesc().getNumDefs(); i != e; ++i) {
|
||||
MachineOperand &mo = mi->getOperand(i);
|
||||
if (!mo.isReg()) continue;
|
||||
for (int rx : regIndices(mo.getReg())) {
|
||||
kill(rx);
|
||||
force(rx, domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A soft instruction can be changed to work in other domains given by mask.
|
||||
void ExecutionDepsFix::visitSoftInstr(MachineInstr *mi, unsigned mask) {
|
||||
// Bitmask of available domains for this instruction after taking collapsed
|
||||
// operands into account.
|
||||
unsigned available = mask;
|
||||
|
||||
// Scan the explicit use operands for incoming domains.
|
||||
SmallVector<int, 4> used;
|
||||
if (LiveRegs)
|
||||
for (unsigned i = mi->getDesc().getNumDefs(),
|
||||
e = mi->getDesc().getNumOperands(); i != e; ++i) {
|
||||
MachineOperand &mo = mi->getOperand(i);
|
||||
if (!mo.isReg()) continue;
|
||||
for (int rx : regIndices(mo.getReg())) {
|
||||
DomainValue *dv = LiveRegs[rx].Value;
|
||||
if (dv == nullptr)
|
||||
continue;
|
||||
// Bitmask of domains that dv and available have in common.
|
||||
unsigned common = dv->getCommonDomains(available);
|
||||
// Is it possible to use this collapsed register for free?
|
||||
if (dv->isCollapsed()) {
|
||||
// Restrict available domains to the ones in common with the operand.
|
||||
// If there are no common domains, we must pay the cross-domain
|
||||
// penalty for this operand.
|
||||
if (common) available = common;
|
||||
} else if (common)
|
||||
// Open DomainValue is compatible, save it for merging.
|
||||
used.push_back(rx);
|
||||
else
|
||||
// Open DomainValue is not compatible with instruction. It is useless
|
||||
// now.
|
||||
kill(rx);
|
||||
}
|
||||
}
|
||||
|
||||
// If the collapsed operands force a single domain, propagate the collapse.
|
||||
if (isPowerOf2_32(available)) {
|
||||
unsigned domain = countTrailingZeros(available);
|
||||
TII->setExecutionDomain(*mi, domain);
|
||||
visitHardInstr(mi, domain);
|
||||
return;
|
||||
}
|
||||
|
||||
// Kill off any remaining uses that don't match available, and build a list of
|
||||
// incoming DomainValues that we want to merge.
|
||||
SmallVector<const LiveReg *, 4> Regs;
|
||||
for (int rx : used) {
|
||||
assert(LiveRegs && "no space allocated for live registers");
|
||||
const LiveReg &LR = LiveRegs[rx];
|
||||
// This useless DomainValue could have been missed above.
|
||||
if (!LR.Value->getCommonDomains(available)) {
|
||||
kill(rx);
|
||||
continue;
|
||||
}
|
||||
// Sorted insertion.
|
||||
auto I = std::upper_bound(Regs.begin(), Regs.end(), &LR,
|
||||
[](const LiveReg *LHS, const LiveReg *RHS) {
|
||||
return LHS->Def < RHS->Def;
|
||||
});
|
||||
Regs.insert(I, &LR);
|
||||
}
|
||||
|
||||
// doms are now sorted in order of appearance. Try to merge them all, giving
|
||||
// priority to the latest ones.
|
||||
DomainValue *dv = nullptr;
|
||||
while (!Regs.empty()) {
|
||||
if (!dv) {
|
||||
dv = Regs.pop_back_val()->Value;
|
||||
// Force the first dv to match the current instruction.
|
||||
dv->AvailableDomains = dv->getCommonDomains(available);
|
||||
assert(dv->AvailableDomains && "Domain should have been filtered");
|
||||
continue;
|
||||
}
|
||||
|
||||
DomainValue *Latest = Regs.pop_back_val()->Value;
|
||||
// Skip already merged values.
|
||||
if (Latest == dv || Latest->Next)
|
||||
continue;
|
||||
if (merge(dv, Latest))
|
||||
continue;
|
||||
|
||||
// If latest didn't merge, it is useless now. Kill all registers using it.
|
||||
for (int i : used) {
|
||||
assert(LiveRegs && "no space allocated for live registers");
|
||||
if (LiveRegs[i].Value == Latest)
|
||||
kill(i);
|
||||
}
|
||||
}
|
||||
|
||||
// dv is the DomainValue we are going to use for this instruction.
|
||||
if (!dv) {
|
||||
dv = alloc();
|
||||
dv->AvailableDomains = available;
|
||||
}
|
||||
dv->Instrs.push_back(mi);
|
||||
|
||||
// Finally set all defs and non-collapsed uses to dv. We must iterate through
|
||||
// all the operators, including imp-def ones.
|
||||
for (MachineInstr::mop_iterator ii = mi->operands_begin(),
|
||||
ee = mi->operands_end();
|
||||
ii != ee; ++ii) {
|
||||
MachineOperand &mo = *ii;
|
||||
if (!mo.isReg()) continue;
|
||||
for (int rx : regIndices(mo.getReg())) {
|
||||
if (!LiveRegs[rx].Value || (mo.isDef() && LiveRegs[rx].Value != dv)) {
|
||||
kill(rx);
|
||||
setLiveReg(rx, dv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExecutionDepsFix::processBasicBlock(MachineBasicBlock *MBB,
|
||||
bool PrimaryPass) {
|
||||
enterBasicBlock(MBB);
|
||||
// If this block is not done, it makes little sense to make any decisions
|
||||
// based on clearance information. We need to make a second pass anyway,
|
||||
// and by then we'll have better information, so we can avoid doing the work
|
||||
// to try and break dependencies now.
|
||||
bool breakDependency = isBlockDone(MBB);
|
||||
for (MachineInstr &MI : *MBB) {
|
||||
if (!MI.isDebugValue()) {
|
||||
bool Kill = false;
|
||||
if (PrimaryPass)
|
||||
Kill = visitInstr(&MI);
|
||||
processDefs(&MI, breakDependency, Kill);
|
||||
}
|
||||
}
|
||||
if (breakDependency)
|
||||
processUndefReads(MBB);
|
||||
leaveBasicBlock(MBB);
|
||||
}
|
||||
|
||||
bool ExecutionDepsFix::isBlockDone(MachineBasicBlock *MBB) {
|
||||
return MBBInfos[MBB].PrimaryCompleted &&
|
||||
MBBInfos[MBB].IncomingCompleted == MBBInfos[MBB].PrimaryIncoming &&
|
||||
MBBInfos[MBB].IncomingProcessed == MBB->pred_size();
|
||||
}
|
||||
|
||||
bool ExecutionDepsFix::runOnMachineFunction(MachineFunction &mf) {
|
||||
if (skipFunction(*mf.getFunction()))
|
||||
return false;
|
||||
MF = &mf;
|
||||
TII = MF->getSubtarget().getInstrInfo();
|
||||
TRI = MF->getSubtarget().getRegisterInfo();
|
||||
RegClassInfo.runOnMachineFunction(mf);
|
||||
LiveRegs = nullptr;
|
||||
assert(NumRegs == RC->getNumRegs() && "Bad regclass");
|
||||
|
||||
DEBUG(dbgs() << "********** FIX EXECUTION DEPENDENCIES: "
|
||||
<< TRI->getRegClassName(RC) << " **********\n");
|
||||
|
||||
// If no relevant registers are used in the function, we can skip it
|
||||
// completely.
|
||||
bool anyregs = false;
|
||||
const MachineRegisterInfo &MRI = mf.getRegInfo();
|
||||
for (unsigned Reg : *RC) {
|
||||
if (MRI.isPhysRegUsed(Reg)) {
|
||||
anyregs = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!anyregs) return false;
|
||||
|
||||
// Initialize the AliasMap on the first use.
|
||||
if (AliasMap.empty()) {
|
||||
// Given a PhysReg, AliasMap[PhysReg] returns a list of indices into RC and
|
||||
// therefore the LiveRegs array.
|
||||
AliasMap.resize(TRI->getNumRegs());
|
||||
for (unsigned i = 0, e = RC->getNumRegs(); i != e; ++i)
|
||||
for (MCRegAliasIterator AI(RC->getRegister(i), TRI, true);
|
||||
AI.isValid(); ++AI)
|
||||
AliasMap[*AI].push_back(i);
|
||||
}
|
||||
|
||||
// Initialize the MMBInfos
|
||||
for (auto &MBB : mf) {
|
||||
MBBInfo InitialInfo;
|
||||
MBBInfos.insert(std::make_pair(&MBB, InitialInfo));
|
||||
}
|
||||
|
||||
/*
|
||||
* We want to visit every instruction in every basic block in order to update
|
||||
* it's execution domain or break any false dependencies. However, for the
|
||||
* dependency breaking, we need to know clearances from all predecessors
|
||||
* (including any backedges). One way to do so would be to do two complete
|
||||
* passes over all basic blocks/instructions, the first for recording
|
||||
* clearances, the second to break the dependencies. However, for functions
|
||||
* without backedges, or functions with a lot of straight-line code, and
|
||||
* a small loop, that would be a lot of unnecessary work (since only the
|
||||
* BBs that are part of the loop require two passes). As an example,
|
||||
* consider the following loop.
|
||||
*
|
||||
*
|
||||
* PH -> A -> B (xmm<Undef> -> xmm<Def>) -> C -> D -> EXIT
|
||||
* ^ |
|
||||
* +----------------------------------+
|
||||
*
|
||||
* The iteration order is as follows:
|
||||
* Naive: PH A B C D A' B' C' D'
|
||||
* Optimized: PH A B C A' B' C' D
|
||||
*
|
||||
* Note that we avoid processing D twice, because we can entirely process
|
||||
* the predecessors before getting to D. We call a block that is ready
|
||||
* for its second round of processing `done` (isBlockDone). Once we finish
|
||||
* processing some block, we update the counters in MBBInfos and re-process
|
||||
* any successors that are now done.
|
||||
*/
|
||||
|
||||
MachineBasicBlock *Entry = &*MF->begin();
|
||||
ReversePostOrderTraversal<MachineBasicBlock*> RPOT(Entry);
|
||||
SmallVector<MachineBasicBlock *, 4> Workqueue;
|
||||
for (ReversePostOrderTraversal<MachineBasicBlock*>::rpo_iterator
|
||||
MBBI = RPOT.begin(), MBBE = RPOT.end(); MBBI != MBBE; ++MBBI) {
|
||||
MachineBasicBlock *MBB = *MBBI;
|
||||
// N.B: IncomingProcessed and IncomingCompleted were already updated while
|
||||
// processing this block's predecessors.
|
||||
MBBInfos[MBB].PrimaryCompleted = true;
|
||||
MBBInfos[MBB].PrimaryIncoming = MBBInfos[MBB].IncomingProcessed;
|
||||
bool Primary = true;
|
||||
Workqueue.push_back(MBB);
|
||||
while (!Workqueue.empty()) {
|
||||
MachineBasicBlock *ActiveMBB = &*Workqueue.back();
|
||||
Workqueue.pop_back();
|
||||
processBasicBlock(ActiveMBB, Primary);
|
||||
bool Done = isBlockDone(ActiveMBB);
|
||||
for (auto *Succ : ActiveMBB->successors()) {
|
||||
if (!isBlockDone(Succ)) {
|
||||
if (Primary) {
|
||||
MBBInfos[Succ].IncomingProcessed++;
|
||||
}
|
||||
if (Done) {
|
||||
MBBInfos[Succ].IncomingCompleted++;
|
||||
}
|
||||
if (isBlockDone(Succ)) {
|
||||
Workqueue.push_back(Succ);
|
||||
}
|
||||
}
|
||||
}
|
||||
Primary = false;
|
||||
}
|
||||
}
|
||||
|
||||
// We need to go through again and finalize any blocks that are not done yet.
|
||||
// This is possible if blocks have dead predecessors, so we didn't visit them
|
||||
// above.
|
||||
for (ReversePostOrderTraversal<MachineBasicBlock *>::rpo_iterator
|
||||
MBBI = RPOT.begin(),
|
||||
MBBE = RPOT.end();
|
||||
MBBI != MBBE; ++MBBI) {
|
||||
MachineBasicBlock *MBB = *MBBI;
|
||||
if (!isBlockDone(MBB)) {
|
||||
processBasicBlock(MBB, false);
|
||||
// Don't update successors here. We'll get to them anyway through this
|
||||
// loop.
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the LiveOuts vectors and collapse any remaining DomainValues.
|
||||
for (ReversePostOrderTraversal<MachineBasicBlock*>::rpo_iterator
|
||||
MBBI = RPOT.begin(), MBBE = RPOT.end(); MBBI != MBBE; ++MBBI) {
|
||||
auto FI = MBBInfos.find(*MBBI);
|
||||
if (FI == MBBInfos.end() || !FI->second.OutRegs)
|
||||
continue;
|
||||
for (unsigned i = 0, e = NumRegs; i != e; ++i)
|
||||
if (FI->second.OutRegs[i].Value)
|
||||
release(FI->second.OutRegs[i].Value);
|
||||
delete[] FI->second.OutRegs;
|
||||
}
|
||||
MBBInfos.clear();
|
||||
UndefReads.clear();
|
||||
Avail.clear();
|
||||
Allocator.DestroyAll();
|
||||
|
||||
return false;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,88 +0,0 @@
|
||||
//===-- LiveStackAnalysis.cpp - Live Stack Slot Analysis ------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the live stack slot analysis pass. It is analogous to
|
||||
// live interval analysis except it's analyzing liveness of stack slots rather
|
||||
// than registers.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/CodeGen/LiveStackAnalysis.h"
|
||||
#include "llvm/CodeGen/LiveIntervalAnalysis.h"
|
||||
#include "llvm/CodeGen/Passes.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Target/TargetRegisterInfo.h"
|
||||
#include "llvm/Target/TargetSubtargetInfo.h"
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "livestacks"
|
||||
|
||||
char LiveStacks::ID = 0;
|
||||
INITIALIZE_PASS_BEGIN(LiveStacks, DEBUG_TYPE,
|
||||
"Live Stack Slot Analysis", false, false)
|
||||
INITIALIZE_PASS_DEPENDENCY(SlotIndexes)
|
||||
INITIALIZE_PASS_END(LiveStacks, DEBUG_TYPE,
|
||||
"Live Stack Slot Analysis", false, false)
|
||||
|
||||
char &llvm::LiveStacksID = LiveStacks::ID;
|
||||
|
||||
void LiveStacks::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AU.setPreservesAll();
|
||||
AU.addPreserved<SlotIndexes>();
|
||||
AU.addRequiredTransitive<SlotIndexes>();
|
||||
MachineFunctionPass::getAnalysisUsage(AU);
|
||||
}
|
||||
|
||||
void LiveStacks::releaseMemory() {
|
||||
// Release VNInfo memory regions, VNInfo objects don't need to be dtor'd.
|
||||
VNInfoAllocator.Reset();
|
||||
S2IMap.clear();
|
||||
S2RCMap.clear();
|
||||
}
|
||||
|
||||
bool LiveStacks::runOnMachineFunction(MachineFunction &MF) {
|
||||
TRI = MF.getSubtarget().getRegisterInfo();
|
||||
// FIXME: No analysis is being done right now. We are relying on the
|
||||
// register allocators to provide the information.
|
||||
return false;
|
||||
}
|
||||
|
||||
LiveInterval &
|
||||
LiveStacks::getOrCreateInterval(int Slot, const TargetRegisterClass *RC) {
|
||||
assert(Slot >= 0 && "Spill slot indice must be >= 0");
|
||||
SS2IntervalMap::iterator I = S2IMap.find(Slot);
|
||||
if (I == S2IMap.end()) {
|
||||
I = S2IMap.emplace(std::piecewise_construct, std::forward_as_tuple(Slot),
|
||||
std::forward_as_tuple(
|
||||
TargetRegisterInfo::index2StackSlot(Slot), 0.0F))
|
||||
.first;
|
||||
S2RCMap.insert(std::make_pair(Slot, RC));
|
||||
} else {
|
||||
// Use the largest common subclass register class.
|
||||
const TargetRegisterClass *OldRC = S2RCMap[Slot];
|
||||
S2RCMap[Slot] = TRI->getCommonSubClass(OldRC, RC);
|
||||
}
|
||||
return I->second;
|
||||
}
|
||||
|
||||
/// print - Implement the dump method.
|
||||
void LiveStacks::print(raw_ostream &OS, const Module*) const {
|
||||
|
||||
OS << "********** INTERVALS **********\n";
|
||||
for (const_iterator I = begin(), E = end(); I != E; ++I) {
|
||||
I->second.print(OS);
|
||||
int Slot = I->first;
|
||||
const TargetRegisterClass *RC = getIntervalRegClass(Slot);
|
||||
if (RC)
|
||||
OS << " [" << TRI->getRegClassName(RC) << "]\n";
|
||||
else
|
||||
OS << " [Unknown]\n";
|
||||
}
|
||||
}
|
@ -1,243 +0,0 @@
|
||||
//===- TypeName.cpp ------------------------------------------- *- C++ --*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/TypeName.h"
|
||||
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
namespace {
|
||||
class TypeNameComputer : public TypeVisitorCallbacks {
|
||||
/// The type collection. Used to calculate names of nested types.
|
||||
TypeCollection &Types;
|
||||
TypeIndex CurrentTypeIndex = TypeIndex::None();
|
||||
|
||||
/// Name of the current type. Only valid before visitTypeEnd.
|
||||
SmallString<256> Name;
|
||||
|
||||
public:
|
||||
explicit TypeNameComputer(TypeCollection &Types) : Types(Types) {}
|
||||
|
||||
StringRef name() const { return Name; }
|
||||
|
||||
/// Paired begin/end actions for all types. Receives all record data,
|
||||
/// including the fixed-length record prefix.
|
||||
Error visitTypeBegin(CVType &Record) override;
|
||||
Error visitTypeBegin(CVType &Record, TypeIndex Index) override;
|
||||
Error visitTypeEnd(CVType &Record) override;
|
||||
|
||||
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
||||
Error visitKnownRecord(CVType &CVR, Name##Record &Record) override;
|
||||
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#define MEMBER_RECORD(EnumName, EnumVal, Name)
|
||||
#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Error TypeNameComputer::visitTypeBegin(CVType &Record) {
|
||||
llvm_unreachable("Must call visitTypeBegin with a TypeIndex!");
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitTypeBegin(CVType &Record, TypeIndex Index) {
|
||||
// Reset Name to the empty string. If the visitor sets it, we know it.
|
||||
Name = "";
|
||||
CurrentTypeIndex = Index;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitTypeEnd(CVType &CVR) { return Error::success(); }
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
FieldListRecord &FieldList) {
|
||||
Name = "<field list>";
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVRecord<TypeLeafKind> &CVR,
|
||||
StringIdRecord &String) {
|
||||
Name = String.getString();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, ArgListRecord &Args) {
|
||||
auto Indices = Args.getIndices();
|
||||
uint32_t Size = Indices.size();
|
||||
Name = "(";
|
||||
for (uint32_t I = 0; I < Size; ++I) {
|
||||
assert(Indices[I] < CurrentTypeIndex);
|
||||
|
||||
Name.append(Types.getTypeName(Indices[I]));
|
||||
if (I + 1 != Size)
|
||||
Name.append(", ");
|
||||
}
|
||||
Name.push_back(')');
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
StringListRecord &Strings) {
|
||||
auto Indices = Strings.getIndices();
|
||||
uint32_t Size = Indices.size();
|
||||
Name = "\"";
|
||||
for (uint32_t I = 0; I < Size; ++I) {
|
||||
Name.append(Types.getTypeName(Indices[I]));
|
||||
if (I + 1 != Size)
|
||||
Name.append("\" \"");
|
||||
}
|
||||
Name.push_back('\"');
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, ClassRecord &Class) {
|
||||
Name = Class.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, UnionRecord &Union) {
|
||||
Name = Union.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
|
||||
Name = Enum.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, ArrayRecord &AT) {
|
||||
Name = AT.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, VFTableRecord &VFT) {
|
||||
Name = VFT.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, MemberFuncIdRecord &Id) {
|
||||
Name = Id.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, ProcedureRecord &Proc) {
|
||||
StringRef Ret = Types.getTypeName(Proc.getReturnType());
|
||||
StringRef Params = Types.getTypeName(Proc.getArgumentList());
|
||||
Name = formatv("{0} {1}", Ret, Params).sstr<256>();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
MemberFunctionRecord &MF) {
|
||||
StringRef Ret = Types.getTypeName(MF.getReturnType());
|
||||
StringRef Class = Types.getTypeName(MF.getClassType());
|
||||
StringRef Params = Types.getTypeName(MF.getArgumentList());
|
||||
Name = formatv("{0} {1}::{2}", Ret, Class, Params).sstr<256>();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, FuncIdRecord &Func) {
|
||||
Name = Func.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, TypeServer2Record &TS) {
|
||||
Name = TS.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, PointerRecord &Ptr) {
|
||||
|
||||
if (Ptr.isPointerToMember()) {
|
||||
const MemberPointerInfo &MI = Ptr.getMemberInfo();
|
||||
|
||||
StringRef Pointee = Types.getTypeName(Ptr.getReferentType());
|
||||
StringRef Class = Types.getTypeName(MI.getContainingType());
|
||||
Name = formatv("{0} {1}::*", Pointee, Class);
|
||||
} else {
|
||||
if (Ptr.isConst())
|
||||
Name.append("const ");
|
||||
if (Ptr.isVolatile())
|
||||
Name.append("volatile ");
|
||||
if (Ptr.isUnaligned())
|
||||
Name.append("__unaligned ");
|
||||
|
||||
Name.append(Types.getTypeName(Ptr.getReferentType()));
|
||||
|
||||
if (Ptr.getMode() == PointerMode::LValueReference)
|
||||
Name.append("&");
|
||||
else if (Ptr.getMode() == PointerMode::RValueReference)
|
||||
Name.append("&&");
|
||||
else if (Ptr.getMode() == PointerMode::Pointer)
|
||||
Name.append("*");
|
||||
}
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, ModifierRecord &Mod) {
|
||||
uint16_t Mods = static_cast<uint16_t>(Mod.getModifiers());
|
||||
|
||||
SmallString<256> TypeName;
|
||||
if (Mods & uint16_t(ModifierOptions::Const))
|
||||
Name.append("const ");
|
||||
if (Mods & uint16_t(ModifierOptions::Volatile))
|
||||
Name.append("volatile ");
|
||||
if (Mods & uint16_t(ModifierOptions::Unaligned))
|
||||
Name.append("__unaligned ");
|
||||
Name.append(Types.getTypeName(Mod.getModifiedType()));
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
VFTableShapeRecord &Shape) {
|
||||
Name = formatv("<vftable {0} methods>", Shape.getEntryCount());
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(
|
||||
CVType &CVR, UdtModSourceLineRecord &ModSourceLine) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
UdtSourceLineRecord &SourceLine) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, BitFieldRecord &BF) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
MethodOverloadListRecord &Overloads) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, BuildInfoRecord &BI) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, LabelRecord &R) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
std::string llvm::codeview::computeTypeName(TypeCollection &Types,
|
||||
TypeIndex Index) {
|
||||
TypeNameComputer Computer(Types);
|
||||
CVType Record = Types.getType(Index);
|
||||
if (auto EC = visitTypeRecord(Record, Index, Computer)) {
|
||||
consumeError(std::move(EC));
|
||||
return "<unknown UDT>";
|
||||
}
|
||||
return Computer.name();
|
||||
}
|
@ -1,389 +0,0 @@
|
||||
//===- TypeSerialzier.cpp -------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/TypeSerializer.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
||||
#include "llvm/DebugInfo/CodeView/RecordSerialization.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/BinaryByteStream.h"
|
||||
#include "llvm/Support/BinaryStreamWriter.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
namespace {
|
||||
|
||||
struct HashedType {
|
||||
uint64_t Hash;
|
||||
const uint8_t *Data;
|
||||
unsigned Size; // FIXME: Go to uint16_t?
|
||||
TypeIndex Index;
|
||||
};
|
||||
|
||||
/// Wrapper around a poitner to a HashedType. Hash and equality operations are
|
||||
/// based on data in the pointee.
|
||||
struct HashedTypePtr {
|
||||
HashedTypePtr() = default;
|
||||
HashedTypePtr(HashedType *Ptr) : Ptr(Ptr) {}
|
||||
|
||||
HashedType *Ptr = nullptr;
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
namespace llvm {
|
||||
|
||||
template <> struct DenseMapInfo<HashedTypePtr> {
|
||||
static inline HashedTypePtr getEmptyKey() { return HashedTypePtr(nullptr); }
|
||||
|
||||
static inline HashedTypePtr getTombstoneKey() {
|
||||
return HashedTypePtr(reinterpret_cast<HashedType *>(1));
|
||||
}
|
||||
|
||||
static unsigned getHashValue(HashedTypePtr Val) {
|
||||
assert(Val.Ptr != getEmptyKey().Ptr && Val.Ptr != getTombstoneKey().Ptr);
|
||||
return Val.Ptr->Hash;
|
||||
}
|
||||
|
||||
static bool isEqual(HashedTypePtr LHSP, HashedTypePtr RHSP) {
|
||||
HashedType *LHS = LHSP.Ptr;
|
||||
HashedType *RHS = RHSP.Ptr;
|
||||
if (RHS == getEmptyKey().Ptr || RHS == getTombstoneKey().Ptr)
|
||||
return LHS == RHS;
|
||||
if (LHS->Hash != RHS->Hash || LHS->Size != RHS->Size)
|
||||
return false;
|
||||
return ::memcmp(LHS->Data, RHS->Data, LHS->Size) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
/// Private implementation so that we don't leak our DenseMap instantiations to
|
||||
/// users.
|
||||
class llvm::codeview::TypeHasher {
|
||||
private:
|
||||
/// Storage for type record provided by the caller. Records will outlive the
|
||||
/// hasher object, so they should be allocated here.
|
||||
BumpPtrAllocator &RecordStorage;
|
||||
|
||||
/// Storage for hash keys. These only need to live as long as the hashing
|
||||
/// operation.
|
||||
BumpPtrAllocator KeyStorage;
|
||||
|
||||
/// Hash table. We really want a DenseMap<ArrayRef<uint8_t>, TypeIndex> here,
|
||||
/// but DenseMap is inefficient when the keys are long (like type records)
|
||||
/// because it recomputes the hash value of every key when it grows. This
|
||||
/// value type stores the hash out of line in KeyStorage, so that table
|
||||
/// entries are small and easy to rehash.
|
||||
DenseSet<HashedTypePtr> HashedRecords;
|
||||
|
||||
public:
|
||||
TypeHasher(BumpPtrAllocator &RecordStorage) : RecordStorage(RecordStorage) {}
|
||||
|
||||
void reset() { HashedRecords.clear(); }
|
||||
|
||||
/// Takes the bytes of type record, inserts them into the hash table, saves
|
||||
/// them, and returns a pointer to an identical stable type record along with
|
||||
/// its type index in the destination stream.
|
||||
TypeIndex getOrCreateRecord(ArrayRef<uint8_t> &Record, TypeIndex TI);
|
||||
};
|
||||
|
||||
TypeIndex TypeHasher::getOrCreateRecord(ArrayRef<uint8_t> &Record,
|
||||
TypeIndex TI) {
|
||||
assert(Record.size() < UINT32_MAX && "Record too big");
|
||||
assert(Record.size() % 4 == 0 && "Record is not aligned to 4 bytes!");
|
||||
|
||||
// Compute the hash up front so we can store it in the key.
|
||||
HashedType TempHashedType = {hash_value(Record), Record.data(),
|
||||
unsigned(Record.size()), TI};
|
||||
auto Result = HashedRecords.insert(HashedTypePtr(&TempHashedType));
|
||||
HashedType *&Hashed = Result.first->Ptr;
|
||||
|
||||
if (Result.second) {
|
||||
// This was a new type record. We need stable storage for both the key and
|
||||
// the record. The record should outlive the hashing operation.
|
||||
Hashed = KeyStorage.Allocate<HashedType>();
|
||||
*Hashed = TempHashedType;
|
||||
|
||||
uint8_t *Stable = RecordStorage.Allocate<uint8_t>(Record.size());
|
||||
memcpy(Stable, Record.data(), Record.size());
|
||||
Hashed->Data = Stable;
|
||||
assert(Hashed->Size == Record.size());
|
||||
}
|
||||
|
||||
// Update the caller's copy of Record to point a stable copy.
|
||||
Record = ArrayRef<uint8_t>(Hashed->Data, Hashed->Size);
|
||||
return Hashed->Index;
|
||||
}
|
||||
|
||||
TypeIndex TypeSerializer::nextTypeIndex() const {
|
||||
return TypeIndex::fromArrayIndex(SeenRecords.size());
|
||||
}
|
||||
|
||||
bool TypeSerializer::isInFieldList() const {
|
||||
return TypeKind.hasValue() && *TypeKind == TypeLeafKind::LF_FIELDLIST;
|
||||
}
|
||||
|
||||
MutableArrayRef<uint8_t> TypeSerializer::getCurrentSubRecordData() {
|
||||
assert(isInFieldList());
|
||||
return getCurrentRecordData().drop_front(CurrentSegment.length());
|
||||
}
|
||||
|
||||
MutableArrayRef<uint8_t> TypeSerializer::getCurrentRecordData() {
|
||||
return MutableArrayRef<uint8_t>(RecordBuffer).take_front(Writer.getOffset());
|
||||
}
|
||||
|
||||
Error TypeSerializer::writeRecordPrefix(TypeLeafKind Kind) {
|
||||
RecordPrefix Prefix;
|
||||
Prefix.RecordKind = Kind;
|
||||
Prefix.RecordLen = 0;
|
||||
if (auto EC = Writer.writeObject(Prefix))
|
||||
return EC;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Expected<MutableArrayRef<uint8_t>>
|
||||
TypeSerializer::addPadding(MutableArrayRef<uint8_t> Record) {
|
||||
uint32_t Align = Record.size() % 4;
|
||||
if (Align == 0)
|
||||
return Record;
|
||||
|
||||
int PaddingBytes = 4 - Align;
|
||||
int N = PaddingBytes;
|
||||
while (PaddingBytes > 0) {
|
||||
uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes);
|
||||
if (auto EC = Writer.writeInteger(Pad))
|
||||
return std::move(EC);
|
||||
--PaddingBytes;
|
||||
}
|
||||
return MutableArrayRef<uint8_t>(Record.data(), Record.size() + N);
|
||||
}
|
||||
|
||||
TypeSerializer::TypeSerializer(BumpPtrAllocator &Storage, bool Hash)
|
||||
: RecordStorage(Storage), RecordBuffer(MaxRecordLength * 2),
|
||||
Stream(RecordBuffer, support::little), Writer(Stream),
|
||||
Mapping(Writer) {
|
||||
// RecordBuffer needs to be able to hold enough data so that if we are 1
|
||||
// byte short of MaxRecordLen, and then we try to write MaxRecordLen bytes,
|
||||
// we won't overflow.
|
||||
if (Hash)
|
||||
Hasher = llvm::make_unique<TypeHasher>(Storage);
|
||||
}
|
||||
|
||||
TypeSerializer::~TypeSerializer() = default;
|
||||
|
||||
ArrayRef<ArrayRef<uint8_t>> TypeSerializer::records() const {
|
||||
return SeenRecords;
|
||||
}
|
||||
|
||||
void TypeSerializer::reset() {
|
||||
if (Hasher)
|
||||
Hasher->reset();
|
||||
Writer.setOffset(0);
|
||||
CurrentSegment = RecordSegment();
|
||||
FieldListSegments.clear();
|
||||
TypeKind.reset();
|
||||
MemberKind.reset();
|
||||
SeenRecords.clear();
|
||||
}
|
||||
|
||||
TypeIndex TypeSerializer::insertRecordBytes(ArrayRef<uint8_t> &Record) {
|
||||
assert(!TypeKind.hasValue() && "Already in a type mapping!");
|
||||
assert(Writer.getOffset() == 0 && "Stream has data already!");
|
||||
|
||||
if (Hasher) {
|
||||
TypeIndex ActualTI = Hasher->getOrCreateRecord(Record, nextTypeIndex());
|
||||
if (nextTypeIndex() == ActualTI)
|
||||
SeenRecords.push_back(Record);
|
||||
return ActualTI;
|
||||
}
|
||||
|
||||
TypeIndex NewTI = nextTypeIndex();
|
||||
uint8_t *Stable = RecordStorage.Allocate<uint8_t>(Record.size());
|
||||
memcpy(Stable, Record.data(), Record.size());
|
||||
Record = ArrayRef<uint8_t>(Stable, Record.size());
|
||||
SeenRecords.push_back(Record);
|
||||
return NewTI;
|
||||
}
|
||||
|
||||
TypeIndex TypeSerializer::insertRecord(const RemappedType &Record) {
|
||||
assert(!TypeKind.hasValue() && "Already in a type mapping!");
|
||||
assert(Writer.getOffset() == 0 && "Stream has data already!");
|
||||
|
||||
TypeIndex TI;
|
||||
ArrayRef<uint8_t> OriginalData = Record.OriginalRecord.RecordData;
|
||||
if (Record.Mappings.empty()) {
|
||||
// This record did not remap any type indices. Just write it.
|
||||
return insertRecordBytes(OriginalData);
|
||||
}
|
||||
|
||||
// At least one type index was remapped. Before we can hash it we have to
|
||||
// copy the full record bytes, re-write each type index, then hash the copy.
|
||||
// We do this in temporary storage since only the DenseMap can decide whether
|
||||
// this record already exists, and if it does we don't want the memory to
|
||||
// stick around.
|
||||
RemapStorage.resize(OriginalData.size());
|
||||
::memcpy(&RemapStorage[0], OriginalData.data(), OriginalData.size());
|
||||
uint8_t *ContentBegin = RemapStorage.data() + sizeof(RecordPrefix);
|
||||
for (const auto &M : Record.Mappings) {
|
||||
// First 4 bytes of every record are the record prefix, but the mapping
|
||||
// offset is relative to the content which starts after.
|
||||
*(TypeIndex *)(ContentBegin + M.first) = M.second;
|
||||
}
|
||||
auto RemapRef = makeArrayRef(RemapStorage);
|
||||
return insertRecordBytes(RemapRef);
|
||||
}
|
||||
|
||||
Error TypeSerializer::visitTypeBegin(CVType &Record) {
|
||||
assert(!TypeKind.hasValue() && "Already in a type mapping!");
|
||||
assert(Writer.getOffset() == 0 && "Stream has data already!");
|
||||
|
||||
if (auto EC = writeRecordPrefix(Record.kind()))
|
||||
return EC;
|
||||
|
||||
TypeKind = Record.kind();
|
||||
if (auto EC = Mapping.visitTypeBegin(Record))
|
||||
return EC;
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Expected<TypeIndex> TypeSerializer::visitTypeEndGetIndex(CVType &Record) {
|
||||
assert(TypeKind.hasValue() && "Not in a type mapping!");
|
||||
if (auto EC = Mapping.visitTypeEnd(Record))
|
||||
return std::move(EC);
|
||||
|
||||
// Update the record's length and fill out the CVType members to point to
|
||||
// the stable memory holding the record's data.
|
||||
auto ThisRecordData = getCurrentRecordData();
|
||||
auto ExpectedData = addPadding(ThisRecordData);
|
||||
if (!ExpectedData)
|
||||
return ExpectedData.takeError();
|
||||
ThisRecordData = *ExpectedData;
|
||||
|
||||
RecordPrefix *Prefix =
|
||||
reinterpret_cast<RecordPrefix *>(ThisRecordData.data());
|
||||
Prefix->RecordLen = ThisRecordData.size() - sizeof(uint16_t);
|
||||
|
||||
Record.Type = *TypeKind;
|
||||
Record.RecordData = ThisRecordData;
|
||||
|
||||
// insertRecordBytes assumes we're not in a mapping, so do this first.
|
||||
TypeKind.reset();
|
||||
Writer.setOffset(0);
|
||||
|
||||
TypeIndex InsertedTypeIndex = insertRecordBytes(Record.RecordData);
|
||||
|
||||
// Write out each additional segment in reverse order, and update each
|
||||
// record's continuation index to point to the previous one.
|
||||
for (auto X : reverse(FieldListSegments)) {
|
||||
auto CIBytes = X.take_back(sizeof(uint32_t));
|
||||
support::ulittle32_t *CI =
|
||||
reinterpret_cast<support::ulittle32_t *>(CIBytes.data());
|
||||
assert(*CI == 0xB0C0B0C0 && "Invalid TypeIndex placeholder");
|
||||
*CI = InsertedTypeIndex.getIndex();
|
||||
InsertedTypeIndex = insertRecordBytes(X);
|
||||
}
|
||||
|
||||
FieldListSegments.clear();
|
||||
CurrentSegment.SubRecords.clear();
|
||||
|
||||
return InsertedTypeIndex;
|
||||
}
|
||||
|
||||
Error TypeSerializer::visitTypeEnd(CVType &Record) {
|
||||
auto ExpectedIndex = visitTypeEndGetIndex(Record);
|
||||
if (!ExpectedIndex)
|
||||
return ExpectedIndex.takeError();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeSerializer::visitMemberBegin(CVMemberRecord &Record) {
|
||||
assert(isInFieldList() && "Not in a field list!");
|
||||
assert(!MemberKind.hasValue() && "Already in a member record!");
|
||||
MemberKind = Record.Kind;
|
||||
|
||||
if (auto EC = Mapping.visitMemberBegin(Record))
|
||||
return EC;
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeSerializer::visitMemberEnd(CVMemberRecord &Record) {
|
||||
if (auto EC = Mapping.visitMemberEnd(Record))
|
||||
return EC;
|
||||
|
||||
// Check if this subrecord makes the current segment not fit in 64K minus
|
||||
// the space for a continuation record (8 bytes). If the segment does not
|
||||
// fit, insert a continuation record.
|
||||
if (Writer.getOffset() > MaxRecordLength - ContinuationLength) {
|
||||
MutableArrayRef<uint8_t> Data = getCurrentRecordData();
|
||||
SubRecord LastSubRecord = CurrentSegment.SubRecords.back();
|
||||
uint32_t CopySize = CurrentSegment.length() - LastSubRecord.Size;
|
||||
auto CopyData = Data.take_front(CopySize);
|
||||
auto LeftOverData = Data.drop_front(CopySize);
|
||||
assert(LastSubRecord.Size == LeftOverData.size());
|
||||
|
||||
// Allocate stable storage for the record and copy the old record plus
|
||||
// continuation over.
|
||||
uint16_t LengthWithSize = CopySize + ContinuationLength;
|
||||
assert(LengthWithSize <= MaxRecordLength);
|
||||
RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(CopyData.data());
|
||||
Prefix->RecordLen = LengthWithSize - sizeof(uint16_t);
|
||||
|
||||
uint8_t *SegmentBytes = RecordStorage.Allocate<uint8_t>(LengthWithSize);
|
||||
auto SavedSegment = MutableArrayRef<uint8_t>(SegmentBytes, LengthWithSize);
|
||||
MutableBinaryByteStream CS(SavedSegment, support::little);
|
||||
BinaryStreamWriter CW(CS);
|
||||
if (auto EC = CW.writeBytes(CopyData))
|
||||
return EC;
|
||||
if (auto EC = CW.writeEnum(TypeLeafKind::LF_INDEX))
|
||||
return EC;
|
||||
if (auto EC = CW.writeInteger<uint16_t>(0))
|
||||
return EC;
|
||||
if (auto EC = CW.writeInteger<uint32_t>(0xB0C0B0C0))
|
||||
return EC;
|
||||
FieldListSegments.push_back(SavedSegment);
|
||||
|
||||
// Write a new placeholder record prefix to mark the start of this new
|
||||
// top-level record.
|
||||
Writer.setOffset(0);
|
||||
if (auto EC = writeRecordPrefix(TypeLeafKind::LF_FIELDLIST))
|
||||
return EC;
|
||||
|
||||
// Then move over the subrecord that overflowed the old segment to the
|
||||
// beginning of this segment. Note that we have to use memmove here
|
||||
// instead of Writer.writeBytes(), because the new and old locations
|
||||
// could overlap.
|
||||
::memmove(Stream.data().data() + sizeof(RecordPrefix), LeftOverData.data(),
|
||||
LeftOverData.size());
|
||||
// And point the segment writer at the end of that subrecord.
|
||||
Writer.setOffset(LeftOverData.size() + sizeof(RecordPrefix));
|
||||
|
||||
CurrentSegment.SubRecords.clear();
|
||||
CurrentSegment.SubRecords.push_back(LastSubRecord);
|
||||
}
|
||||
|
||||
// Update the CVMemberRecord since we may have shifted around or gotten
|
||||
// padded.
|
||||
Record.Data = getCurrentSubRecordData();
|
||||
|
||||
MemberKind.reset();
|
||||
return Error::success();
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
//===- SyntaxHighlighting.cpp ---------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "SyntaxHighlighting.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace dwarf;
|
||||
using namespace syntax;
|
||||
|
||||
static cl::opt<cl::boolOrDefault>
|
||||
UseColor("color",
|
||||
cl::desc("use colored syntax highlighting (default=autodetect)"),
|
||||
cl::init(cl::BOU_UNSET));
|
||||
|
||||
WithColor::WithColor(raw_ostream &OS, enum HighlightColor Type) : OS(OS) {
|
||||
// Detect color from terminal type unless the user passed the --color option.
|
||||
if (UseColor == cl::BOU_UNSET ? OS.has_colors() : UseColor == cl::BOU_TRUE) {
|
||||
switch (Type) {
|
||||
case Address: OS.changeColor(raw_ostream::YELLOW); break;
|
||||
case String: OS.changeColor(raw_ostream::GREEN); break;
|
||||
case Tag: OS.changeColor(raw_ostream::BLUE); break;
|
||||
case Attribute: OS.changeColor(raw_ostream::CYAN); break;
|
||||
case Enumerator: OS.changeColor(raw_ostream::MAGENTA); break;
|
||||
case Macro: OS.changeColor(raw_ostream::RED); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WithColor::~WithColor() {
|
||||
if (UseColor == cl::BOU_UNSET ? OS.has_colors() : UseColor == cl::BOU_TRUE)
|
||||
OS.resetColor();
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
//===- SyntaxHighlighting.h -------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_DEBUGINFO_SYNTAXHIGHLIGHTING_H
|
||||
#define LLVM_LIB_DEBUGINFO_SYNTAXHIGHLIGHTING_H
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class raw_ostream;
|
||||
|
||||
namespace dwarf {
|
||||
namespace syntax {
|
||||
|
||||
// Symbolic names for various syntax elements.
|
||||
enum HighlightColor { Address, String, Tag, Attribute, Enumerator, Macro };
|
||||
|
||||
/// An RAII object that temporarily switches an output stream to a
|
||||
/// specific color.
|
||||
class WithColor {
|
||||
raw_ostream &OS;
|
||||
|
||||
public:
|
||||
/// To be used like this: WithColor(OS, syntax::String) << "text";
|
||||
WithColor(raw_ostream &OS, enum HighlightColor Type);
|
||||
~WithColor();
|
||||
|
||||
raw_ostream& get() { return OS; }
|
||||
operator raw_ostream& () { return OS; }
|
||||
};
|
||||
|
||||
} // end namespace syntax
|
||||
} // end namespace dwarf
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_LIB_DEBUGINFO_SYNTAXHIGHLIGHTING_H
|
@ -1,93 +0,0 @@
|
||||
//===- GSI.cpp - Common Functions for GlobalsStream and PublicsStream ----===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "GSI.h"
|
||||
|
||||
#include "llvm/DebugInfo/PDB/Native/RawError.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
|
||||
#include "llvm/Support/BinaryStreamArray.h"
|
||||
#include "llvm/Support/BinaryStreamReader.h"
|
||||
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace pdb {
|
||||
|
||||
static Error checkHashHdrVersion(const GSIHashHeader *HashHdr) {
|
||||
if (HashHdr->VerHdr != GSIHashHeader::HdrVersion)
|
||||
return make_error<RawError>(
|
||||
raw_error_code::feature_unsupported,
|
||||
"Encountered unsupported globals stream version.");
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error readGSIHashBuckets(FixedStreamArray<support::ulittle32_t> &HashBuckets,
|
||||
const GSIHashHeader *HashHdr,
|
||||
BinaryStreamReader &Reader) {
|
||||
if (auto EC = checkHashHdrVersion(HashHdr))
|
||||
return EC;
|
||||
|
||||
// Before the actual hash buckets, there is a bitmap of length determined by
|
||||
// IPHR_HASH.
|
||||
ArrayRef<uint8_t> Bitmap;
|
||||
size_t BitmapSizeInBits = alignTo(IPHR_HASH + 1, 32);
|
||||
uint32_t NumBitmapEntries = BitmapSizeInBits / 8;
|
||||
if (auto EC = Reader.readBytes(Bitmap, NumBitmapEntries))
|
||||
return joinErrors(std::move(EC),
|
||||
make_error<RawError>(raw_error_code::corrupt_file,
|
||||
"Could not read a bitmap."));
|
||||
uint32_t NumBuckets = 0;
|
||||
for (uint8_t B : Bitmap)
|
||||
NumBuckets += countPopulation(B);
|
||||
|
||||
// Hash buckets follow.
|
||||
if (auto EC = Reader.readArray(HashBuckets, NumBuckets))
|
||||
return joinErrors(std::move(EC),
|
||||
make_error<RawError>(raw_error_code::corrupt_file,
|
||||
"Hash buckets corrupted."));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error readGSIHashHeader(const GSIHashHeader *&HashHdr,
|
||||
BinaryStreamReader &Reader) {
|
||||
if (Reader.readObject(HashHdr))
|
||||
return make_error<RawError>(raw_error_code::corrupt_file,
|
||||
"Stream does not contain a GSIHashHeader.");
|
||||
|
||||
if (HashHdr->VerSignature != GSIHashHeader::HdrSignature)
|
||||
return make_error<RawError>(
|
||||
raw_error_code::feature_unsupported,
|
||||
"GSIHashHeader signature (0xffffffff) not found.");
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error readGSIHashRecords(FixedStreamArray<PSHashRecord> &HashRecords,
|
||||
const GSIHashHeader *HashHdr,
|
||||
BinaryStreamReader &Reader) {
|
||||
if (auto EC = checkHashHdrVersion(HashHdr))
|
||||
return EC;
|
||||
|
||||
// HashHdr->HrSize specifies the number of bytes of PSHashRecords we have.
|
||||
// Verify that we can read them all.
|
||||
if (HashHdr->HrSize % sizeof(PSHashRecord))
|
||||
return make_error<RawError>(raw_error_code::corrupt_file,
|
||||
"Invalid HR array size.");
|
||||
uint32_t NumHashRecords = HashHdr->HrSize / sizeof(PSHashRecord);
|
||||
if (auto EC = Reader.readArray(HashRecords, NumHashRecords))
|
||||
return joinErrors(std::move(EC),
|
||||
make_error<RawError>(raw_error_code::corrupt_file,
|
||||
"Error reading hash records."));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
//===- GSI.h - Common Declarations for GlobalsStream and PublicsStream ----===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The data structures defined in this file are based on the reference
|
||||
// implementation which is available at
|
||||
// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h
|
||||
//
|
||||
// When you are reading the reference source code, you'd find the
|
||||
// information below useful.
|
||||
//
|
||||
// - ppdb1->m_fMinimalDbgInfo seems to be always true.
|
||||
// - SMALLBUCKETS macro is defined.
|
||||
//
|
||||
// The reference doesn't compile, so I learned just by reading code.
|
||||
// It's not guaranteed to be correct.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_DEBUGINFO_PDB_RAW_GSI_H
|
||||
#define LLVM_LIB_DEBUGINFO_PDB_RAW_GSI_H
|
||||
|
||||
#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
|
||||
#include "llvm/Support/BinaryStreamArray.h"
|
||||
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class BinaryStreamReader;
|
||||
|
||||
namespace pdb {
|
||||
|
||||
/// From https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.cpp
|
||||
static const unsigned IPHR_HASH = 4096;
|
||||
|
||||
/// Header of the hash tables found in the globals and publics sections.
|
||||
/// Based on GSIHashHeader in
|
||||
/// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h
|
||||
struct GSIHashHeader {
|
||||
enum : unsigned {
|
||||
HdrSignature = ~0U,
|
||||
HdrVersion = 0xeffe0000 + 19990810,
|
||||
};
|
||||
support::ulittle32_t VerSignature;
|
||||
support::ulittle32_t VerHdr;
|
||||
support::ulittle32_t HrSize;
|
||||
support::ulittle32_t NumBuckets;
|
||||
};
|
||||
|
||||
Error readGSIHashBuckets(FixedStreamArray<support::ulittle32_t> &HashBuckets,
|
||||
const GSIHashHeader *HashHdr,
|
||||
BinaryStreamReader &Reader);
|
||||
Error readGSIHashHeader(const GSIHashHeader *&HashHdr,
|
||||
BinaryStreamReader &Reader);
|
||||
Error readGSIHashRecords(FixedStreamArray<PSHashRecord> &HashRecords,
|
||||
const GSIHashHeader *HashHdr,
|
||||
BinaryStreamReader &Reader);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,89 +0,0 @@
|
||||
//===- DbiStreamBuilder.cpp - PDB Dbi Stream Creation -----------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/DebugInfo/PDB/Native/PublicsStreamBuilder.h"
|
||||
|
||||
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
|
||||
#include "llvm/DebugInfo/MSF/MSFCommon.h"
|
||||
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
|
||||
|
||||
#include "GSI.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::msf;
|
||||
using namespace llvm::pdb;
|
||||
|
||||
PublicsStreamBuilder::PublicsStreamBuilder(msf::MSFBuilder &Msf) : Msf(Msf) {}
|
||||
|
||||
PublicsStreamBuilder::~PublicsStreamBuilder() {}
|
||||
|
||||
uint32_t PublicsStreamBuilder::calculateSerializedLength() const {
|
||||
uint32_t Size = 0;
|
||||
Size += sizeof(PublicsStreamHeader);
|
||||
Size += sizeof(GSIHashHeader);
|
||||
Size += HashRecords.size() * sizeof(PSHashRecord);
|
||||
size_t BitmapSizeInBits = alignTo(IPHR_HASH + 1, 32);
|
||||
uint32_t NumBitmapEntries = BitmapSizeInBits / 8;
|
||||
Size += NumBitmapEntries;
|
||||
|
||||
// FIXME: Account for hash buckets. For now since we we write a zero-bitmap
|
||||
// indicating that no hash buckets are valid, we also write zero byets of hash
|
||||
// bucket data.
|
||||
Size += 0;
|
||||
return Size;
|
||||
}
|
||||
|
||||
Error PublicsStreamBuilder::finalizeMsfLayout() {
|
||||
Expected<uint32_t> Idx = Msf.addStream(calculateSerializedLength());
|
||||
if (!Idx)
|
||||
return Idx.takeError();
|
||||
StreamIdx = *Idx;
|
||||
|
||||
Expected<uint32_t> RecordIdx = Msf.addStream(0);
|
||||
if (!RecordIdx)
|
||||
return RecordIdx.takeError();
|
||||
RecordStreamIdx = *RecordIdx;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error PublicsStreamBuilder::commit(BinaryStreamWriter &PublicsWriter) {
|
||||
PublicsStreamHeader PSH;
|
||||
GSIHashHeader GSH;
|
||||
|
||||
// FIXME: Figure out what to put for these values.
|
||||
PSH.AddrMap = 0;
|
||||
PSH.ISectThunkTable = 0;
|
||||
PSH.NumSections = 0;
|
||||
PSH.NumThunks = 0;
|
||||
PSH.OffThunkTable = 0;
|
||||
PSH.SizeOfThunk = 0;
|
||||
PSH.SymHash = 0;
|
||||
|
||||
GSH.VerSignature = GSIHashHeader::HdrSignature;
|
||||
GSH.VerHdr = GSIHashHeader::HdrVersion;
|
||||
GSH.HrSize = 0;
|
||||
GSH.NumBuckets = 0;
|
||||
|
||||
if (auto EC = PublicsWriter.writeObject(PSH))
|
||||
return EC;
|
||||
if (auto EC = PublicsWriter.writeObject(GSH))
|
||||
return EC;
|
||||
if (auto EC = PublicsWriter.writeArray(makeArrayRef(HashRecords)))
|
||||
return EC;
|
||||
|
||||
size_t BitmapSizeInBits = alignTo(IPHR_HASH + 1, 32);
|
||||
uint32_t NumBitmapEntries = BitmapSizeInBits / 8;
|
||||
std::vector<uint8_t> BitmapData(NumBitmapEntries);
|
||||
// FIXME: Build an actual bitmap
|
||||
if (auto EC = PublicsWriter.writeBytes(makeArrayRef(BitmapData)))
|
||||
return EC;
|
||||
|
||||
// FIXME: Write actual hash buckets.
|
||||
return Error::success();
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
//===--- ObjectBuffer.h - Utility class to wrap object memory ---*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file declares a wrapper class to hold the memory into which an
|
||||
// object will be generated.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_EXECUTIONENGINE_OBJECTBUFFER_H
|
||||
#define LLVM_EXECUTIONENGINE_OBJECTBUFFER_H
|
||||
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class ObjectMemoryBuffer : public MemoryBuffer {
|
||||
public:
|
||||
template <unsigned N>
|
||||
ObjectMemoryBuffer(SmallVector<char, N> SV)
|
||||
: SV(SV), BufferName("<in-memory object>") {
|
||||
init(this->SV.begin(), this->SV.end(), false);
|
||||
}
|
||||
|
||||
template <unsigned N>
|
||||
ObjectMemoryBuffer(SmallVector<char, N> SV, StringRef Name)
|
||||
: SV(SV), BufferName(Name) {
|
||||
init(this->SV.begin(), this->SV.end(), false);
|
||||
}
|
||||
const char* getBufferIdentifier() const override { return BufferName.c_str(); }
|
||||
|
||||
BufferKind getBufferKind() const override { return MemoryBuffer_Malloc; }
|
||||
|
||||
private:
|
||||
SmallVector<char, 4096> SV;
|
||||
std::string BufferName;
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
@ -1,68 +0,0 @@
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
if( APPLE )
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
static thread_local int blah;
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
||||
" HAS_THREAD_LOCAL)
|
||||
|
||||
if( NOT HAS_THREAD_LOCAL )
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dthread_local=__thread")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(LIBFUZZER_FLAGS_BASE "${CMAKE_CXX_FLAGS}")
|
||||
if( LLVM_USE_SANITIZE_COVERAGE )
|
||||
if(NOT "${LLVM_USE_SANITIZER}" STREQUAL "Address")
|
||||
message(FATAL_ERROR
|
||||
"LibFuzzer and its tests require LLVM_USE_SANITIZER=Address and "
|
||||
"LLVM_USE_SANITIZE_COVERAGE=YES to be set."
|
||||
)
|
||||
endif()
|
||||
|
||||
# Disable the coverage and sanitizer instrumentation for the fuzzer itself.
|
||||
set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fno-sanitize-coverage=trace-pc-guard,edge,trace-cmp,indirect-calls,8bit-counters -Werror")
|
||||
endif()
|
||||
|
||||
# Compile libFuzzer if the compilation is specifically requested, OR
|
||||
# if the platform is known to be working.
|
||||
if ( LLVM_USE_SANITIZE_COVERAGE OR CMAKE_SYSTEM_NAME MATCHES "Darwin|Linux" )
|
||||
add_library(LLVMFuzzerNoMainObjects OBJECT
|
||||
FuzzerCrossOver.cpp
|
||||
FuzzerDriver.cpp
|
||||
FuzzerExtFunctionsDlsym.cpp
|
||||
FuzzerExtFunctionsDlsymWin.cpp
|
||||
FuzzerExtFunctionsWeak.cpp
|
||||
FuzzerExtraCounters.cpp
|
||||
FuzzerIO.cpp
|
||||
FuzzerIOPosix.cpp
|
||||
FuzzerIOWindows.cpp
|
||||
FuzzerLoop.cpp
|
||||
FuzzerMerge.cpp
|
||||
FuzzerMutate.cpp
|
||||
FuzzerSHA1.cpp
|
||||
FuzzerShmemPosix.cpp
|
||||
FuzzerShmemWindows.cpp
|
||||
FuzzerTracePC.cpp
|
||||
FuzzerUtil.cpp
|
||||
FuzzerUtilDarwin.cpp
|
||||
FuzzerUtilLinux.cpp
|
||||
FuzzerUtilPosix.cpp
|
||||
FuzzerUtilWindows.cpp
|
||||
)
|
||||
add_library(LLVMFuzzerNoMain STATIC
|
||||
$<TARGET_OBJECTS:LLVMFuzzerNoMainObjects>
|
||||
)
|
||||
target_link_libraries(LLVMFuzzerNoMain ${LLVM_PTHREAD_LIB})
|
||||
add_library(LLVMFuzzer STATIC
|
||||
FuzzerMain.cpp
|
||||
$<TARGET_OBJECTS:LLVMFuzzerNoMainObjects>
|
||||
)
|
||||
target_link_libraries(LLVMFuzzer ${LLVM_PTHREAD_LIB})
|
||||
endif()
|
||||
|
||||
if( LLVM_USE_SANITIZE_COVERAGE AND LLVM_INCLUDE_TESTS )
|
||||
add_subdirectory(test)
|
||||
endif()
|
@ -1,275 +0,0 @@
|
||||
//===- FuzzerCorpus.h - Internal header for the Fuzzer ----------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::InputCorpus
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_CORPUS
|
||||
#define LLVM_FUZZER_CORPUS
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "FuzzerSHA1.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
struct InputInfo {
|
||||
Unit U; // The actual input data.
|
||||
uint8_t Sha1[kSHA1NumBytes]; // Checksum.
|
||||
// Number of features that this input has and no smaller input has.
|
||||
size_t NumFeatures = 0;
|
||||
size_t Tmp = 0; // Used by ValidateFeatureSet.
|
||||
// Stats.
|
||||
size_t NumExecutedMutations = 0;
|
||||
size_t NumSuccessfullMutations = 0;
|
||||
bool MayDeleteFile = false;
|
||||
bool Reduced = false;
|
||||
std::vector<uint32_t> UniqFeatureSet;
|
||||
};
|
||||
|
||||
class InputCorpus {
|
||||
static const size_t kFeatureSetSize = 1 << 21;
|
||||
public:
|
||||
InputCorpus(const std::string &OutputCorpus) : OutputCorpus(OutputCorpus) {
|
||||
memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature));
|
||||
memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature));
|
||||
}
|
||||
~InputCorpus() {
|
||||
for (auto II : Inputs)
|
||||
delete II;
|
||||
}
|
||||
size_t size() const { return Inputs.size(); }
|
||||
size_t SizeInBytes() const {
|
||||
size_t Res = 0;
|
||||
for (auto II : Inputs)
|
||||
Res += II->U.size();
|
||||
return Res;
|
||||
}
|
||||
size_t NumActiveUnits() const {
|
||||
size_t Res = 0;
|
||||
for (auto II : Inputs)
|
||||
Res += !II->U.empty();
|
||||
return Res;
|
||||
}
|
||||
size_t MaxInputSize() const {
|
||||
size_t Res = 0;
|
||||
for (auto II : Inputs)
|
||||
Res = std::max(Res, II->U.size());
|
||||
return Res;
|
||||
}
|
||||
bool empty() const { return Inputs.empty(); }
|
||||
const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; }
|
||||
void AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile,
|
||||
const std::vector<uint32_t> &FeatureSet) {
|
||||
assert(!U.empty());
|
||||
if (FeatureDebug)
|
||||
Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures);
|
||||
Inputs.push_back(new InputInfo());
|
||||
InputInfo &II = *Inputs.back();
|
||||
II.U = U;
|
||||
II.NumFeatures = NumFeatures;
|
||||
II.MayDeleteFile = MayDeleteFile;
|
||||
II.UniqFeatureSet = FeatureSet;
|
||||
std::sort(II.UniqFeatureSet.begin(), II.UniqFeatureSet.end());
|
||||
ComputeSHA1(U.data(), U.size(), II.Sha1);
|
||||
Hashes.insert(Sha1ToString(II.Sha1));
|
||||
UpdateCorpusDistribution();
|
||||
PrintCorpus();
|
||||
// ValidateFeatureSet();
|
||||
}
|
||||
|
||||
// Debug-only
|
||||
void PrintUnit(const Unit &U) {
|
||||
if (!FeatureDebug) return;
|
||||
for (uint8_t C : U) {
|
||||
if (C != 'F' && C != 'U' && C != 'Z')
|
||||
C = '.';
|
||||
Printf("%c", C);
|
||||
}
|
||||
}
|
||||
|
||||
// Debug-only
|
||||
void PrintFeatureSet(const std::vector<uint32_t> &FeatureSet) {
|
||||
if (!FeatureDebug) return;
|
||||
Printf("{");
|
||||
for (uint32_t Feature: FeatureSet)
|
||||
Printf("%u,", Feature);
|
||||
Printf("}");
|
||||
}
|
||||
|
||||
// Debug-only
|
||||
void PrintCorpus() {
|
||||
if (!FeatureDebug) return;
|
||||
Printf("======= CORPUS:\n");
|
||||
int i = 0;
|
||||
for (auto II : Inputs) {
|
||||
if (std::find(II->U.begin(), II->U.end(), 'F') != II->U.end()) {
|
||||
Printf("[%2d] ", i);
|
||||
Printf("%s sz=%zd ", Sha1ToString(II->Sha1).c_str(), II->U.size());
|
||||
PrintUnit(II->U);
|
||||
Printf(" ");
|
||||
PrintFeatureSet(II->UniqFeatureSet);
|
||||
Printf("\n");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void Replace(InputInfo *II, const Unit &U) {
|
||||
assert(II->U.size() > U.size());
|
||||
Hashes.erase(Sha1ToString(II->Sha1));
|
||||
DeleteFile(*II);
|
||||
ComputeSHA1(U.data(), U.size(), II->Sha1);
|
||||
Hashes.insert(Sha1ToString(II->Sha1));
|
||||
II->U = U;
|
||||
II->Reduced = true;
|
||||
}
|
||||
|
||||
bool HasUnit(const Unit &U) { return Hashes.count(Hash(U)); }
|
||||
bool HasUnit(const std::string &H) { return Hashes.count(H); }
|
||||
InputInfo &ChooseUnitToMutate(Random &Rand) {
|
||||
InputInfo &II = *Inputs[ChooseUnitIdxToMutate(Rand)];
|
||||
assert(!II.U.empty());
|
||||
return II;
|
||||
};
|
||||
|
||||
// Returns an index of random unit from the corpus to mutate.
|
||||
// Hypothesis: units added to the corpus last are more likely to be
|
||||
// interesting. This function gives more weight to the more recent units.
|
||||
size_t ChooseUnitIdxToMutate(Random &Rand) {
|
||||
size_t Idx = static_cast<size_t>(CorpusDistribution(Rand));
|
||||
assert(Idx < Inputs.size());
|
||||
return Idx;
|
||||
}
|
||||
|
||||
void PrintStats() {
|
||||
for (size_t i = 0; i < Inputs.size(); i++) {
|
||||
const auto &II = *Inputs[i];
|
||||
Printf(" [%zd %s]\tsz: %zd\truns: %zd\tsucc: %zd\n", i,
|
||||
Sha1ToString(II.Sha1).c_str(), II.U.size(),
|
||||
II.NumExecutedMutations, II.NumSuccessfullMutations);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintFeatureSet() {
|
||||
for (size_t i = 0; i < kFeatureSetSize; i++) {
|
||||
if(size_t Sz = GetFeature(i))
|
||||
Printf("[%zd: id %zd sz%zd] ", i, SmallestElementPerFeature[i], Sz);
|
||||
}
|
||||
Printf("\n\t");
|
||||
for (size_t i = 0; i < Inputs.size(); i++)
|
||||
if (size_t N = Inputs[i]->NumFeatures)
|
||||
Printf(" %zd=>%zd ", i, N);
|
||||
Printf("\n");
|
||||
}
|
||||
|
||||
void DeleteFile(const InputInfo &II) {
|
||||
if (!OutputCorpus.empty() && II.MayDeleteFile)
|
||||
RemoveFile(DirPlusFile(OutputCorpus, Sha1ToString(II.Sha1)));
|
||||
}
|
||||
|
||||
void DeleteInput(size_t Idx) {
|
||||
InputInfo &II = *Inputs[Idx];
|
||||
DeleteFile(II);
|
||||
Unit().swap(II.U);
|
||||
if (FeatureDebug)
|
||||
Printf("EVICTED %zd\n", Idx);
|
||||
}
|
||||
|
||||
bool AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) {
|
||||
assert(NewSize);
|
||||
Idx = Idx % kFeatureSetSize;
|
||||
uint32_t OldSize = GetFeature(Idx);
|
||||
if (OldSize == 0 || (Shrink && OldSize > NewSize)) {
|
||||
if (OldSize > 0) {
|
||||
size_t OldIdx = SmallestElementPerFeature[Idx];
|
||||
InputInfo &II = *Inputs[OldIdx];
|
||||
assert(II.NumFeatures > 0);
|
||||
II.NumFeatures--;
|
||||
if (II.NumFeatures == 0)
|
||||
DeleteInput(OldIdx);
|
||||
} else {
|
||||
NumAddedFeatures++;
|
||||
}
|
||||
NumUpdatedFeatures++;
|
||||
if (FeatureDebug)
|
||||
Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize);
|
||||
SmallestElementPerFeature[Idx] = Inputs.size();
|
||||
InputSizesPerFeature[Idx] = NewSize;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t NumFeatures() const { return NumAddedFeatures; }
|
||||
size_t NumFeatureUpdates() const { return NumUpdatedFeatures; }
|
||||
|
||||
void ResetFeatureSet() {
|
||||
assert(Inputs.empty());
|
||||
memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature));
|
||||
memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static const bool FeatureDebug = false;
|
||||
|
||||
size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; }
|
||||
|
||||
void ValidateFeatureSet() {
|
||||
if (FeatureDebug)
|
||||
PrintFeatureSet();
|
||||
for (size_t Idx = 0; Idx < kFeatureSetSize; Idx++)
|
||||
if (GetFeature(Idx))
|
||||
Inputs[SmallestElementPerFeature[Idx]]->Tmp++;
|
||||
for (auto II: Inputs) {
|
||||
if (II->Tmp != II->NumFeatures)
|
||||
Printf("ZZZ %zd %zd\n", II->Tmp, II->NumFeatures);
|
||||
assert(II->Tmp == II->NumFeatures);
|
||||
II->Tmp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Updates the probability distribution for the units in the corpus.
|
||||
// Must be called whenever the corpus or unit weights are changed.
|
||||
void UpdateCorpusDistribution() {
|
||||
size_t N = Inputs.size();
|
||||
assert(N);
|
||||
Intervals.resize(N + 1);
|
||||
Weights.resize(N);
|
||||
std::iota(Intervals.begin(), Intervals.end(), 0);
|
||||
for (size_t i = 0; i < N; i++)
|
||||
Weights[i] = Inputs[i]->NumFeatures * (i + 1);
|
||||
CorpusDistribution = std::piecewise_constant_distribution<double>(
|
||||
Intervals.begin(), Intervals.end(), Weights.begin());
|
||||
}
|
||||
std::piecewise_constant_distribution<double> CorpusDistribution;
|
||||
|
||||
std::vector<double> Intervals;
|
||||
std::vector<double> Weights;
|
||||
|
||||
std::unordered_set<std::string> Hashes;
|
||||
std::vector<InputInfo*> Inputs;
|
||||
|
||||
size_t NumAddedFeatures = 0;
|
||||
size_t NumUpdatedFeatures = 0;
|
||||
uint32_t InputSizesPerFeature[kFeatureSetSize];
|
||||
uint32_t SmallestElementPerFeature[kFeatureSetSize];
|
||||
|
||||
std::string OutputCorpus;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_CORPUS
|
@ -1,52 +0,0 @@
|
||||
//===- FuzzerCrossOver.cpp - Cross over two test inputs -------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Cross over test inputs.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
// Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out.
|
||||
size_t MutationDispatcher::CrossOver(const uint8_t *Data1, size_t Size1,
|
||||
const uint8_t *Data2, size_t Size2,
|
||||
uint8_t *Out, size_t MaxOutSize) {
|
||||
assert(Size1 || Size2);
|
||||
MaxOutSize = Rand(MaxOutSize) + 1;
|
||||
size_t OutPos = 0;
|
||||
size_t Pos1 = 0;
|
||||
size_t Pos2 = 0;
|
||||
size_t *InPos = &Pos1;
|
||||
size_t InSize = Size1;
|
||||
const uint8_t *Data = Data1;
|
||||
bool CurrentlyUsingFirstData = true;
|
||||
while (OutPos < MaxOutSize && (Pos1 < Size1 || Pos2 < Size2)) {
|
||||
// Merge a part of Data into Out.
|
||||
size_t OutSizeLeft = MaxOutSize - OutPos;
|
||||
if (*InPos < InSize) {
|
||||
size_t InSizeLeft = InSize - *InPos;
|
||||
size_t MaxExtraSize = std::min(OutSizeLeft, InSizeLeft);
|
||||
size_t ExtraSize = Rand(MaxExtraSize) + 1;
|
||||
memcpy(Out + OutPos, Data + *InPos, ExtraSize);
|
||||
OutPos += ExtraSize;
|
||||
(*InPos) += ExtraSize;
|
||||
}
|
||||
// Use the other input data on the next iteration.
|
||||
InPos = CurrentlyUsingFirstData ? &Pos2 : &Pos1;
|
||||
InSize = CurrentlyUsingFirstData ? Size2 : Size1;
|
||||
Data = CurrentlyUsingFirstData ? Data2 : Data1;
|
||||
CurrentlyUsingFirstData = !CurrentlyUsingFirstData;
|
||||
}
|
||||
return OutPos;
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
128
external/bsd/llvm/dist/llvm/lib/Fuzzer/FuzzerDefs.h
vendored
128
external/bsd/llvm/dist/llvm/lib/Fuzzer/FuzzerDefs.h
vendored
@ -1,128 +0,0 @@
|
||||
//===- FuzzerDefs.h - Internal header for the Fuzzer ------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Basic definitions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_DEFS_H
|
||||
#define LLVM_FUZZER_DEFS_H
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Platform detection.
|
||||
#ifdef __linux__
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_LINUX 1
|
||||
#define LIBFUZZER_WINDOWS 0
|
||||
#elif __APPLE__
|
||||
#define LIBFUZZER_APPLE 1
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_WINDOWS 0
|
||||
#elif _WIN32
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_WINDOWS 1
|
||||
#else
|
||||
#error "Support for your platform has not been implemented"
|
||||
#endif
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0
|
||||
#endif
|
||||
|
||||
#define LIBFUZZER_POSIX LIBFUZZER_APPLE || LIBFUZZER_LINUX
|
||||
|
||||
#ifdef __x86_64
|
||||
# if __has_attribute(target)
|
||||
# define ATTRIBUTE_TARGET_POPCNT __attribute__((target("popcnt")))
|
||||
# else
|
||||
# define ATTRIBUTE_TARGET_POPCNT
|
||||
# endif
|
||||
#else
|
||||
# define ATTRIBUTE_TARGET_POPCNT
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __clang__ // avoid gcc warning.
|
||||
# if __has_attribute(no_sanitize)
|
||||
# define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory")))
|
||||
# else
|
||||
# define ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
# endif
|
||||
# define ALWAYS_INLINE __attribute__((always_inline))
|
||||
#else
|
||||
# define ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
# define ALWAYS_INLINE
|
||||
#endif // __clang__
|
||||
|
||||
#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
|
||||
|
||||
#if defined(__has_feature)
|
||||
# if __has_feature(address_sanitizer)
|
||||
# define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_ADDRESS
|
||||
# elif __has_feature(memory_sanitizer)
|
||||
# define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
# else
|
||||
# define ATTRIBUTE_NO_SANITIZE_ALL
|
||||
# endif
|
||||
#else
|
||||
# define ATTRIBUTE_NO_SANITIZE_ALL
|
||||
#endif
|
||||
|
||||
#if LIBFUZZER_WINDOWS
|
||||
#define ATTRIBUTE_INTERFACE __declspec(dllexport)
|
||||
#else
|
||||
#define ATTRIBUTE_INTERFACE __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
template <class T> T Min(T a, T b) { return a < b ? a : b; }
|
||||
template <class T> T Max(T a, T b) { return a > b ? a : b; }
|
||||
|
||||
class Random;
|
||||
class Dictionary;
|
||||
class DictionaryEntry;
|
||||
class MutationDispatcher;
|
||||
struct FuzzingOptions;
|
||||
class InputCorpus;
|
||||
struct InputInfo;
|
||||
struct ExternalFunctions;
|
||||
|
||||
// Global interface to functions that may or may not be available.
|
||||
extern ExternalFunctions *EF;
|
||||
|
||||
typedef std::vector<uint8_t> Unit;
|
||||
typedef std::vector<Unit> UnitVector;
|
||||
typedef int (*UserCallback)(const uint8_t *Data, size_t Size);
|
||||
|
||||
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback);
|
||||
|
||||
struct ScopedDoingMyOwnMemOrStr {
|
||||
ScopedDoingMyOwnMemOrStr() { DoingMyOwnMemOrStr++; }
|
||||
~ScopedDoingMyOwnMemOrStr() { DoingMyOwnMemOrStr--; }
|
||||
static int DoingMyOwnMemOrStr;
|
||||
};
|
||||
|
||||
inline uint8_t Bswap(uint8_t x) { return x; }
|
||||
inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); }
|
||||
inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); }
|
||||
inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); }
|
||||
|
||||
uint8_t *ExtraCountersBegin();
|
||||
uint8_t *ExtraCountersEnd();
|
||||
void ClearExtraCounters();
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_DEFS_H
|
@ -1,127 +0,0 @@
|
||||
//===- FuzzerDictionary.h - Internal header for the Fuzzer ------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::Dictionary
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_DICTIONARY_H
|
||||
#define LLVM_FUZZER_DICTIONARY_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerUtil.h"
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace fuzzer {
|
||||
// A simple POD sized array of bytes.
|
||||
template <size_t kMaxSizeT> class FixedWord {
|
||||
public:
|
||||
static const size_t kMaxSize = kMaxSizeT;
|
||||
FixedWord() {}
|
||||
FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); }
|
||||
|
||||
void Set(const uint8_t *B, uint8_t S) {
|
||||
assert(S <= kMaxSize);
|
||||
memcpy(Data, B, S);
|
||||
Size = S;
|
||||
}
|
||||
|
||||
bool operator==(const FixedWord<kMaxSize> &w) const {
|
||||
ScopedDoingMyOwnMemOrStr scoped_doing_my_own_mem_os_str;
|
||||
return Size == w.Size && 0 == memcmp(Data, w.Data, Size);
|
||||
}
|
||||
|
||||
bool operator<(const FixedWord<kMaxSize> &w) const {
|
||||
ScopedDoingMyOwnMemOrStr scoped_doing_my_own_mem_os_str;
|
||||
if (Size != w.Size)
|
||||
return Size < w.Size;
|
||||
return memcmp(Data, w.Data, Size) < 0;
|
||||
}
|
||||
|
||||
static size_t GetMaxSize() { return kMaxSize; }
|
||||
const uint8_t *data() const { return Data; }
|
||||
uint8_t size() const { return Size; }
|
||||
|
||||
private:
|
||||
uint8_t Size = 0;
|
||||
uint8_t Data[kMaxSize];
|
||||
};
|
||||
|
||||
typedef FixedWord<64> Word;
|
||||
|
||||
class DictionaryEntry {
|
||||
public:
|
||||
DictionaryEntry() {}
|
||||
DictionaryEntry(Word W) : W(W) {}
|
||||
DictionaryEntry(Word W, size_t PositionHint) : W(W), PositionHint(PositionHint) {}
|
||||
const Word &GetW() const { return W; }
|
||||
|
||||
bool HasPositionHint() const { return PositionHint != std::numeric_limits<size_t>::max(); }
|
||||
size_t GetPositionHint() const {
|
||||
assert(HasPositionHint());
|
||||
return PositionHint;
|
||||
}
|
||||
void IncUseCount() { UseCount++; }
|
||||
void IncSuccessCount() { SuccessCount++; }
|
||||
size_t GetUseCount() const { return UseCount; }
|
||||
size_t GetSuccessCount() const {return SuccessCount; }
|
||||
|
||||
void Print(const char *PrintAfter = "\n") {
|
||||
PrintASCII(W.data(), W.size());
|
||||
if (HasPositionHint())
|
||||
Printf("@%zd", GetPositionHint());
|
||||
Printf("%s", PrintAfter);
|
||||
}
|
||||
|
||||
private:
|
||||
Word W;
|
||||
size_t PositionHint = std::numeric_limits<size_t>::max();
|
||||
size_t UseCount = 0;
|
||||
size_t SuccessCount = 0;
|
||||
};
|
||||
|
||||
class Dictionary {
|
||||
public:
|
||||
static const size_t kMaxDictSize = 1 << 14;
|
||||
|
||||
bool ContainsWord(const Word &W) const {
|
||||
return std::any_of(begin(), end(), [&](const DictionaryEntry &DE) {
|
||||
return DE.GetW() == W;
|
||||
});
|
||||
}
|
||||
const DictionaryEntry *begin() const { return &DE[0]; }
|
||||
const DictionaryEntry *end() const { return begin() + Size; }
|
||||
DictionaryEntry & operator[] (size_t Idx) {
|
||||
assert(Idx < Size);
|
||||
return DE[Idx];
|
||||
}
|
||||
void push_back(DictionaryEntry DE) {
|
||||
if (Size < kMaxDictSize)
|
||||
this->DE[Size++] = DE;
|
||||
}
|
||||
void clear() { Size = 0; }
|
||||
bool empty() const { return Size == 0; }
|
||||
size_t size() const { return Size; }
|
||||
|
||||
private:
|
||||
DictionaryEntry DE[kMaxDictSize];
|
||||
size_t Size = 0;
|
||||
};
|
||||
|
||||
// Parses one dictionary entry.
|
||||
// If successfull, write the enty to Unit and returns true,
|
||||
// otherwise returns false.
|
||||
bool ParseOneDictionaryEntry(const std::string &Str, Unit *U);
|
||||
// Parses the dictionary file, fills Units, returns true iff all lines
|
||||
// were parsed succesfully.
|
||||
bool ParseDictionaryFile(const std::string &Text, std::vector<Unit> *Units);
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_DICTIONARY_H
|
@ -1,763 +0,0 @@
|
||||
//===- FuzzerDriver.cpp - FuzzerDriver function and flags -----------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// FuzzerDriver and flag parsing.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerInterface.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "FuzzerShmem.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// This function should be present in the libFuzzer so that the client
|
||||
// binary can test for its existence.
|
||||
extern "C" __attribute__((used)) void __libfuzzer_is_present() {}
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
// Program arguments.
|
||||
struct FlagDescription {
|
||||
const char *Name;
|
||||
const char *Description;
|
||||
int Default;
|
||||
int *IntFlag;
|
||||
const char **StrFlag;
|
||||
unsigned int *UIntFlag;
|
||||
};
|
||||
|
||||
struct {
|
||||
#define FUZZER_DEPRECATED_FLAG(Name)
|
||||
#define FUZZER_FLAG_INT(Name, Default, Description) int Name;
|
||||
#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) unsigned int Name;
|
||||
#define FUZZER_FLAG_STRING(Name, Description) const char *Name;
|
||||
#include "FuzzerFlags.def"
|
||||
#undef FUZZER_DEPRECATED_FLAG
|
||||
#undef FUZZER_FLAG_INT
|
||||
#undef FUZZER_FLAG_UNSIGNED
|
||||
#undef FUZZER_FLAG_STRING
|
||||
} Flags;
|
||||
|
||||
static const FlagDescription FlagDescriptions [] {
|
||||
#define FUZZER_DEPRECATED_FLAG(Name) \
|
||||
{#Name, "Deprecated; don't use", 0, nullptr, nullptr, nullptr},
|
||||
#define FUZZER_FLAG_INT(Name, Default, Description) \
|
||||
{#Name, Description, Default, &Flags.Name, nullptr, nullptr},
|
||||
#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) \
|
||||
{#Name, Description, static_cast<int>(Default), \
|
||||
nullptr, nullptr, &Flags.Name},
|
||||
#define FUZZER_FLAG_STRING(Name, Description) \
|
||||
{#Name, Description, 0, nullptr, &Flags.Name, nullptr},
|
||||
#include "FuzzerFlags.def"
|
||||
#undef FUZZER_DEPRECATED_FLAG
|
||||
#undef FUZZER_FLAG_INT
|
||||
#undef FUZZER_FLAG_UNSIGNED
|
||||
#undef FUZZER_FLAG_STRING
|
||||
};
|
||||
|
||||
static const size_t kNumFlags =
|
||||
sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]);
|
||||
|
||||
static std::vector<std::string> *Inputs;
|
||||
static std::string *ProgName;
|
||||
|
||||
static void PrintHelp() {
|
||||
Printf("Usage:\n");
|
||||
auto Prog = ProgName->c_str();
|
||||
Printf("\nTo run fuzzing pass 0 or more directories.\n");
|
||||
Printf("%s [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n", Prog);
|
||||
|
||||
Printf("\nTo run individual tests without fuzzing pass 1 or more files:\n");
|
||||
Printf("%s [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]\n", Prog);
|
||||
|
||||
Printf("\nFlags: (strictly in form -flag=value)\n");
|
||||
size_t MaxFlagLen = 0;
|
||||
for (size_t F = 0; F < kNumFlags; F++)
|
||||
MaxFlagLen = std::max(strlen(FlagDescriptions[F].Name), MaxFlagLen);
|
||||
|
||||
for (size_t F = 0; F < kNumFlags; F++) {
|
||||
const auto &D = FlagDescriptions[F];
|
||||
if (strstr(D.Description, "internal flag") == D.Description) continue;
|
||||
Printf(" %s", D.Name);
|
||||
for (size_t i = 0, n = MaxFlagLen - strlen(D.Name); i < n; i++)
|
||||
Printf(" ");
|
||||
Printf("\t");
|
||||
Printf("%d\t%s\n", D.Default, D.Description);
|
||||
}
|
||||
Printf("\nFlags starting with '--' will be ignored and "
|
||||
"will be passed verbatim to subprocesses.\n");
|
||||
}
|
||||
|
||||
static const char *FlagValue(const char *Param, const char *Name) {
|
||||
size_t Len = strlen(Name);
|
||||
if (Param[0] == '-' && strstr(Param + 1, Name) == Param + 1 &&
|
||||
Param[Len + 1] == '=')
|
||||
return &Param[Len + 2];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Avoid calling stol as it triggers a bug in clang/glibc build.
|
||||
static long MyStol(const char *Str) {
|
||||
long Res = 0;
|
||||
long Sign = 1;
|
||||
if (*Str == '-') {
|
||||
Str++;
|
||||
Sign = -1;
|
||||
}
|
||||
for (size_t i = 0; Str[i]; i++) {
|
||||
char Ch = Str[i];
|
||||
if (Ch < '0' || Ch > '9')
|
||||
return Res;
|
||||
Res = Res * 10 + (Ch - '0');
|
||||
}
|
||||
return Res * Sign;
|
||||
}
|
||||
|
||||
static bool ParseOneFlag(const char *Param) {
|
||||
if (Param[0] != '-') return false;
|
||||
if (Param[1] == '-') {
|
||||
static bool PrintedWarning = false;
|
||||
if (!PrintedWarning) {
|
||||
PrintedWarning = true;
|
||||
Printf("INFO: libFuzzer ignores flags that start with '--'\n");
|
||||
}
|
||||
for (size_t F = 0; F < kNumFlags; F++)
|
||||
if (FlagValue(Param + 1, FlagDescriptions[F].Name))
|
||||
Printf("WARNING: did you mean '%s' (single dash)?\n", Param + 1);
|
||||
return true;
|
||||
}
|
||||
for (size_t F = 0; F < kNumFlags; F++) {
|
||||
const char *Name = FlagDescriptions[F].Name;
|
||||
const char *Str = FlagValue(Param, Name);
|
||||
if (Str) {
|
||||
if (FlagDescriptions[F].IntFlag) {
|
||||
int Val = MyStol(Str);
|
||||
*FlagDescriptions[F].IntFlag = Val;
|
||||
if (Flags.verbosity >= 2)
|
||||
Printf("Flag: %s %d\n", Name, Val);
|
||||
return true;
|
||||
} else if (FlagDescriptions[F].UIntFlag) {
|
||||
unsigned int Val = std::stoul(Str);
|
||||
*FlagDescriptions[F].UIntFlag = Val;
|
||||
if (Flags.verbosity >= 2)
|
||||
Printf("Flag: %s %u\n", Name, Val);
|
||||
return true;
|
||||
} else if (FlagDescriptions[F].StrFlag) {
|
||||
*FlagDescriptions[F].StrFlag = Str;
|
||||
if (Flags.verbosity >= 2)
|
||||
Printf("Flag: %s %s\n", Name, Str);
|
||||
return true;
|
||||
} else { // Deprecated flag.
|
||||
Printf("Flag: %s: deprecated, don't use\n", Name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Printf("\n\nWARNING: unrecognized flag '%s'; "
|
||||
"use -help=1 to list all flags\n\n", Param);
|
||||
return true;
|
||||
}
|
||||
|
||||
// We don't use any library to minimize dependencies.
|
||||
static void ParseFlags(const std::vector<std::string> &Args) {
|
||||
for (size_t F = 0; F < kNumFlags; F++) {
|
||||
if (FlagDescriptions[F].IntFlag)
|
||||
*FlagDescriptions[F].IntFlag = FlagDescriptions[F].Default;
|
||||
if (FlagDescriptions[F].UIntFlag)
|
||||
*FlagDescriptions[F].UIntFlag =
|
||||
static_cast<unsigned int>(FlagDescriptions[F].Default);
|
||||
if (FlagDescriptions[F].StrFlag)
|
||||
*FlagDescriptions[F].StrFlag = nullptr;
|
||||
}
|
||||
Inputs = new std::vector<std::string>;
|
||||
for (size_t A = 1; A < Args.size(); A++) {
|
||||
if (ParseOneFlag(Args[A].c_str())) {
|
||||
if (Flags.ignore_remaining_args)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
Inputs->push_back(Args[A]);
|
||||
}
|
||||
}
|
||||
|
||||
static std::mutex Mu;
|
||||
|
||||
static void PulseThread() {
|
||||
while (true) {
|
||||
SleepSeconds(600);
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
Printf("pulse...\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void WorkerThread(const std::string &Cmd, std::atomic<unsigned> *Counter,
|
||||
unsigned NumJobs, std::atomic<bool> *HasErrors) {
|
||||
while (true) {
|
||||
unsigned C = (*Counter)++;
|
||||
if (C >= NumJobs) break;
|
||||
std::string Log = "fuzz-" + std::to_string(C) + ".log";
|
||||
std::string ToRun = Cmd + " > " + Log + " 2>&1\n";
|
||||
if (Flags.verbosity)
|
||||
Printf("%s", ToRun.c_str());
|
||||
int ExitCode = ExecuteCommand(ToRun);
|
||||
if (ExitCode != 0)
|
||||
*HasErrors = true;
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
Printf("================== Job %u exited with exit code %d ============\n",
|
||||
C, ExitCode);
|
||||
fuzzer::CopyFileToErr(Log);
|
||||
}
|
||||
}
|
||||
|
||||
std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
|
||||
const char *X1, const char *X2) {
|
||||
std::string Cmd;
|
||||
for (auto &S : Args) {
|
||||
if (FlagValue(S.c_str(), X1) || FlagValue(S.c_str(), X2))
|
||||
continue;
|
||||
Cmd += S + " ";
|
||||
}
|
||||
return Cmd;
|
||||
}
|
||||
|
||||
static int RunInMultipleProcesses(const std::vector<std::string> &Args,
|
||||
unsigned NumWorkers, unsigned NumJobs) {
|
||||
std::atomic<unsigned> Counter(0);
|
||||
std::atomic<bool> HasErrors(false);
|
||||
std::string Cmd = CloneArgsWithoutX(Args, "jobs", "workers");
|
||||
std::vector<std::thread> V;
|
||||
std::thread Pulse(PulseThread);
|
||||
Pulse.detach();
|
||||
for (unsigned i = 0; i < NumWorkers; i++)
|
||||
V.push_back(std::thread(WorkerThread, Cmd, &Counter, NumJobs, &HasErrors));
|
||||
for (auto &T : V)
|
||||
T.join();
|
||||
return HasErrors ? 1 : 0;
|
||||
}
|
||||
|
||||
static void RssThread(Fuzzer *F, size_t RssLimitMb) {
|
||||
while (true) {
|
||||
SleepSeconds(1);
|
||||
size_t Peak = GetPeakRSSMb();
|
||||
if (Peak > RssLimitMb)
|
||||
F->RssLimitCallback();
|
||||
}
|
||||
}
|
||||
|
||||
static void StartRssThread(Fuzzer *F, size_t RssLimitMb) {
|
||||
if (!RssLimitMb) return;
|
||||
std::thread T(RssThread, F, RssLimitMb);
|
||||
T.detach();
|
||||
}
|
||||
|
||||
int RunOneTest(Fuzzer *F, const char *InputFilePath, size_t MaxLen) {
|
||||
Unit U = FileToVector(InputFilePath);
|
||||
if (MaxLen && MaxLen < U.size())
|
||||
U.resize(MaxLen);
|
||||
F->ExecuteCallback(U.data(), U.size());
|
||||
F->TryDetectingAMemoryLeak(U.data(), U.size(), true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool AllInputsAreFiles() {
|
||||
if (Inputs->empty()) return false;
|
||||
for (auto &Path : *Inputs)
|
||||
if (!IsFile(Path))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string GetDedupTokenFromFile(const std::string &Path) {
|
||||
auto S = FileToString(Path);
|
||||
auto Beg = S.find("DEDUP_TOKEN:");
|
||||
if (Beg == std::string::npos)
|
||||
return "";
|
||||
auto End = S.find('\n', Beg);
|
||||
if (End == std::string::npos)
|
||||
return "";
|
||||
return S.substr(Beg, End - Beg);
|
||||
}
|
||||
|
||||
int CleanseCrashInput(const std::vector<std::string> &Args,
|
||||
const FuzzingOptions &Options) {
|
||||
if (Inputs->size() != 1 || !Flags.exact_artifact_path) {
|
||||
Printf("ERROR: -cleanse_crash should be given one input file and"
|
||||
" -exact_artifact_path\n");
|
||||
exit(1);
|
||||
}
|
||||
std::string InputFilePath = Inputs->at(0);
|
||||
std::string OutputFilePath = Flags.exact_artifact_path;
|
||||
std::string BaseCmd =
|
||||
CloneArgsWithoutX(Args, "cleanse_crash", "cleanse_crash");
|
||||
|
||||
auto InputPos = BaseCmd.find(" " + InputFilePath + " ");
|
||||
assert(InputPos != std::string::npos);
|
||||
BaseCmd.erase(InputPos, InputFilePath.size() + 1);
|
||||
|
||||
auto LogFilePath = DirPlusFile(
|
||||
TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
|
||||
auto TmpFilePath = DirPlusFile(
|
||||
TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".repro");
|
||||
auto LogFileRedirect = " > " + LogFilePath + " 2>&1 ";
|
||||
|
||||
auto Cmd = BaseCmd + " " + TmpFilePath + LogFileRedirect;
|
||||
|
||||
std::string CurrentFilePath = InputFilePath;
|
||||
auto U = FileToVector(CurrentFilePath);
|
||||
size_t Size = U.size();
|
||||
|
||||
const std::vector<uint8_t> ReplacementBytes = {' ', 0xff};
|
||||
for (int NumAttempts = 0; NumAttempts < 5; NumAttempts++) {
|
||||
bool Changed = false;
|
||||
for (size_t Idx = 0; Idx < Size; Idx++) {
|
||||
Printf("CLEANSE[%d]: Trying to replace byte %zd of %zd\n", NumAttempts,
|
||||
Idx, Size);
|
||||
uint8_t OriginalByte = U[Idx];
|
||||
if (ReplacementBytes.end() != std::find(ReplacementBytes.begin(),
|
||||
ReplacementBytes.end(),
|
||||
OriginalByte))
|
||||
continue;
|
||||
for (auto NewByte : ReplacementBytes) {
|
||||
U[Idx] = NewByte;
|
||||
WriteToFile(U, TmpFilePath);
|
||||
auto ExitCode = ExecuteCommand(Cmd);
|
||||
RemoveFile(TmpFilePath);
|
||||
if (!ExitCode) {
|
||||
U[Idx] = OriginalByte;
|
||||
} else {
|
||||
Changed = true;
|
||||
Printf("CLEANSE: Replaced byte %zd with 0x%x\n", Idx, NewByte);
|
||||
WriteToFile(U, OutputFilePath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!Changed) break;
|
||||
}
|
||||
RemoveFile(LogFilePath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MinimizeCrashInput(const std::vector<std::string> &Args,
|
||||
const FuzzingOptions &Options) {
|
||||
if (Inputs->size() != 1) {
|
||||
Printf("ERROR: -minimize_crash should be given one input file\n");
|
||||
exit(1);
|
||||
}
|
||||
std::string InputFilePath = Inputs->at(0);
|
||||
auto BaseCmd = SplitBefore(
|
||||
"-ignore_remaining_args=1",
|
||||
CloneArgsWithoutX(Args, "minimize_crash", "exact_artifact_path"));
|
||||
auto InputPos = BaseCmd.first.find(" " + InputFilePath + " ");
|
||||
assert(InputPos != std::string::npos);
|
||||
BaseCmd.first.erase(InputPos, InputFilePath.size() + 1);
|
||||
if (Flags.runs <= 0 && Flags.max_total_time == 0) {
|
||||
Printf("INFO: you need to specify -runs=N or "
|
||||
"-max_total_time=N with -minimize_crash=1\n"
|
||||
"INFO: defaulting to -max_total_time=600\n");
|
||||
BaseCmd.first += " -max_total_time=600";
|
||||
}
|
||||
|
||||
auto LogFilePath = DirPlusFile(
|
||||
TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
|
||||
auto LogFileRedirect = " > " + LogFilePath + " 2>&1 ";
|
||||
|
||||
std::string CurrentFilePath = InputFilePath;
|
||||
while (true) {
|
||||
Unit U = FileToVector(CurrentFilePath);
|
||||
Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n",
|
||||
CurrentFilePath.c_str(), U.size());
|
||||
|
||||
auto Cmd = BaseCmd.first + " " + CurrentFilePath + LogFileRedirect + " " +
|
||||
BaseCmd.second;
|
||||
|
||||
Printf("CRASH_MIN: executing: %s\n", Cmd.c_str());
|
||||
int ExitCode = ExecuteCommand(Cmd);
|
||||
if (ExitCode == 0) {
|
||||
Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str());
|
||||
exit(1);
|
||||
}
|
||||
Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize "
|
||||
"it further\n",
|
||||
CurrentFilePath.c_str(), U.size());
|
||||
auto DedupToken1 = GetDedupTokenFromFile(LogFilePath);
|
||||
if (!DedupToken1.empty())
|
||||
Printf("CRASH_MIN: DedupToken1: %s\n", DedupToken1.c_str());
|
||||
|
||||
std::string ArtifactPath =
|
||||
Flags.exact_artifact_path
|
||||
? Flags.exact_artifact_path
|
||||
: Options.ArtifactPrefix + "minimized-from-" + Hash(U);
|
||||
Cmd += " -minimize_crash_internal_step=1 -exact_artifact_path=" +
|
||||
ArtifactPath;
|
||||
Printf("CRASH_MIN: executing: %s\n", Cmd.c_str());
|
||||
ExitCode = ExecuteCommand(Cmd);
|
||||
CopyFileToErr(LogFilePath);
|
||||
if (ExitCode == 0) {
|
||||
if (Flags.exact_artifact_path) {
|
||||
CurrentFilePath = Flags.exact_artifact_path;
|
||||
WriteToFile(U, CurrentFilePath);
|
||||
}
|
||||
Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n",
|
||||
CurrentFilePath.c_str(), U.size());
|
||||
break;
|
||||
}
|
||||
auto DedupToken2 = GetDedupTokenFromFile(LogFilePath);
|
||||
if (!DedupToken2.empty())
|
||||
Printf("CRASH_MIN: DedupToken2: %s\n", DedupToken2.c_str());
|
||||
|
||||
if (DedupToken1 != DedupToken2) {
|
||||
if (Flags.exact_artifact_path) {
|
||||
CurrentFilePath = Flags.exact_artifact_path;
|
||||
WriteToFile(U, CurrentFilePath);
|
||||
}
|
||||
Printf("CRASH_MIN: mismatch in dedup tokens"
|
||||
" (looks like a different bug). Won't minimize further\n");
|
||||
break;
|
||||
}
|
||||
|
||||
CurrentFilePath = ArtifactPath;
|
||||
Printf("*********************************\n");
|
||||
}
|
||||
RemoveFile(LogFilePath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
|
||||
assert(Inputs->size() == 1);
|
||||
std::string InputFilePath = Inputs->at(0);
|
||||
Unit U = FileToVector(InputFilePath);
|
||||
Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size());
|
||||
if (U.size() < 2) {
|
||||
Printf("INFO: The input is small enough, exiting\n");
|
||||
exit(0);
|
||||
}
|
||||
F->SetMaxInputLen(U.size());
|
||||
F->SetMaxMutationLen(U.size() - 1);
|
||||
F->MinimizeCrashLoop(U);
|
||||
Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n");
|
||||
exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AnalyzeDictionary(Fuzzer *F, const std::vector<Unit>& Dict,
|
||||
UnitVector& Corpus) {
|
||||
Printf("Started dictionary minimization (up to %d tests)\n",
|
||||
Dict.size() * Corpus.size() * 2);
|
||||
|
||||
// Scores and usage count for each dictionary unit.
|
||||
std::vector<int> Scores(Dict.size());
|
||||
std::vector<int> Usages(Dict.size());
|
||||
|
||||
std::vector<size_t> InitialFeatures;
|
||||
std::vector<size_t> ModifiedFeatures;
|
||||
for (auto &C : Corpus) {
|
||||
// Get coverage for the testcase without modifications.
|
||||
F->ExecuteCallback(C.data(), C.size());
|
||||
InitialFeatures.clear();
|
||||
TPC.CollectFeatures([&](size_t Feature) -> bool {
|
||||
InitialFeatures.push_back(Feature);
|
||||
return true;
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < Dict.size(); ++i) {
|
||||
auto Data = C;
|
||||
auto StartPos = std::search(Data.begin(), Data.end(),
|
||||
Dict[i].begin(), Dict[i].end());
|
||||
// Skip dictionary unit, if the testcase does not contain it.
|
||||
if (StartPos == Data.end())
|
||||
continue;
|
||||
|
||||
++Usages[i];
|
||||
while (StartPos != Data.end()) {
|
||||
// Replace all occurrences of dictionary unit in the testcase.
|
||||
auto EndPos = StartPos + Dict[i].size();
|
||||
for (auto It = StartPos; It != EndPos; ++It)
|
||||
*It ^= 0xFF;
|
||||
|
||||
StartPos = std::search(EndPos, Data.end(),
|
||||
Dict[i].begin(), Dict[i].end());
|
||||
}
|
||||
|
||||
// Get coverage for testcase with masked occurrences of dictionary unit.
|
||||
F->ExecuteCallback(Data.data(), Data.size());
|
||||
ModifiedFeatures.clear();
|
||||
TPC.CollectFeatures([&](size_t Feature) -> bool {
|
||||
ModifiedFeatures.push_back(Feature);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (InitialFeatures == ModifiedFeatures)
|
||||
--Scores[i];
|
||||
else
|
||||
Scores[i] += 2;
|
||||
}
|
||||
}
|
||||
|
||||
Printf("###### Useless dictionary elements. ######\n");
|
||||
for (size_t i = 0; i < Dict.size(); ++i) {
|
||||
// Dictionary units with positive score are treated as useful ones.
|
||||
if (Scores[i] > 0)
|
||||
continue;
|
||||
|
||||
Printf("\"");
|
||||
PrintASCII(Dict[i].data(), Dict[i].size(), "\"");
|
||||
Printf(" # Score: %d, Used: %d\n", Scores[i], Usages[i]);
|
||||
}
|
||||
Printf("###### End of useless dictionary elements. ######\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
|
||||
using namespace fuzzer;
|
||||
assert(argc && argv && "Argument pointers cannot be nullptr");
|
||||
std::string Argv0((*argv)[0]);
|
||||
EF = new ExternalFunctions();
|
||||
if (EF->LLVMFuzzerInitialize)
|
||||
EF->LLVMFuzzerInitialize(argc, argv);
|
||||
const std::vector<std::string> Args(*argv, *argv + *argc);
|
||||
assert(!Args.empty());
|
||||
ProgName = new std::string(Args[0]);
|
||||
if (Argv0 != *ProgName) {
|
||||
Printf("ERROR: argv[0] has been modified in LLVMFuzzerInitialize\n");
|
||||
exit(1);
|
||||
}
|
||||
ParseFlags(Args);
|
||||
if (Flags.help) {
|
||||
PrintHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Flags.close_fd_mask & 2)
|
||||
DupAndCloseStderr();
|
||||
if (Flags.close_fd_mask & 1)
|
||||
CloseStdout();
|
||||
|
||||
if (Flags.jobs > 0 && Flags.workers == 0) {
|
||||
Flags.workers = std::min(NumberOfCpuCores() / 2, Flags.jobs);
|
||||
if (Flags.workers > 1)
|
||||
Printf("Running %u workers\n", Flags.workers);
|
||||
}
|
||||
|
||||
if (Flags.workers > 0 && Flags.jobs > 0)
|
||||
return RunInMultipleProcesses(Args, Flags.workers, Flags.jobs);
|
||||
|
||||
const size_t kMaxSaneLen = 1 << 20;
|
||||
const size_t kMinDefaultLen = 4096;
|
||||
FuzzingOptions Options;
|
||||
Options.Verbosity = Flags.verbosity;
|
||||
Options.MaxLen = Flags.max_len;
|
||||
Options.ExperimentalLenControl = Flags.experimental_len_control;
|
||||
if (Flags.experimental_len_control && Flags.max_len == kMinDefaultLen)
|
||||
Options.MaxLen = 1 << 20;
|
||||
Options.UnitTimeoutSec = Flags.timeout;
|
||||
Options.ErrorExitCode = Flags.error_exitcode;
|
||||
Options.TimeoutExitCode = Flags.timeout_exitcode;
|
||||
Options.MaxTotalTimeSec = Flags.max_total_time;
|
||||
Options.DoCrossOver = Flags.cross_over;
|
||||
Options.MutateDepth = Flags.mutate_depth;
|
||||
Options.UseCounters = Flags.use_counters;
|
||||
Options.UseIndirCalls = Flags.use_indir_calls;
|
||||
Options.UseMemmem = Flags.use_memmem;
|
||||
Options.UseCmp = Flags.use_cmp;
|
||||
Options.UseValueProfile = Flags.use_value_profile;
|
||||
Options.Shrink = Flags.shrink;
|
||||
Options.ReduceInputs = Flags.reduce_inputs;
|
||||
Options.ShuffleAtStartUp = Flags.shuffle;
|
||||
Options.PreferSmall = Flags.prefer_small;
|
||||
Options.ReloadIntervalSec = Flags.reload;
|
||||
Options.OnlyASCII = Flags.only_ascii;
|
||||
Options.DetectLeaks = Flags.detect_leaks;
|
||||
Options.TraceMalloc = Flags.trace_malloc;
|
||||
Options.RssLimitMb = Flags.rss_limit_mb;
|
||||
if (Flags.runs >= 0)
|
||||
Options.MaxNumberOfRuns = Flags.runs;
|
||||
if (!Inputs->empty() && !Flags.minimize_crash_internal_step)
|
||||
Options.OutputCorpus = (*Inputs)[0];
|
||||
Options.ReportSlowUnits = Flags.report_slow_units;
|
||||
if (Flags.artifact_prefix)
|
||||
Options.ArtifactPrefix = Flags.artifact_prefix;
|
||||
if (Flags.exact_artifact_path)
|
||||
Options.ExactArtifactPath = Flags.exact_artifact_path;
|
||||
std::vector<Unit> Dictionary;
|
||||
if (Flags.dict)
|
||||
if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary))
|
||||
return 1;
|
||||
if (Flags.verbosity > 0 && !Dictionary.empty())
|
||||
Printf("Dictionary: %zd entries\n", Dictionary.size());
|
||||
bool DoPlainRun = AllInputsAreFiles();
|
||||
Options.SaveArtifacts =
|
||||
!DoPlainRun || Flags.minimize_crash_internal_step;
|
||||
Options.PrintNewCovPcs = Flags.print_pcs;
|
||||
Options.PrintFinalStats = Flags.print_final_stats;
|
||||
Options.PrintCorpusStats = Flags.print_corpus_stats;
|
||||
Options.PrintCoverage = Flags.print_coverage;
|
||||
Options.DumpCoverage = Flags.dump_coverage;
|
||||
if (Flags.exit_on_src_pos)
|
||||
Options.ExitOnSrcPos = Flags.exit_on_src_pos;
|
||||
if (Flags.exit_on_item)
|
||||
Options.ExitOnItem = Flags.exit_on_item;
|
||||
|
||||
unsigned Seed = Flags.seed;
|
||||
// Initialize Seed.
|
||||
if (Seed == 0)
|
||||
Seed =
|
||||
std::chrono::system_clock::now().time_since_epoch().count() + GetPid();
|
||||
if (Flags.verbosity)
|
||||
Printf("INFO: Seed: %u\n", Seed);
|
||||
|
||||
Random Rand(Seed);
|
||||
auto *MD = new MutationDispatcher(Rand, Options);
|
||||
auto *Corpus = new InputCorpus(Options.OutputCorpus);
|
||||
auto *F = new Fuzzer(Callback, *Corpus, *MD, Options);
|
||||
|
||||
for (auto &U: Dictionary)
|
||||
if (U.size() <= Word::GetMaxSize())
|
||||
MD->AddWordToManualDictionary(Word(U.data(), U.size()));
|
||||
|
||||
StartRssThread(F, Flags.rss_limit_mb);
|
||||
|
||||
Options.HandleAbrt = Flags.handle_abrt;
|
||||
Options.HandleBus = Flags.handle_bus;
|
||||
Options.HandleFpe = Flags.handle_fpe;
|
||||
Options.HandleIll = Flags.handle_ill;
|
||||
Options.HandleInt = Flags.handle_int;
|
||||
Options.HandleSegv = Flags.handle_segv;
|
||||
Options.HandleTerm = Flags.handle_term;
|
||||
Options.HandleXfsz = Flags.handle_xfsz;
|
||||
SetSignalHandler(Options);
|
||||
|
||||
if (Flags.minimize_crash)
|
||||
return MinimizeCrashInput(Args, Options);
|
||||
|
||||
if (Flags.minimize_crash_internal_step)
|
||||
return MinimizeCrashInputInternalStep(F, Corpus);
|
||||
|
||||
if (Flags.cleanse_crash)
|
||||
return CleanseCrashInput(Args, Options);
|
||||
|
||||
if (auto Name = Flags.run_equivalence_server) {
|
||||
SMR.Destroy(Name);
|
||||
if (!SMR.Create(Name)) {
|
||||
Printf("ERROR: can't create shared memory region\n");
|
||||
return 1;
|
||||
}
|
||||
Printf("INFO: EQUIVALENCE SERVER UP\n");
|
||||
while (true) {
|
||||
SMR.WaitClient();
|
||||
size_t Size = SMR.ReadByteArraySize();
|
||||
SMR.WriteByteArray(nullptr, 0);
|
||||
const Unit tmp(SMR.GetByteArray(), SMR.GetByteArray() + Size);
|
||||
F->ExecuteCallback(tmp.data(), tmp.size());
|
||||
SMR.PostServer();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (auto Name = Flags.use_equivalence_server) {
|
||||
if (!SMR.Open(Name)) {
|
||||
Printf("ERROR: can't open shared memory region\n");
|
||||
return 1;
|
||||
}
|
||||
Printf("INFO: EQUIVALENCE CLIENT UP\n");
|
||||
}
|
||||
|
||||
if (DoPlainRun) {
|
||||
Options.SaveArtifacts = false;
|
||||
int Runs = std::max(1, Flags.runs);
|
||||
Printf("%s: Running %zd inputs %d time(s) each.\n", ProgName->c_str(),
|
||||
Inputs->size(), Runs);
|
||||
for (auto &Path : *Inputs) {
|
||||
auto StartTime = system_clock::now();
|
||||
Printf("Running: %s\n", Path.c_str());
|
||||
for (int Iter = 0; Iter < Runs; Iter++)
|
||||
RunOneTest(F, Path.c_str(), Options.MaxLen);
|
||||
auto StopTime = system_clock::now();
|
||||
auto MS = duration_cast<milliseconds>(StopTime - StartTime).count();
|
||||
Printf("Executed %s in %zd ms\n", Path.c_str(), (long)MS);
|
||||
}
|
||||
Printf("***\n"
|
||||
"*** NOTE: fuzzing was not performed, you have only\n"
|
||||
"*** executed the target code on a fixed set of inputs.\n"
|
||||
"***\n");
|
||||
F->PrintFinalStats();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (Flags.merge) {
|
||||
if (Options.MaxLen == 0)
|
||||
F->SetMaxInputLen(kMaxSaneLen);
|
||||
if (Flags.merge_control_file)
|
||||
F->CrashResistantMergeInternalStep(Flags.merge_control_file);
|
||||
else
|
||||
F->CrashResistantMerge(Args, *Inputs,
|
||||
Flags.load_coverage_summary,
|
||||
Flags.save_coverage_summary);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
size_t TemporaryMaxLen = Options.MaxLen ? Options.MaxLen : kMaxSaneLen;
|
||||
|
||||
UnitVector InitialCorpus;
|
||||
for (auto &Inp : *Inputs) {
|
||||
Printf("Loading corpus dir: %s\n", Inp.c_str());
|
||||
ReadDirToVectorOfUnits(Inp.c_str(), &InitialCorpus, nullptr,
|
||||
TemporaryMaxLen, /*ExitOnError=*/false);
|
||||
}
|
||||
|
||||
if (Flags.analyze_dict) {
|
||||
if (Dictionary.empty() || Inputs->empty()) {
|
||||
Printf("ERROR: can't analyze dict without dict and corpus provided\n");
|
||||
return 1;
|
||||
}
|
||||
if (AnalyzeDictionary(F, Dictionary, InitialCorpus)) {
|
||||
Printf("Dictionary analysis failed\n");
|
||||
exit(1);
|
||||
}
|
||||
Printf("Dictionary analysis suceeded\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (Options.MaxLen == 0) {
|
||||
size_t MaxLen = 0;
|
||||
for (auto &U : InitialCorpus)
|
||||
MaxLen = std::max(U.size(), MaxLen);
|
||||
F->SetMaxInputLen(std::min(std::max(kMinDefaultLen, MaxLen), kMaxSaneLen));
|
||||
}
|
||||
|
||||
if (InitialCorpus.empty()) {
|
||||
InitialCorpus.push_back(Unit({'\n'})); // Valid ASCII input.
|
||||
if (Options.Verbosity)
|
||||
Printf("INFO: A corpus is not provided, starting from an empty corpus\n");
|
||||
}
|
||||
F->ShuffleAndMinimize(&InitialCorpus);
|
||||
InitialCorpus.clear(); // Don't need this memory any more.
|
||||
F->Loop();
|
||||
|
||||
if (Flags.verbosity)
|
||||
Printf("Done %zd runs in %zd second(s)\n", F->getTotalNumberOfRuns(),
|
||||
F->secondsSinceProcessStartUp());
|
||||
F->PrintFinalStats();
|
||||
|
||||
exit(0); // Don't let F destroy itself.
|
||||
}
|
||||
|
||||
// Storage for global ExternalFunctions object.
|
||||
ExternalFunctions *EF = nullptr;
|
||||
|
||||
} // namespace fuzzer
|
@ -1,46 +0,0 @@
|
||||
//===- FuzzerExtFunctions.def - External functions --------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// This defines the external function pointers that
|
||||
// ``fuzzer::ExternalFunctions`` should contain and try to initialize. The
|
||||
// EXT_FUNC macro must be defined at the point of inclusion. The signature of
|
||||
// the macro is:
|
||||
//
|
||||
// EXT_FUNC(<name>, <return_type>, <function_signature>, <warn_if_missing>)
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Optional user functions
|
||||
EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false);
|
||||
EXT_FUNC(LLVMFuzzerCustomMutator, size_t,
|
||||
(uint8_t * Data, size_t Size, size_t MaxSize, unsigned int Seed),
|
||||
false);
|
||||
EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t,
|
||||
(const uint8_t * Data1, size_t Size1,
|
||||
const uint8_t * Data2, size_t Size2,
|
||||
uint8_t * Out, size_t MaxOutSize, unsigned int Seed),
|
||||
false);
|
||||
|
||||
// Sanitizer functions
|
||||
EXT_FUNC(__lsan_enable, void, (), false);
|
||||
EXT_FUNC(__lsan_disable, void, (), false);
|
||||
EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false);
|
||||
EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int,
|
||||
(void (*malloc_hook)(const volatile void *, size_t),
|
||||
void (*free_hook)(const volatile void *)),
|
||||
false);
|
||||
EXT_FUNC(__sanitizer_print_memory_profile, int, (size_t, size_t), false);
|
||||
EXT_FUNC(__sanitizer_print_stack_trace, void, (), true);
|
||||
EXT_FUNC(__sanitizer_symbolize_pc, void,
|
||||
(void *, const char *fmt, char *out_buf, size_t out_buf_size), false);
|
||||
EXT_FUNC(__sanitizer_get_module_and_offset_for_pc, int,
|
||||
(void *pc, char *module_path,
|
||||
size_t module_path_len,void **pc_offset), false);
|
||||
EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true);
|
||||
EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false);
|
||||
EXT_FUNC(__sanitizer_dump_coverage, void, (const uintptr_t *, uintptr_t),
|
||||
false);
|
@ -1,35 +0,0 @@
|
||||
//===- FuzzerExtFunctions.h - Interface to external functions ---*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Defines an interface to (possibly optional) functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_EXT_FUNCTIONS_H
|
||||
#define LLVM_FUZZER_EXT_FUNCTIONS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
struct ExternalFunctions {
|
||||
// Initialize function pointers. Functions that are not available will be set
|
||||
// to nullptr. Do not call this constructor before ``main()`` has been
|
||||
// entered.
|
||||
ExternalFunctions();
|
||||
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
RETURN_TYPE(*NAME) FUNC_SIG = nullptr
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
};
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif
|
@ -1,52 +0,0 @@
|
||||
//===- FuzzerExtFunctionsDlsym.cpp - Interface to external functions ------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Implementation for operating systems that support dlsym(). We only use it on
|
||||
// Apple platforms for now. We don't use this approach on Linux because it
|
||||
// requires that clients of LibFuzzer pass ``--export-dynamic`` to the linker.
|
||||
// That is a complication we don't wish to expose to clients right now.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_APPLE
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include <dlfcn.h>
|
||||
|
||||
using namespace fuzzer;
|
||||
|
||||
template <typename T>
|
||||
static T GetFnPtr(const char *FnName, bool WarnIfMissing) {
|
||||
dlerror(); // Clear any previous errors.
|
||||
void *Fn = dlsym(RTLD_DEFAULT, FnName);
|
||||
if (Fn == nullptr) {
|
||||
if (WarnIfMissing) {
|
||||
const char *ErrorMsg = dlerror();
|
||||
Printf("WARNING: Failed to find function \"%s\".", FnName);
|
||||
if (ErrorMsg)
|
||||
Printf(" Reason %s.", ErrorMsg);
|
||||
Printf("\n");
|
||||
}
|
||||
}
|
||||
return reinterpret_cast<T>(Fn);
|
||||
}
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
ExternalFunctions::ExternalFunctions() {
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
this->NAME = GetFnPtr<decltype(ExternalFunctions::NAME)>(#NAME, WARN)
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_APPLE
|
@ -1,62 +0,0 @@
|
||||
//===- FuzzerExtFunctionsDlsymWin.cpp - Interface to external functions ---===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Implementation using dynamic loading for Windows.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_WINDOWS
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "Windows.h"
|
||||
|
||||
// This must be included after Windows.h.
|
||||
#include "Psapi.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
ExternalFunctions::ExternalFunctions() {
|
||||
HMODULE Modules[1024];
|
||||
DWORD BytesNeeded;
|
||||
HANDLE CurrentProcess = GetCurrentProcess();
|
||||
|
||||
if (!EnumProcessModules(CurrentProcess, Modules, sizeof(Modules),
|
||||
&BytesNeeded)) {
|
||||
Printf("EnumProcessModules failed (error: %d).\n", GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (sizeof(Modules) < BytesNeeded) {
|
||||
Printf("Error: the array is not big enough to hold all loaded modules.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < (BytesNeeded / sizeof(HMODULE)); i++)
|
||||
{
|
||||
FARPROC Fn;
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
if (this->NAME == nullptr) { \
|
||||
Fn = GetProcAddress(Modules[i], #NAME); \
|
||||
if (Fn == nullptr) \
|
||||
Fn = GetProcAddress(Modules[i], #NAME "__dll"); \
|
||||
this->NAME = (decltype(ExternalFunctions::NAME)) Fn; \
|
||||
}
|
||||
#include "FuzzerExtFunctions.def"
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
if (this->NAME == nullptr && WARN) \
|
||||
Printf("WARNING: Failed to find function \"%s\".\n", #NAME);
|
||||
#include "FuzzerExtFunctions.def"
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
@ -1,54 +0,0 @@
|
||||
//===- FuzzerExtFunctionsWeak.cpp - Interface to external functions -------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Implementation for Linux. This relies on the linker's support for weak
|
||||
// symbols. We don't use this approach on Apple platforms because it requires
|
||||
// clients of LibFuzzer to pass ``-U _<symbol_name>`` to the linker to allow
|
||||
// weak symbols to be undefined. That is a complication we don't want to expose
|
||||
// to clients right now.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_LINUX
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
|
||||
extern "C" {
|
||||
// Declare these symbols as weak to allow them to be optionally defined.
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
__attribute__((weak)) RETURN_TYPE NAME FUNC_SIG
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
using namespace fuzzer;
|
||||
|
||||
static void CheckFnPtr(void *FnPtr, const char *FnName, bool WarnIfMissing) {
|
||||
if (FnPtr == nullptr && WarnIfMissing) {
|
||||
Printf("WARNING: Failed to find function \"%s\".\n", FnName);
|
||||
}
|
||||
}
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
ExternalFunctions::ExternalFunctions() {
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
this->NAME = ::NAME; \
|
||||
CheckFnPtr(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(::NAME)), \
|
||||
#NAME, WARN);
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_LINUX
|
@ -1,56 +0,0 @@
|
||||
//===- FuzzerExtFunctionsWeakAlias.cpp - Interface to external functions --===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Implementation using weak aliases. Works for Windows.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_WINDOWS
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
|
||||
using namespace fuzzer;
|
||||
|
||||
extern "C" {
|
||||
// Declare these symbols as weak to allow them to be optionally defined.
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
RETURN_TYPE NAME##Def FUNC_SIG { \
|
||||
Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \
|
||||
exit(1); \
|
||||
} \
|
||||
RETURN_TYPE NAME FUNC_SIG __attribute__((weak, alias(#NAME "Def")));
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) {
|
||||
if (Fun == FunDef) {
|
||||
if (WarnIfMissing)
|
||||
Printf("WARNING: Failed to find function \"%s\".\n", FnName);
|
||||
return nullptr;
|
||||
}
|
||||
return Fun;
|
||||
}
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
ExternalFunctions::ExternalFunctions() {
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
this->NAME = GetFnPtr<decltype(::NAME)>(::NAME, ::NAME##Def, #NAME, WARN);
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
@ -1,41 +0,0 @@
|
||||
//===- FuzzerExtraCounters.cpp - Extra coverage counters ------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Extra coverage counters defined by user code.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
#if LIBFUZZER_LINUX
|
||||
__attribute__((weak)) extern uint8_t __start___libfuzzer_extra_counters;
|
||||
__attribute__((weak)) extern uint8_t __stop___libfuzzer_extra_counters;
|
||||
|
||||
namespace fuzzer {
|
||||
uint8_t *ExtraCountersBegin() { return &__start___libfuzzer_extra_counters; }
|
||||
uint8_t *ExtraCountersEnd() { return &__stop___libfuzzer_extra_counters; }
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void ClearExtraCounters() { // hand-written memset, don't asan-ify.
|
||||
uintptr_t *Beg = reinterpret_cast<uintptr_t*>(ExtraCountersBegin());
|
||||
uintptr_t *End = reinterpret_cast<uintptr_t*>(ExtraCountersEnd());
|
||||
for (; Beg < End; Beg++) {
|
||||
*Beg = 0;
|
||||
__asm__ __volatile__("" : : : "memory");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#else
|
||||
// TODO: implement for other platforms.
|
||||
namespace fuzzer {
|
||||
uint8_t *ExtraCountersBegin() { return nullptr; }
|
||||
uint8_t *ExtraCountersEnd() { return nullptr; }
|
||||
void ClearExtraCounters() {}
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif
|
@ -1,139 +0,0 @@
|
||||
//===- FuzzerFlags.def - Run-time flags -------------------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Flags. FUZZER_FLAG_INT/FUZZER_FLAG_STRING macros should be defined at the
|
||||
// point of inclusion. We are not using any flag parsing library for better
|
||||
// portability and independence.
|
||||
//===----------------------------------------------------------------------===//
|
||||
FUZZER_FLAG_INT(verbosity, 1, "Verbosity level.")
|
||||
FUZZER_FLAG_UNSIGNED(seed, 0, "Random seed. If 0, seed is generated.")
|
||||
FUZZER_FLAG_INT(runs, -1,
|
||||
"Number of individual test runs (-1 for infinite runs).")
|
||||
FUZZER_FLAG_INT(max_len, 0, "Maximum length of the test input. "
|
||||
"If 0, libFuzzer tries to guess a good value based on the corpus "
|
||||
"and reports it. ")
|
||||
FUZZER_FLAG_INT(experimental_len_control, 0, "experimental flag")
|
||||
FUZZER_FLAG_INT(cross_over, 1, "If 1, cross over inputs.")
|
||||
FUZZER_FLAG_INT(mutate_depth, 5,
|
||||
"Apply this number of consecutive mutations to each input.")
|
||||
FUZZER_FLAG_INT(shuffle, 1, "Shuffle inputs at startup")
|
||||
FUZZER_FLAG_INT(prefer_small, 1,
|
||||
"If 1, always prefer smaller inputs during the corpus shuffle.")
|
||||
FUZZER_FLAG_INT(
|
||||
timeout, 1200,
|
||||
"Timeout in seconds (if positive). "
|
||||
"If one unit runs more than this number of seconds the process will abort.")
|
||||
FUZZER_FLAG_INT(error_exitcode, 77, "When libFuzzer itself reports a bug "
|
||||
"this exit code will be used.")
|
||||
FUZZER_FLAG_INT(timeout_exitcode, 77, "When libFuzzer reports a timeout "
|
||||
"this exit code will be used.")
|
||||
FUZZER_FLAG_INT(max_total_time, 0, "If positive, indicates the maximal total "
|
||||
"time in seconds to run the fuzzer.")
|
||||
FUZZER_FLAG_INT(help, 0, "Print help.")
|
||||
FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be "
|
||||
"merged into the 1-st corpus. Only interesting units will be taken. "
|
||||
"This flag can be used to minimize a corpus.")
|
||||
FUZZER_FLAG_STRING(merge_control_file, "internal flag")
|
||||
FUZZER_FLAG_STRING(save_coverage_summary, "Experimental:"
|
||||
" save coverage summary to a given file."
|
||||
" Used with -merge=1")
|
||||
FUZZER_FLAG_STRING(load_coverage_summary, "Experimental:"
|
||||
" load coverage summary from a given file."
|
||||
" Treat this coverage as belonging to the first corpus. "
|
||||
" Used with -merge=1")
|
||||
FUZZER_FLAG_INT(minimize_crash, 0, "If 1, minimizes the provided"
|
||||
" crash input. Use with -runs=N or -max_total_time=N to limit "
|
||||
"the number attempts."
|
||||
" Use with -exact_artifact_path to specify the output."
|
||||
" Combine with ASAN_OPTIONS=dedup_token_length=3 (or similar) to ensure that"
|
||||
" the minimized input triggers the same crash."
|
||||
)
|
||||
FUZZER_FLAG_INT(cleanse_crash, 0, "If 1, tries to cleanse the provided"
|
||||
" crash input to make it contain fewer original bytes."
|
||||
" Use with -exact_artifact_path to specify the output."
|
||||
)
|
||||
FUZZER_FLAG_INT(minimize_crash_internal_step, 0, "internal flag")
|
||||
FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters")
|
||||
FUZZER_FLAG_INT(use_indir_calls, 1, "Use indirect caller-callee counters")
|
||||
FUZZER_FLAG_INT(use_memmem, 1,
|
||||
"Use hints from intercepting memmem, strstr, etc")
|
||||
FUZZER_FLAG_INT(use_value_profile, 0,
|
||||
"Experimental. Use value profile to guide fuzzing.")
|
||||
FUZZER_FLAG_INT(use_cmp, 1, "Use CMP traces to guide mutations")
|
||||
FUZZER_FLAG_INT(shrink, 0, "Experimental. Try to shrink corpus inputs.")
|
||||
FUZZER_FLAG_INT(reduce_inputs, 0, "Experimental. "
|
||||
"Try to reduce the size of inputs wile preserving their full feature sets")
|
||||
FUZZER_FLAG_UNSIGNED(jobs, 0, "Number of jobs to run. If jobs >= 1 we spawn"
|
||||
" this number of jobs in separate worker processes"
|
||||
" with stdout/stderr redirected to fuzz-JOB.log.")
|
||||
FUZZER_FLAG_UNSIGNED(workers, 0,
|
||||
"Number of simultaneous worker processes to run the jobs."
|
||||
" If zero, \"min(jobs,NumberOfCpuCores()/2)\" is used.")
|
||||
FUZZER_FLAG_INT(reload, 1,
|
||||
"Reload the main corpus every <N> seconds to get new units"
|
||||
" discovered by other processes. If 0, disabled")
|
||||
FUZZER_FLAG_INT(report_slow_units, 10,
|
||||
"Report slowest units if they run for more than this number of seconds.")
|
||||
FUZZER_FLAG_INT(only_ascii, 0,
|
||||
"If 1, generate only ASCII (isprint+isspace) inputs.")
|
||||
FUZZER_FLAG_STRING(dict, "Experimental. Use the dictionary file.")
|
||||
FUZZER_FLAG_STRING(artifact_prefix, "Write fuzzing artifacts (crash, "
|
||||
"timeout, or slow inputs) as "
|
||||
"$(artifact_prefix)file")
|
||||
FUZZER_FLAG_STRING(exact_artifact_path,
|
||||
"Write the single artifact on failure (crash, timeout) "
|
||||
"as $(exact_artifact_path). This overrides -artifact_prefix "
|
||||
"and will not use checksum in the file name. Do not "
|
||||
"use the same path for several parallel processes.")
|
||||
FUZZER_FLAG_INT(print_pcs, 0, "If 1, print out newly covered PCs.")
|
||||
FUZZER_FLAG_INT(print_final_stats, 0, "If 1, print statistics at exit.")
|
||||
FUZZER_FLAG_INT(print_corpus_stats, 0,
|
||||
"If 1, print statistics on corpus elements at exit.")
|
||||
FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information as text"
|
||||
" at exit.")
|
||||
FUZZER_FLAG_INT(dump_coverage, 0, "If 1, dump coverage information as a"
|
||||
" .sancov file at exit.")
|
||||
FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.")
|
||||
FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.")
|
||||
FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.")
|
||||
FUZZER_FLAG_INT(handle_ill, 1, "If 1, try to intercept SIGILL.")
|
||||
FUZZER_FLAG_INT(handle_fpe, 1, "If 1, try to intercept SIGFPE.")
|
||||
FUZZER_FLAG_INT(handle_int, 1, "If 1, try to intercept SIGINT.")
|
||||
FUZZER_FLAG_INT(handle_term, 1, "If 1, try to intercept SIGTERM.")
|
||||
FUZZER_FLAG_INT(handle_xfsz, 1, "If 1, try to intercept SIGXFSZ.")
|
||||
FUZZER_FLAG_INT(close_fd_mask, 0, "If 1, close stdout at startup; "
|
||||
"if 2, close stderr; if 3, close both. "
|
||||
"Be careful, this will also close e.g. asan's stderr/stdout.")
|
||||
FUZZER_FLAG_INT(detect_leaks, 1, "If 1, and if LeakSanitizer is enabled "
|
||||
"try to detect memory leaks during fuzzing (i.e. not only at shut down).")
|
||||
FUZZER_FLAG_INT(trace_malloc, 0, "If >= 1 will print all mallocs/frees. "
|
||||
"If >= 2 will also print stack traces.")
|
||||
FUZZER_FLAG_INT(rss_limit_mb, 2048, "If non-zero, the fuzzer will exit upon"
|
||||
"reaching this limit of RSS memory usage.")
|
||||
FUZZER_FLAG_STRING(exit_on_src_pos, "Exit if a newly found PC originates"
|
||||
" from the given source location. Example: -exit_on_src_pos=foo.cc:123. "
|
||||
"Used primarily for testing libFuzzer itself.")
|
||||
FUZZER_FLAG_STRING(exit_on_item, "Exit if an item with a given sha1 sum"
|
||||
" was added to the corpus. "
|
||||
"Used primarily for testing libFuzzer itself.")
|
||||
FUZZER_FLAG_INT(ignore_remaining_args, 0, "If 1, ignore all arguments passed "
|
||||
"after this one. Useful for fuzzers that need to do their own "
|
||||
"argument parsing.")
|
||||
|
||||
FUZZER_FLAG_STRING(run_equivalence_server, "Experimental")
|
||||
FUZZER_FLAG_STRING(use_equivalence_server, "Experimental")
|
||||
FUZZER_FLAG_INT(analyze_dict, 0, "Experimental")
|
||||
|
||||
FUZZER_DEPRECATED_FLAG(exit_on_first)
|
||||
FUZZER_DEPRECATED_FLAG(save_minimized_corpus)
|
||||
FUZZER_DEPRECATED_FLAG(sync_command)
|
||||
FUZZER_DEPRECATED_FLAG(sync_timeout)
|
||||
FUZZER_DEPRECATED_FLAG(test_single_input)
|
||||
FUZZER_DEPRECATED_FLAG(drill)
|
||||
FUZZER_DEPRECATED_FLAG(truncate_units)
|
||||
FUZZER_DEPRECATED_FLAG(output_csv)
|
118
external/bsd/llvm/dist/llvm/lib/Fuzzer/FuzzerIO.cpp
vendored
118
external/bsd/llvm/dist/llvm/lib/Fuzzer/FuzzerIO.cpp
vendored
@ -1,118 +0,0 @@
|
||||
//===- FuzzerIO.cpp - IO utils. -------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// IO functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
static FILE *OutputFile = stderr;
|
||||
|
||||
long GetEpoch(const std::string &Path) {
|
||||
struct stat St;
|
||||
if (stat(Path.c_str(), &St))
|
||||
return 0; // Can't stat, be conservative.
|
||||
return St.st_mtime;
|
||||
}
|
||||
|
||||
Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) {
|
||||
std::ifstream T(Path);
|
||||
if (ExitOnError && !T) {
|
||||
Printf("No such directory: %s; exiting\n", Path.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
T.seekg(0, T.end);
|
||||
size_t FileLen = T.tellg();
|
||||
if (MaxSize)
|
||||
FileLen = std::min(FileLen, MaxSize);
|
||||
|
||||
T.seekg(0, T.beg);
|
||||
Unit Res(FileLen);
|
||||
T.read(reinterpret_cast<char *>(Res.data()), FileLen);
|
||||
return Res;
|
||||
}
|
||||
|
||||
std::string FileToString(const std::string &Path) {
|
||||
std::ifstream T(Path);
|
||||
return std::string((std::istreambuf_iterator<char>(T)),
|
||||
std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
void CopyFileToErr(const std::string &Path) {
|
||||
Printf("%s", FileToString(Path).c_str());
|
||||
}
|
||||
|
||||
void WriteToFile(const Unit &U, const std::string &Path) {
|
||||
// Use raw C interface because this function may be called from a sig handler.
|
||||
FILE *Out = fopen(Path.c_str(), "w");
|
||||
if (!Out) return;
|
||||
fwrite(U.data(), sizeof(U[0]), U.size(), Out);
|
||||
fclose(Out);
|
||||
}
|
||||
|
||||
void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V,
|
||||
long *Epoch, size_t MaxSize, bool ExitOnError) {
|
||||
long E = Epoch ? *Epoch : 0;
|
||||
std::vector<std::string> Files;
|
||||
ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true);
|
||||
size_t NumLoaded = 0;
|
||||
for (size_t i = 0; i < Files.size(); i++) {
|
||||
auto &X = Files[i];
|
||||
if (Epoch && GetEpoch(X) < E) continue;
|
||||
NumLoaded++;
|
||||
if ((NumLoaded & (NumLoaded - 1)) == 0 && NumLoaded >= 1024)
|
||||
Printf("Loaded %zd/%zd files from %s\n", NumLoaded, Files.size(), Path);
|
||||
auto S = FileToVector(X, MaxSize, ExitOnError);
|
||||
if (!S.empty())
|
||||
V->push_back(S);
|
||||
}
|
||||
}
|
||||
|
||||
std::string DirPlusFile(const std::string &DirPath,
|
||||
const std::string &FileName) {
|
||||
return DirPath + GetSeparator() + FileName;
|
||||
}
|
||||
|
||||
void DupAndCloseStderr() {
|
||||
int OutputFd = DuplicateFile(2);
|
||||
if (OutputFd > 0) {
|
||||
FILE *NewOutputFile = OpenFile(OutputFd, "w");
|
||||
if (NewOutputFile) {
|
||||
OutputFile = NewOutputFile;
|
||||
if (EF->__sanitizer_set_report_fd)
|
||||
EF->__sanitizer_set_report_fd(
|
||||
reinterpret_cast<void *>(GetHandleFromFd(OutputFd)));
|
||||
DiscardOutput(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CloseStdout() {
|
||||
DiscardOutput(1);
|
||||
}
|
||||
|
||||
void Printf(const char *Fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, Fmt);
|
||||
vfprintf(OutputFile, Fmt, ap);
|
||||
va_end(ap);
|
||||
fflush(OutputFile);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
@ -1,76 +0,0 @@
|
||||
//===- FuzzerIO.h - Internal header for IO utils ----------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// IO interface.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_IO_H
|
||||
#define LLVM_FUZZER_IO_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
long GetEpoch(const std::string &Path);
|
||||
|
||||
Unit FileToVector(const std::string &Path, size_t MaxSize = 0,
|
||||
bool ExitOnError = true);
|
||||
|
||||
std::string FileToString(const std::string &Path);
|
||||
|
||||
void CopyFileToErr(const std::string &Path);
|
||||
|
||||
void WriteToFile(const Unit &U, const std::string &Path);
|
||||
|
||||
void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V,
|
||||
long *Epoch, size_t MaxSize, bool ExitOnError);
|
||||
|
||||
// Returns "Dir/FileName" or equivalent for the current OS.
|
||||
std::string DirPlusFile(const std::string &DirPath,
|
||||
const std::string &FileName);
|
||||
|
||||
// Returns the name of the dir, similar to the 'dirname' utility.
|
||||
std::string DirName(const std::string &FileName);
|
||||
|
||||
// Returns path to a TmpDir.
|
||||
std::string TmpDir();
|
||||
|
||||
bool IsInterestingCoverageFile(const std::string &FileName);
|
||||
|
||||
void DupAndCloseStderr();
|
||||
|
||||
void CloseStdout();
|
||||
|
||||
void Printf(const char *Fmt, ...);
|
||||
|
||||
// Print using raw syscalls, useful when printing at early init stages.
|
||||
void RawPrint(const char *Str);
|
||||
|
||||
// Platform specific functions:
|
||||
bool IsFile(const std::string &Path);
|
||||
|
||||
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||
std::vector<std::string> *V, bool TopDir);
|
||||
|
||||
char GetSeparator();
|
||||
|
||||
FILE* OpenFile(int Fd, const char *Mode);
|
||||
|
||||
int CloseFile(int Fd);
|
||||
|
||||
int DuplicateFile(int Fd);
|
||||
|
||||
void RemoveFile(const std::string &Path);
|
||||
|
||||
void DiscardOutput(int Fd);
|
||||
|
||||
intptr_t GetHandleFromFd(int fd);
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_IO_H
|
@ -1,123 +0,0 @@
|
||||
//===- FuzzerIOPosix.cpp - IO utils for Posix. ----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// IO functions implementation using Posix API.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_POSIX
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <dirent.h>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <libgen.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
bool IsFile(const std::string &Path) {
|
||||
struct stat St;
|
||||
if (stat(Path.c_str(), &St))
|
||||
return false;
|
||||
return S_ISREG(St.st_mode);
|
||||
}
|
||||
|
||||
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||
std::vector<std::string> *V, bool TopDir) {
|
||||
auto E = GetEpoch(Dir);
|
||||
if (Epoch)
|
||||
if (E && *Epoch >= E) return;
|
||||
|
||||
DIR *D = opendir(Dir.c_str());
|
||||
if (!D) {
|
||||
Printf("No such directory: %s; exiting\n", Dir.c_str());
|
||||
exit(1);
|
||||
}
|
||||
while (auto E = readdir(D)) {
|
||||
std::string Path = DirPlusFile(Dir, E->d_name);
|
||||
if (E->d_type == DT_REG || E->d_type == DT_LNK)
|
||||
V->push_back(Path);
|
||||
else if (E->d_type == DT_DIR && *E->d_name != '.')
|
||||
ListFilesInDirRecursive(Path, Epoch, V, false);
|
||||
}
|
||||
closedir(D);
|
||||
if (Epoch && TopDir)
|
||||
*Epoch = E;
|
||||
}
|
||||
|
||||
char GetSeparator() {
|
||||
return '/';
|
||||
}
|
||||
|
||||
FILE* OpenFile(int Fd, const char* Mode) {
|
||||
return fdopen(Fd, Mode);
|
||||
}
|
||||
|
||||
int CloseFile(int fd) {
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
int DuplicateFile(int Fd) {
|
||||
return dup(Fd);
|
||||
}
|
||||
|
||||
void RemoveFile(const std::string &Path) {
|
||||
unlink(Path.c_str());
|
||||
}
|
||||
|
||||
void DiscardOutput(int Fd) {
|
||||
FILE* Temp = fopen("/dev/null", "w");
|
||||
if (!Temp)
|
||||
return;
|
||||
dup2(fileno(Temp), Fd);
|
||||
fclose(Temp);
|
||||
}
|
||||
|
||||
intptr_t GetHandleFromFd(int fd) {
|
||||
return static_cast<intptr_t>(fd);
|
||||
}
|
||||
|
||||
std::string DirName(const std::string &FileName) {
|
||||
char *Tmp = new char[FileName.size() + 1];
|
||||
memcpy(Tmp, FileName.c_str(), FileName.size() + 1);
|
||||
std::string Res = dirname(Tmp);
|
||||
delete [] Tmp;
|
||||
return Res;
|
||||
}
|
||||
|
||||
std::string TmpDir() {
|
||||
if (auto Env = getenv("TMPDIR"))
|
||||
return Env;
|
||||
return "/tmp";
|
||||
}
|
||||
|
||||
bool IsInterestingCoverageFile(const std::string &FileName) {
|
||||
if (FileName.find("compiler-rt/lib/") != std::string::npos)
|
||||
return false; // sanitizer internal.
|
||||
if (FileName.find("/usr/lib/") != std::string::npos)
|
||||
return false;
|
||||
if (FileName.find("/usr/include/") != std::string::npos)
|
||||
return false;
|
||||
if (FileName == "<null>")
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void RawPrint(const char *Str) {
|
||||
write(2, Str, strlen(Str));
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_POSIX
|
@ -1,323 +0,0 @@
|
||||
//===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// IO functions implementation for Windows.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_WINDOWS
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <io.h>
|
||||
#include <iterator>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <windows.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
static bool IsFile(const std::string &Path, const DWORD &FileAttributes) {
|
||||
|
||||
if (FileAttributes & FILE_ATTRIBUTE_NORMAL)
|
||||
return true;
|
||||
|
||||
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
return false;
|
||||
|
||||
HANDLE FileHandle(
|
||||
CreateFileA(Path.c_str(), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, 0));
|
||||
|
||||
if (FileHandle == INVALID_HANDLE_VALUE) {
|
||||
Printf("CreateFileA() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
|
||||
GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD FileType = GetFileType(FileHandle);
|
||||
|
||||
if (FileType == FILE_TYPE_UNKNOWN) {
|
||||
Printf("GetFileType() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
|
||||
GetLastError());
|
||||
CloseHandle(FileHandle);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FileType != FILE_TYPE_DISK) {
|
||||
CloseHandle(FileHandle);
|
||||
return false;
|
||||
}
|
||||
|
||||
CloseHandle(FileHandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsFile(const std::string &Path) {
|
||||
DWORD Att = GetFileAttributesA(Path.c_str());
|
||||
|
||||
if (Att == INVALID_FILE_ATTRIBUTES) {
|
||||
Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n",
|
||||
Path.c_str(), GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsFile(Path, Att);
|
||||
}
|
||||
|
||||
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||
std::vector<std::string> *V, bool TopDir) {
|
||||
auto E = GetEpoch(Dir);
|
||||
if (Epoch)
|
||||
if (E && *Epoch >= E) return;
|
||||
|
||||
std::string Path(Dir);
|
||||
assert(!Path.empty());
|
||||
if (Path.back() != '\\')
|
||||
Path.push_back('\\');
|
||||
Path.push_back('*');
|
||||
|
||||
// Get the first directory entry.
|
||||
WIN32_FIND_DATAA FindInfo;
|
||||
HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo));
|
||||
if (FindHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (GetLastError() == ERROR_FILE_NOT_FOUND)
|
||||
return;
|
||||
Printf("No such directory: %s; exiting\n", Dir.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
do {
|
||||
std::string FileName = DirPlusFile(Dir, FindInfo.cFileName);
|
||||
|
||||
if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
size_t FilenameLen = strlen(FindInfo.cFileName);
|
||||
if ((FilenameLen == 1 && FindInfo.cFileName[0] == '.') ||
|
||||
(FilenameLen == 2 && FindInfo.cFileName[0] == '.' &&
|
||||
FindInfo.cFileName[1] == '.'))
|
||||
continue;
|
||||
|
||||
ListFilesInDirRecursive(FileName, Epoch, V, false);
|
||||
}
|
||||
else if (IsFile(FileName, FindInfo.dwFileAttributes))
|
||||
V->push_back(FileName);
|
||||
} while (FindNextFileA(FindHandle, &FindInfo));
|
||||
|
||||
DWORD LastError = GetLastError();
|
||||
if (LastError != ERROR_NO_MORE_FILES)
|
||||
Printf("FindNextFileA failed (Error code: %lu).\n", LastError);
|
||||
|
||||
FindClose(FindHandle);
|
||||
|
||||
if (Epoch && TopDir)
|
||||
*Epoch = E;
|
||||
}
|
||||
|
||||
char GetSeparator() {
|
||||
return '\\';
|
||||
}
|
||||
|
||||
FILE* OpenFile(int Fd, const char* Mode) {
|
||||
return _fdopen(Fd, Mode);
|
||||
}
|
||||
|
||||
int CloseFile(int Fd) {
|
||||
return _close(Fd);
|
||||
}
|
||||
|
||||
int DuplicateFile(int Fd) {
|
||||
return _dup(Fd);
|
||||
}
|
||||
|
||||
void RemoveFile(const std::string &Path) {
|
||||
_unlink(Path.c_str());
|
||||
}
|
||||
|
||||
void DiscardOutput(int Fd) {
|
||||
FILE* Temp = fopen("nul", "w");
|
||||
if (!Temp)
|
||||
return;
|
||||
_dup2(_fileno(Temp), Fd);
|
||||
fclose(Temp);
|
||||
}
|
||||
|
||||
intptr_t GetHandleFromFd(int fd) {
|
||||
return _get_osfhandle(fd);
|
||||
}
|
||||
|
||||
static bool IsSeparator(char C) {
|
||||
return C == '\\' || C == '/';
|
||||
}
|
||||
|
||||
// Parse disk designators, like "C:\". If Relative == true, also accepts: "C:".
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseDrive(const std::string &FileName, const size_t Offset,
|
||||
bool Relative = true) {
|
||||
if (Offset + 1 >= FileName.size() || FileName[Offset + 1] != ':')
|
||||
return 0;
|
||||
if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) {
|
||||
if (!Relative) // Accept relative path?
|
||||
return 0;
|
||||
else
|
||||
return 2;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Parse a file name, like: SomeFile.txt
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseFileName(const std::string &FileName, const size_t Offset) {
|
||||
size_t Pos = Offset;
|
||||
const size_t End = FileName.size();
|
||||
for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
|
||||
;
|
||||
return Pos - Offset;
|
||||
}
|
||||
|
||||
// Parse a directory ending in separator, like: `SomeDir\`
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseDir(const std::string &FileName, const size_t Offset) {
|
||||
size_t Pos = Offset;
|
||||
const size_t End = FileName.size();
|
||||
if (Pos >= End || IsSeparator(FileName[Pos]))
|
||||
return 0;
|
||||
for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
|
||||
;
|
||||
if (Pos >= End)
|
||||
return 0;
|
||||
++Pos; // Include separator.
|
||||
return Pos - Offset;
|
||||
}
|
||||
|
||||
// Parse a servername and share, like: `SomeServer\SomeShare\`
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseServerAndShare(const std::string &FileName,
|
||||
const size_t Offset) {
|
||||
size_t Pos = Offset, Res;
|
||||
if (!(Res = ParseDir(FileName, Pos)))
|
||||
return 0;
|
||||
Pos += Res;
|
||||
if (!(Res = ParseDir(FileName, Pos)))
|
||||
return 0;
|
||||
Pos += Res;
|
||||
return Pos - Offset;
|
||||
}
|
||||
|
||||
// Parse the given Ref string from the position Offset, to exactly match the given
|
||||
// string Patt.
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseCustomString(const std::string &Ref, size_t Offset,
|
||||
const char *Patt) {
|
||||
size_t Len = strlen(Patt);
|
||||
if (Offset + Len > Ref.size())
|
||||
return 0;
|
||||
return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0;
|
||||
}
|
||||
|
||||
// Parse a location, like:
|
||||
// \\?\UNC\Server\Share\ \\?\C:\ \\Server\Share\ \ C:\ C:
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseLocation(const std::string &FileName) {
|
||||
size_t Pos = 0, Res;
|
||||
|
||||
if ((Res = ParseCustomString(FileName, Pos, R"(\\?\)"))) {
|
||||
Pos += Res;
|
||||
if ((Res = ParseCustomString(FileName, Pos, R"(UNC\)"))) {
|
||||
Pos += Res;
|
||||
if ((Res = ParseServerAndShare(FileName, Pos)))
|
||||
return Pos + Res;
|
||||
return 0;
|
||||
}
|
||||
if ((Res = ParseDrive(FileName, Pos, false)))
|
||||
return Pos + Res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
|
||||
++Pos;
|
||||
if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
|
||||
++Pos;
|
||||
if ((Res = ParseServerAndShare(FileName, Pos)))
|
||||
return Pos + Res;
|
||||
return 0;
|
||||
}
|
||||
return Pos;
|
||||
}
|
||||
|
||||
if ((Res = ParseDrive(FileName, Pos)))
|
||||
return Pos + Res;
|
||||
|
||||
return Pos;
|
||||
}
|
||||
|
||||
std::string DirName(const std::string &FileName) {
|
||||
size_t LocationLen = ParseLocation(FileName);
|
||||
size_t DirLen = 0, Res;
|
||||
while ((Res = ParseDir(FileName, LocationLen + DirLen)))
|
||||
DirLen += Res;
|
||||
size_t FileLen = ParseFileName(FileName, LocationLen + DirLen);
|
||||
|
||||
if (LocationLen + DirLen + FileLen != FileName.size()) {
|
||||
Printf("DirName() failed for \"%s\", invalid path.\n", FileName.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (DirLen) {
|
||||
--DirLen; // Remove trailing separator.
|
||||
if (!FileLen) { // Path ended in separator.
|
||||
assert(DirLen);
|
||||
// Remove file name from Dir.
|
||||
while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1]))
|
||||
--DirLen;
|
||||
if (DirLen) // Remove trailing separator.
|
||||
--DirLen;
|
||||
}
|
||||
}
|
||||
|
||||
if (!LocationLen) { // Relative path.
|
||||
if (!DirLen)
|
||||
return ".";
|
||||
return std::string(".\\").append(FileName, 0, DirLen);
|
||||
}
|
||||
|
||||
return FileName.substr(0, LocationLen + DirLen);
|
||||
}
|
||||
|
||||
std::string TmpDir() {
|
||||
std::string Tmp;
|
||||
Tmp.resize(MAX_PATH + 1);
|
||||
DWORD Size = GetTempPathA(Tmp.size(), &Tmp[0]);
|
||||
if (Size == 0) {
|
||||
Printf("Couldn't get Tmp path.\n");
|
||||
exit(1);
|
||||
}
|
||||
Tmp.resize(Size);
|
||||
return Tmp;
|
||||
}
|
||||
|
||||
bool IsInterestingCoverageFile(const std::string &FileName) {
|
||||
if (FileName.find("Program Files") != std::string::npos)
|
||||
return false;
|
||||
if (FileName.find("compiler-rt\\lib\\") != std::string::npos)
|
||||
return false; // sanitizer internal.
|
||||
if (FileName == "<null>")
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RawPrint(const char *Str) {
|
||||
// Not tested, may or may not work. Fix if needed.
|
||||
Printf("%s", Str);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
@ -1,67 +0,0 @@
|
||||
//===- FuzzerInterface.h - Interface header for the Fuzzer ------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Define the interface between libFuzzer and the library being tested.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// NOTE: the libFuzzer interface is thin and in the majority of cases
|
||||
// you should not include this file into your target. In 95% of cases
|
||||
// all you need is to define the following function in your file:
|
||||
// extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
|
||||
|
||||
// WARNING: keep the interface in C.
|
||||
|
||||
#ifndef LLVM_FUZZER_INTERFACE_H
|
||||
#define LLVM_FUZZER_INTERFACE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
// Mandatory user-provided target function.
|
||||
// Executes the code under test with [Data, Data+Size) as the input.
|
||||
// libFuzzer will invoke this function *many* times with different inputs.
|
||||
// Must return 0.
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
|
||||
|
||||
// Optional user-provided initialization function.
|
||||
// If provided, this function will be called by libFuzzer once at startup.
|
||||
// It may read and modify argc/argv.
|
||||
// Must return 0.
|
||||
int LLVMFuzzerInitialize(int *argc, char ***argv);
|
||||
|
||||
// Optional user-provided custom mutator.
|
||||
// Mutates raw data in [Data, Data+Size) inplace.
|
||||
// Returns the new size, which is not greater than MaxSize.
|
||||
// Given the same Seed produces the same mutation.
|
||||
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
unsigned int Seed);
|
||||
|
||||
// Optional user-provided custom cross-over function.
|
||||
// Combines pieces of Data1 & Data2 together into Out.
|
||||
// Returns the new size, which is not greater than MaxOutSize.
|
||||
// Should produce the same mutation given the same Seed.
|
||||
size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
|
||||
const uint8_t *Data2, size_t Size2,
|
||||
uint8_t *Out, size_t MaxOutSize,
|
||||
unsigned int Seed);
|
||||
|
||||
// Experimental, may go away in future.
|
||||
// libFuzzer-provided function to be used inside LLVMFuzzerCustomMutator.
|
||||
// Mutates raw data in [Data, Data+Size) inplace.
|
||||
// Returns the new size, which is not greater than MaxSize.
|
||||
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // LLVM_FUZZER_INTERFACE_H
|
@ -1,143 +0,0 @@
|
||||
//===- FuzzerInternal.h - Internal header for the Fuzzer --------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Define the main class fuzzer::Fuzzer and most functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_INTERNAL_H
|
||||
#define LLVM_FUZZER_INTERNAL_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerInterface.h"
|
||||
#include "FuzzerOptions.h"
|
||||
#include "FuzzerSHA1.h"
|
||||
#include "FuzzerValueBitMap.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <string.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
class Fuzzer {
|
||||
public:
|
||||
|
||||
Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
|
||||
FuzzingOptions Options);
|
||||
~Fuzzer();
|
||||
void Loop();
|
||||
void MinimizeCrashLoop(const Unit &U);
|
||||
void ShuffleAndMinimize(UnitVector *V);
|
||||
void RereadOutputCorpus(size_t MaxSize);
|
||||
|
||||
size_t secondsSinceProcessStartUp() {
|
||||
return duration_cast<seconds>(system_clock::now() - ProcessStartTime)
|
||||
.count();
|
||||
}
|
||||
|
||||
bool TimedOut() {
|
||||
return Options.MaxTotalTimeSec > 0 &&
|
||||
secondsSinceProcessStartUp() >
|
||||
static_cast<size_t>(Options.MaxTotalTimeSec);
|
||||
}
|
||||
|
||||
size_t execPerSec() {
|
||||
size_t Seconds = secondsSinceProcessStartUp();
|
||||
return Seconds ? TotalNumberOfRuns / Seconds : 0;
|
||||
}
|
||||
|
||||
size_t getTotalNumberOfRuns() { return TotalNumberOfRuns; }
|
||||
|
||||
static void StaticAlarmCallback();
|
||||
static void StaticCrashSignalCallback();
|
||||
static void StaticInterruptCallback();
|
||||
static void StaticFileSizeExceedCallback();
|
||||
|
||||
void ExecuteCallback(const uint8_t *Data, size_t Size);
|
||||
bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false,
|
||||
InputInfo *II = nullptr);
|
||||
|
||||
// Merge Corpora[1:] into Corpora[0].
|
||||
void Merge(const std::vector<std::string> &Corpora);
|
||||
void CrashResistantMerge(const std::vector<std::string> &Args,
|
||||
const std::vector<std::string> &Corpora,
|
||||
const char *CoverageSummaryInputPathOrNull,
|
||||
const char *CoverageSummaryOutputPathOrNull);
|
||||
void CrashResistantMergeInternalStep(const std::string &ControlFilePath);
|
||||
MutationDispatcher &GetMD() { return MD; }
|
||||
void PrintFinalStats();
|
||||
void SetMaxInputLen(size_t MaxInputLen);
|
||||
void SetMaxMutationLen(size_t MaxMutationLen);
|
||||
void RssLimitCallback();
|
||||
|
||||
bool InFuzzingThread() const { return IsMyThread; }
|
||||
size_t GetCurrentUnitInFuzzingThead(const uint8_t **Data) const;
|
||||
void TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size,
|
||||
bool DuringInitialCorpusExecution);
|
||||
|
||||
void HandleMalloc(size_t Size);
|
||||
void AnnounceOutput(const uint8_t *Data, size_t Size);
|
||||
|
||||
private:
|
||||
void AlarmCallback();
|
||||
void CrashCallback();
|
||||
void CrashOnOverwrittenData();
|
||||
void InterruptCallback();
|
||||
void MutateAndTestOne();
|
||||
void ReportNewCoverage(InputInfo *II, const Unit &U);
|
||||
void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size);
|
||||
void WriteToOutputCorpus(const Unit &U);
|
||||
void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix);
|
||||
void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0);
|
||||
void PrintStatusForNewUnit(const Unit &U, const char *Text);
|
||||
void ShuffleCorpus(UnitVector *V);
|
||||
void CheckExitOnSrcPosOrItem();
|
||||
|
||||
static void StaticDeathCallback();
|
||||
void DumpCurrentUnit(const char *Prefix);
|
||||
void DeathCallback();
|
||||
|
||||
void AllocateCurrentUnitData();
|
||||
uint8_t *CurrentUnitData = nullptr;
|
||||
std::atomic<size_t> CurrentUnitSize;
|
||||
uint8_t BaseSha1[kSHA1NumBytes]; // Checksum of the base unit.
|
||||
bool RunningCB = false;
|
||||
|
||||
size_t TotalNumberOfRuns = 0;
|
||||
size_t NumberOfNewUnitsAdded = 0;
|
||||
|
||||
bool HasMoreMallocsThanFrees = false;
|
||||
size_t NumberOfLeakDetectionAttempts = 0;
|
||||
|
||||
UserCallback CB;
|
||||
InputCorpus &Corpus;
|
||||
MutationDispatcher &MD;
|
||||
FuzzingOptions Options;
|
||||
|
||||
system_clock::time_point ProcessStartTime = system_clock::now();
|
||||
system_clock::time_point UnitStartTime, UnitStopTime;
|
||||
long TimeOfLongestUnitInSeconds = 0;
|
||||
long EpochOfLastReadOfOutputCorpus = 0;
|
||||
|
||||
size_t MaxInputLen = 0;
|
||||
size_t MaxMutationLen = 0;
|
||||
|
||||
std::vector<uint32_t> UniqFeatureSetTmp;
|
||||
|
||||
// Need to know our own thread.
|
||||
static thread_local bool IsMyThread;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_INTERNAL_H
|
@ -1,695 +0,0 @@
|
||||
//===- FuzzerLoop.cpp - Fuzzer's main loop --------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Fuzzer's main loop.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "FuzzerShmem.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<sanitizer / lsan_interface.h>)
|
||||
#include <sanitizer/lsan_interface.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define NO_SANITIZE_MEMORY
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(memory_sanitizer)
|
||||
#undef NO_SANITIZE_MEMORY
|
||||
#define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace fuzzer {
|
||||
static const size_t kMaxUnitSizeToPrint = 256;
|
||||
|
||||
thread_local bool Fuzzer::IsMyThread;
|
||||
|
||||
SharedMemoryRegion SMR;
|
||||
|
||||
// Only one Fuzzer per process.
|
||||
static Fuzzer *F;
|
||||
|
||||
// Leak detection is expensive, so we first check if there were more mallocs
|
||||
// than frees (using the sanitizer malloc hooks) and only then try to call lsan.
|
||||
struct MallocFreeTracer {
|
||||
void Start(int TraceLevel) {
|
||||
this->TraceLevel = TraceLevel;
|
||||
if (TraceLevel)
|
||||
Printf("MallocFreeTracer: START\n");
|
||||
Mallocs = 0;
|
||||
Frees = 0;
|
||||
}
|
||||
// Returns true if there were more mallocs than frees.
|
||||
bool Stop() {
|
||||
if (TraceLevel)
|
||||
Printf("MallocFreeTracer: STOP %zd %zd (%s)\n", Mallocs.load(),
|
||||
Frees.load(), Mallocs == Frees ? "same" : "DIFFERENT");
|
||||
bool Result = Mallocs > Frees;
|
||||
Mallocs = 0;
|
||||
Frees = 0;
|
||||
TraceLevel = 0;
|
||||
return Result;
|
||||
}
|
||||
std::atomic<size_t> Mallocs;
|
||||
std::atomic<size_t> Frees;
|
||||
int TraceLevel = 0;
|
||||
};
|
||||
|
||||
static MallocFreeTracer AllocTracer;
|
||||
|
||||
ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void MallocHook(const volatile void *ptr, size_t size) {
|
||||
size_t N = AllocTracer.Mallocs++;
|
||||
F->HandleMalloc(size);
|
||||
if (int TraceLevel = AllocTracer.TraceLevel) {
|
||||
Printf("MALLOC[%zd] %p %zd\n", N, ptr, size);
|
||||
if (TraceLevel >= 2 && EF)
|
||||
EF->__sanitizer_print_stack_trace();
|
||||
}
|
||||
}
|
||||
|
||||
ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void FreeHook(const volatile void *ptr) {
|
||||
size_t N = AllocTracer.Frees++;
|
||||
if (int TraceLevel = AllocTracer.TraceLevel) {
|
||||
Printf("FREE[%zd] %p\n", N, ptr);
|
||||
if (TraceLevel >= 2 && EF)
|
||||
EF->__sanitizer_print_stack_trace();
|
||||
}
|
||||
}
|
||||
|
||||
// Crash on a single malloc that exceeds the rss limit.
|
||||
void Fuzzer::HandleMalloc(size_t Size) {
|
||||
if (!Options.RssLimitMb || (Size >> 20) < (size_t)Options.RssLimitMb)
|
||||
return;
|
||||
Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n", GetPid(),
|
||||
Size);
|
||||
Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n");
|
||||
if (EF->__sanitizer_print_stack_trace)
|
||||
EF->__sanitizer_print_stack_trace();
|
||||
DumpCurrentUnit("oom-");
|
||||
Printf("SUMMARY: libFuzzer: out-of-memory\n");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.ErrorExitCode); // Stop right now.
|
||||
}
|
||||
|
||||
Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
|
||||
FuzzingOptions Options)
|
||||
: CB(CB), Corpus(Corpus), MD(MD), Options(Options) {
|
||||
if (EF->__sanitizer_set_death_callback)
|
||||
EF->__sanitizer_set_death_callback(StaticDeathCallback);
|
||||
assert(!F);
|
||||
F = this;
|
||||
TPC.ResetMaps();
|
||||
IsMyThread = true;
|
||||
if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks)
|
||||
EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook);
|
||||
TPC.SetUseCounters(Options.UseCounters);
|
||||
TPC.SetUseValueProfile(Options.UseValueProfile);
|
||||
TPC.SetPrintNewPCs(Options.PrintNewCovPcs);
|
||||
|
||||
if (Options.Verbosity)
|
||||
TPC.PrintModuleInfo();
|
||||
if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec)
|
||||
EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus);
|
||||
MaxInputLen = MaxMutationLen = Options.MaxLen;
|
||||
AllocateCurrentUnitData();
|
||||
CurrentUnitSize = 0;
|
||||
memset(BaseSha1, 0, sizeof(BaseSha1));
|
||||
}
|
||||
|
||||
Fuzzer::~Fuzzer() { }
|
||||
|
||||
void Fuzzer::AllocateCurrentUnitData() {
|
||||
if (CurrentUnitData || MaxInputLen == 0) return;
|
||||
CurrentUnitData = new uint8_t[MaxInputLen];
|
||||
}
|
||||
|
||||
void Fuzzer::StaticDeathCallback() {
|
||||
assert(F);
|
||||
F->DeathCallback();
|
||||
}
|
||||
|
||||
void Fuzzer::DumpCurrentUnit(const char *Prefix) {
|
||||
if (!CurrentUnitData) return; // Happens when running individual inputs.
|
||||
MD.PrintMutationSequence();
|
||||
Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str());
|
||||
size_t UnitSize = CurrentUnitSize;
|
||||
if (UnitSize <= kMaxUnitSizeToPrint) {
|
||||
PrintHexArray(CurrentUnitData, UnitSize, "\n");
|
||||
PrintASCII(CurrentUnitData, UnitSize, "\n");
|
||||
}
|
||||
WriteUnitToFileWithPrefix({CurrentUnitData, CurrentUnitData + UnitSize},
|
||||
Prefix);
|
||||
}
|
||||
|
||||
NO_SANITIZE_MEMORY
|
||||
void Fuzzer::DeathCallback() {
|
||||
DumpCurrentUnit("crash-");
|
||||
PrintFinalStats();
|
||||
}
|
||||
|
||||
void Fuzzer::StaticAlarmCallback() {
|
||||
assert(F);
|
||||
F->AlarmCallback();
|
||||
}
|
||||
|
||||
void Fuzzer::StaticCrashSignalCallback() {
|
||||
assert(F);
|
||||
F->CrashCallback();
|
||||
}
|
||||
|
||||
void Fuzzer::StaticInterruptCallback() {
|
||||
assert(F);
|
||||
F->InterruptCallback();
|
||||
}
|
||||
|
||||
void Fuzzer::StaticFileSizeExceedCallback() {
|
||||
Printf("==%lu== ERROR: libFuzzer: file size exceeded\n", GetPid());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void Fuzzer::CrashCallback() {
|
||||
Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid());
|
||||
if (EF->__sanitizer_print_stack_trace)
|
||||
EF->__sanitizer_print_stack_trace();
|
||||
Printf("NOTE: libFuzzer has rudimentary signal handlers.\n"
|
||||
" Combine libFuzzer with AddressSanitizer or similar for better "
|
||||
"crash reports.\n");
|
||||
Printf("SUMMARY: libFuzzer: deadly signal\n");
|
||||
DumpCurrentUnit("crash-");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.ErrorExitCode); // Stop right now.
|
||||
}
|
||||
|
||||
void Fuzzer::InterruptCallback() {
|
||||
Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid());
|
||||
PrintFinalStats();
|
||||
_Exit(0); // Stop right now, don't perform any at-exit actions.
|
||||
}
|
||||
|
||||
NO_SANITIZE_MEMORY
|
||||
void Fuzzer::AlarmCallback() {
|
||||
assert(Options.UnitTimeoutSec > 0);
|
||||
// In Windows Alarm callback is executed by a different thread.
|
||||
#if !LIBFUZZER_WINDOWS
|
||||
if (!InFuzzingThread()) return;
|
||||
#endif
|
||||
if (!RunningCB)
|
||||
return; // We have not started running units yet.
|
||||
size_t Seconds =
|
||||
duration_cast<seconds>(system_clock::now() - UnitStartTime).count();
|
||||
if (Seconds == 0)
|
||||
return;
|
||||
if (Options.Verbosity >= 2)
|
||||
Printf("AlarmCallback %zd\n", Seconds);
|
||||
if (Seconds >= (size_t)Options.UnitTimeoutSec) {
|
||||
Printf("ALARM: working on the last Unit for %zd seconds\n", Seconds);
|
||||
Printf(" and the timeout value is %d (use -timeout=N to change)\n",
|
||||
Options.UnitTimeoutSec);
|
||||
DumpCurrentUnit("timeout-");
|
||||
Printf("==%lu== ERROR: libFuzzer: timeout after %d seconds\n", GetPid(),
|
||||
Seconds);
|
||||
if (EF->__sanitizer_print_stack_trace)
|
||||
EF->__sanitizer_print_stack_trace();
|
||||
Printf("SUMMARY: libFuzzer: timeout\n");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.TimeoutExitCode); // Stop right now.
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::RssLimitCallback() {
|
||||
Printf(
|
||||
"==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %zdMb)\n",
|
||||
GetPid(), GetPeakRSSMb(), Options.RssLimitMb);
|
||||
Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n");
|
||||
if (EF->__sanitizer_print_memory_profile)
|
||||
EF->__sanitizer_print_memory_profile(95, 8);
|
||||
DumpCurrentUnit("oom-");
|
||||
Printf("SUMMARY: libFuzzer: out-of-memory\n");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.ErrorExitCode); // Stop right now.
|
||||
}
|
||||
|
||||
void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) {
|
||||
size_t ExecPerSec = execPerSec();
|
||||
if (!Options.Verbosity)
|
||||
return;
|
||||
Printf("#%zd\t%s", TotalNumberOfRuns, Where);
|
||||
if (size_t N = TPC.GetTotalPCCoverage())
|
||||
Printf(" cov: %zd", N);
|
||||
if (size_t N = Corpus.NumFeatures())
|
||||
Printf( " ft: %zd", N);
|
||||
if (!Corpus.empty()) {
|
||||
Printf(" corp: %zd", Corpus.NumActiveUnits());
|
||||
if (size_t N = Corpus.SizeInBytes()) {
|
||||
if (N < (1<<14))
|
||||
Printf("/%zdb", N);
|
||||
else if (N < (1 << 24))
|
||||
Printf("/%zdKb", N >> 10);
|
||||
else
|
||||
Printf("/%zdMb", N >> 20);
|
||||
}
|
||||
}
|
||||
if (Units)
|
||||
Printf(" units: %zd", Units);
|
||||
|
||||
Printf(" exec/s: %zd", ExecPerSec);
|
||||
Printf(" rss: %zdMb", GetPeakRSSMb());
|
||||
Printf("%s", End);
|
||||
}
|
||||
|
||||
void Fuzzer::PrintFinalStats() {
|
||||
if (Options.PrintCoverage)
|
||||
TPC.PrintCoverage();
|
||||
if (Options.DumpCoverage)
|
||||
TPC.DumpCoverage();
|
||||
if (Options.PrintCorpusStats)
|
||||
Corpus.PrintStats();
|
||||
if (!Options.PrintFinalStats) return;
|
||||
size_t ExecPerSec = execPerSec();
|
||||
Printf("stat::number_of_executed_units: %zd\n", TotalNumberOfRuns);
|
||||
Printf("stat::average_exec_per_sec: %zd\n", ExecPerSec);
|
||||
Printf("stat::new_units_added: %zd\n", NumberOfNewUnitsAdded);
|
||||
Printf("stat::slowest_unit_time_sec: %zd\n", TimeOfLongestUnitInSeconds);
|
||||
Printf("stat::peak_rss_mb: %zd\n", GetPeakRSSMb());
|
||||
}
|
||||
|
||||
void Fuzzer::SetMaxInputLen(size_t MaxInputLen) {
|
||||
assert(this->MaxInputLen == 0); // Can only reset MaxInputLen from 0 to non-0.
|
||||
assert(MaxInputLen);
|
||||
this->MaxInputLen = MaxInputLen;
|
||||
this->MaxMutationLen = MaxInputLen;
|
||||
AllocateCurrentUnitData();
|
||||
Printf("INFO: -max_len is not provided; "
|
||||
"libFuzzer will not generate inputs larger than %zd bytes\n",
|
||||
MaxInputLen);
|
||||
}
|
||||
|
||||
void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) {
|
||||
assert(MaxMutationLen && MaxMutationLen <= MaxInputLen);
|
||||
this->MaxMutationLen = MaxMutationLen;
|
||||
}
|
||||
|
||||
void Fuzzer::CheckExitOnSrcPosOrItem() {
|
||||
if (!Options.ExitOnSrcPos.empty()) {
|
||||
static auto *PCsSet = new std::set<uintptr_t>;
|
||||
for (size_t i = 1, N = TPC.GetNumPCs(); i < N; i++) {
|
||||
uintptr_t PC = TPC.GetPC(i);
|
||||
if (!PC) continue;
|
||||
if (!PCsSet->insert(PC).second) continue;
|
||||
std::string Descr = DescribePC("%L", PC);
|
||||
if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) {
|
||||
Printf("INFO: found line matching '%s', exiting.\n",
|
||||
Options.ExitOnSrcPos.c_str());
|
||||
_Exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!Options.ExitOnItem.empty()) {
|
||||
if (Corpus.HasUnit(Options.ExitOnItem)) {
|
||||
Printf("INFO: found item with checksum '%s', exiting.\n",
|
||||
Options.ExitOnItem.c_str());
|
||||
_Exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::RereadOutputCorpus(size_t MaxSize) {
|
||||
if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec) return;
|
||||
std::vector<Unit> AdditionalCorpus;
|
||||
ReadDirToVectorOfUnits(Options.OutputCorpus.c_str(), &AdditionalCorpus,
|
||||
&EpochOfLastReadOfOutputCorpus, MaxSize,
|
||||
/*ExitOnError*/ false);
|
||||
if (Options.Verbosity >= 2)
|
||||
Printf("Reload: read %zd new units.\n", AdditionalCorpus.size());
|
||||
bool Reloaded = false;
|
||||
for (auto &U : AdditionalCorpus) {
|
||||
if (U.size() > MaxSize)
|
||||
U.resize(MaxSize);
|
||||
if (!Corpus.HasUnit(U)) {
|
||||
if (RunOne(U.data(), U.size()))
|
||||
Reloaded = true;
|
||||
}
|
||||
}
|
||||
if (Reloaded)
|
||||
PrintStats("RELOAD");
|
||||
}
|
||||
|
||||
void Fuzzer::ShuffleCorpus(UnitVector *V) {
|
||||
std::shuffle(V->begin(), V->end(), MD.GetRand());
|
||||
if (Options.PreferSmall)
|
||||
std::stable_sort(V->begin(), V->end(), [](const Unit &A, const Unit &B) {
|
||||
return A.size() < B.size();
|
||||
});
|
||||
}
|
||||
|
||||
void Fuzzer::ShuffleAndMinimize(UnitVector *InitialCorpus) {
|
||||
Printf("#0\tREAD units: %zd\n", InitialCorpus->size());
|
||||
if (Options.ShuffleAtStartUp)
|
||||
ShuffleCorpus(InitialCorpus);
|
||||
|
||||
// Test the callback with empty input and never try it again.
|
||||
uint8_t dummy;
|
||||
ExecuteCallback(&dummy, 0);
|
||||
|
||||
for (const auto &U : *InitialCorpus) {
|
||||
RunOne(U.data(), U.size());
|
||||
TryDetectingAMemoryLeak(U.data(), U.size(),
|
||||
/*DuringInitialCorpusExecution*/ true);
|
||||
}
|
||||
PrintStats("INITED");
|
||||
if (Corpus.empty()) {
|
||||
Printf("ERROR: no interesting inputs were found. "
|
||||
"Is the code instrumented for coverage? Exiting.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) {
|
||||
auto TimeOfUnit =
|
||||
duration_cast<seconds>(UnitStopTime - UnitStartTime).count();
|
||||
if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) &&
|
||||
secondsSinceProcessStartUp() >= 2)
|
||||
PrintStats("pulse ");
|
||||
if (TimeOfUnit > TimeOfLongestUnitInSeconds * 1.1 &&
|
||||
TimeOfUnit >= Options.ReportSlowUnits) {
|
||||
TimeOfLongestUnitInSeconds = TimeOfUnit;
|
||||
Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds);
|
||||
WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-");
|
||||
}
|
||||
}
|
||||
|
||||
bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
|
||||
InputInfo *II) {
|
||||
if (!Size) return false;
|
||||
|
||||
ExecuteCallback(Data, Size);
|
||||
|
||||
UniqFeatureSetTmp.clear();
|
||||
size_t FoundUniqFeaturesOfII = 0;
|
||||
size_t NumUpdatesBefore = Corpus.NumFeatureUpdates();
|
||||
TPC.CollectFeatures([&](size_t Feature) {
|
||||
if (Corpus.AddFeature(Feature, Size, Options.Shrink))
|
||||
UniqFeatureSetTmp.push_back(Feature);
|
||||
if (Options.ReduceInputs && II)
|
||||
if (std::binary_search(II->UniqFeatureSet.begin(),
|
||||
II->UniqFeatureSet.end(), Feature))
|
||||
FoundUniqFeaturesOfII++;
|
||||
});
|
||||
PrintPulseAndReportSlowInput(Data, Size);
|
||||
size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore;
|
||||
if (NumNewFeatures) {
|
||||
Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile,
|
||||
UniqFeatureSetTmp);
|
||||
CheckExitOnSrcPosOrItem();
|
||||
return true;
|
||||
}
|
||||
if (II && FoundUniqFeaturesOfII &&
|
||||
FoundUniqFeaturesOfII == II->UniqFeatureSet.size() &&
|
||||
II->U.size() > Size) {
|
||||
Corpus.Replace(II, {Data, Data + Size});
|
||||
CheckExitOnSrcPosOrItem();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const {
|
||||
assert(InFuzzingThread());
|
||||
*Data = CurrentUnitData;
|
||||
return CurrentUnitSize;
|
||||
}
|
||||
|
||||
void Fuzzer::CrashOnOverwrittenData() {
|
||||
Printf("==%d== ERROR: libFuzzer: fuzz target overwrites it's const input\n",
|
||||
GetPid());
|
||||
DumpCurrentUnit("crash-");
|
||||
Printf("SUMMARY: libFuzzer: out-of-memory\n");
|
||||
_Exit(Options.ErrorExitCode); // Stop right now.
|
||||
}
|
||||
|
||||
// Compare two arrays, but not all bytes if the arrays are large.
|
||||
static bool LooseMemeq(const uint8_t *A, const uint8_t *B, size_t Size) {
|
||||
const size_t Limit = 64;
|
||||
if (Size <= 64)
|
||||
return !memcmp(A, B, Size);
|
||||
// Compare first and last Limit/2 bytes.
|
||||
return !memcmp(A, B, Limit / 2) &&
|
||||
!memcmp(A + Size - Limit / 2, B + Size - Limit / 2, Limit / 2);
|
||||
}
|
||||
|
||||
void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
|
||||
TotalNumberOfRuns++;
|
||||
assert(InFuzzingThread());
|
||||
if (SMR.IsClient())
|
||||
SMR.WriteByteArray(Data, Size);
|
||||
// We copy the contents of Unit into a separate heap buffer
|
||||
// so that we reliably find buffer overflows in it.
|
||||
uint8_t *DataCopy = new uint8_t[Size];
|
||||
memcpy(DataCopy, Data, Size);
|
||||
if (CurrentUnitData && CurrentUnitData != Data)
|
||||
memcpy(CurrentUnitData, Data, Size);
|
||||
CurrentUnitSize = Size;
|
||||
AllocTracer.Start(Options.TraceMalloc);
|
||||
UnitStartTime = system_clock::now();
|
||||
TPC.ResetMaps();
|
||||
RunningCB = true;
|
||||
int Res = CB(DataCopy, Size);
|
||||
RunningCB = false;
|
||||
UnitStopTime = system_clock::now();
|
||||
(void)Res;
|
||||
assert(Res == 0);
|
||||
HasMoreMallocsThanFrees = AllocTracer.Stop();
|
||||
if (!LooseMemeq(DataCopy, Data, Size))
|
||||
CrashOnOverwrittenData();
|
||||
CurrentUnitSize = 0;
|
||||
delete[] DataCopy;
|
||||
}
|
||||
|
||||
void Fuzzer::WriteToOutputCorpus(const Unit &U) {
|
||||
if (Options.OnlyASCII)
|
||||
assert(IsASCII(U));
|
||||
if (Options.OutputCorpus.empty())
|
||||
return;
|
||||
std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U));
|
||||
WriteToFile(U, Path);
|
||||
if (Options.Verbosity >= 2)
|
||||
Printf("Written to %s\n", Path.c_str());
|
||||
}
|
||||
|
||||
void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) {
|
||||
if (!Options.SaveArtifacts)
|
||||
return;
|
||||
std::string Path = Options.ArtifactPrefix + Prefix + Hash(U);
|
||||
if (!Options.ExactArtifactPath.empty())
|
||||
Path = Options.ExactArtifactPath; // Overrides ArtifactPrefix.
|
||||
WriteToFile(U, Path);
|
||||
Printf("artifact_prefix='%s'; Test unit written to %s\n",
|
||||
Options.ArtifactPrefix.c_str(), Path.c_str());
|
||||
if (U.size() <= kMaxUnitSizeToPrint)
|
||||
Printf("Base64: %s\n", Base64(U).c_str());
|
||||
}
|
||||
|
||||
void Fuzzer::PrintStatusForNewUnit(const Unit &U, const char *Text) {
|
||||
if (!Options.PrintNEW)
|
||||
return;
|
||||
PrintStats(Text, "");
|
||||
if (Options.Verbosity) {
|
||||
Printf(" L: %zd ", U.size());
|
||||
MD.PrintMutationSequence();
|
||||
Printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) {
|
||||
II->NumSuccessfullMutations++;
|
||||
MD.RecordSuccessfulMutationSequence();
|
||||
PrintStatusForNewUnit(U, II->Reduced ? "REDUCE" :
|
||||
"NEW ");
|
||||
WriteToOutputCorpus(U);
|
||||
NumberOfNewUnitsAdded++;
|
||||
TPC.PrintNewPCs();
|
||||
}
|
||||
|
||||
// Tries detecting a memory leak on the particular input that we have just
|
||||
// executed before calling this function.
|
||||
void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size,
|
||||
bool DuringInitialCorpusExecution) {
|
||||
if (!HasMoreMallocsThanFrees) return; // mallocs==frees, a leak is unlikely.
|
||||
if (!Options.DetectLeaks) return;
|
||||
if (!&(EF->__lsan_enable) || !&(EF->__lsan_disable) ||
|
||||
!(EF->__lsan_do_recoverable_leak_check))
|
||||
return; // No lsan.
|
||||
// Run the target once again, but with lsan disabled so that if there is
|
||||
// a real leak we do not report it twice.
|
||||
EF->__lsan_disable();
|
||||
ExecuteCallback(Data, Size);
|
||||
EF->__lsan_enable();
|
||||
if (!HasMoreMallocsThanFrees) return; // a leak is unlikely.
|
||||
if (NumberOfLeakDetectionAttempts++ > 1000) {
|
||||
Options.DetectLeaks = false;
|
||||
Printf("INFO: libFuzzer disabled leak detection after every mutation.\n"
|
||||
" Most likely the target function accumulates allocated\n"
|
||||
" memory in a global state w/o actually leaking it.\n"
|
||||
" You may try running this binary with -trace_malloc=[12]"
|
||||
" to get a trace of mallocs and frees.\n"
|
||||
" If LeakSanitizer is enabled in this process it will still\n"
|
||||
" run on the process shutdown.\n");
|
||||
return;
|
||||
}
|
||||
// Now perform the actual lsan pass. This is expensive and we must ensure
|
||||
// we don't call it too often.
|
||||
if (EF->__lsan_do_recoverable_leak_check()) { // Leak is found, report it.
|
||||
if (DuringInitialCorpusExecution)
|
||||
Printf("\nINFO: a leak has been found in the initial corpus.\n\n");
|
||||
Printf("INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.\n\n");
|
||||
CurrentUnitSize = Size;
|
||||
DumpCurrentUnit("leak-");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.ErrorExitCode); // not exit() to disable lsan further on.
|
||||
}
|
||||
}
|
||||
|
||||
static size_t ComputeMutationLen(size_t MaxInputSize, size_t MaxMutationLen,
|
||||
Random &Rand) {
|
||||
assert(MaxInputSize <= MaxMutationLen);
|
||||
if (MaxInputSize == MaxMutationLen) return MaxMutationLen;
|
||||
size_t Result = MaxInputSize;
|
||||
size_t R = Rand.Rand();
|
||||
if ((R % (1U << 7)) == 0)
|
||||
Result++;
|
||||
if ((R % (1U << 15)) == 0)
|
||||
Result += 10 + Result / 2;
|
||||
return Min(Result, MaxMutationLen);
|
||||
}
|
||||
|
||||
void Fuzzer::MutateAndTestOne() {
|
||||
MD.StartMutationSequence();
|
||||
|
||||
auto &II = Corpus.ChooseUnitToMutate(MD.GetRand());
|
||||
const auto &U = II.U;
|
||||
memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1));
|
||||
assert(CurrentUnitData);
|
||||
size_t Size = U.size();
|
||||
assert(Size <= MaxInputLen && "Oversized Unit");
|
||||
memcpy(CurrentUnitData, U.data(), Size);
|
||||
|
||||
assert(MaxMutationLen > 0);
|
||||
|
||||
size_t CurrentMaxMutationLen =
|
||||
Options.ExperimentalLenControl
|
||||
? ComputeMutationLen(Corpus.MaxInputSize(), MaxMutationLen,
|
||||
MD.GetRand())
|
||||
: MaxMutationLen;
|
||||
|
||||
for (int i = 0; i < Options.MutateDepth; i++) {
|
||||
if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
|
||||
break;
|
||||
size_t NewSize = 0;
|
||||
NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen);
|
||||
assert(NewSize > 0 && "Mutator returned empty unit");
|
||||
assert(NewSize <= CurrentMaxMutationLen && "Mutator return overisized unit");
|
||||
Size = NewSize;
|
||||
II.NumExecutedMutations++;
|
||||
if (RunOne(CurrentUnitData, Size, /*MayDeleteFile=*/true, &II))
|
||||
ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size});
|
||||
|
||||
TryDetectingAMemoryLeak(CurrentUnitData, Size,
|
||||
/*DuringInitialCorpusExecution*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::Loop() {
|
||||
TPC.InitializePrintNewPCs();
|
||||
system_clock::time_point LastCorpusReload = system_clock::now();
|
||||
if (Options.DoCrossOver)
|
||||
MD.SetCorpus(&Corpus);
|
||||
while (true) {
|
||||
auto Now = system_clock::now();
|
||||
if (duration_cast<seconds>(Now - LastCorpusReload).count() >=
|
||||
Options.ReloadIntervalSec) {
|
||||
RereadOutputCorpus(MaxInputLen);
|
||||
LastCorpusReload = system_clock::now();
|
||||
}
|
||||
if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
|
||||
break;
|
||||
if (TimedOut()) break;
|
||||
// Perform several mutations and runs.
|
||||
MutateAndTestOne();
|
||||
}
|
||||
|
||||
PrintStats("DONE ", "\n");
|
||||
MD.PrintRecommendedDictionary();
|
||||
}
|
||||
|
||||
void Fuzzer::MinimizeCrashLoop(const Unit &U) {
|
||||
if (U.size() <= 1) return;
|
||||
while (!TimedOut() && TotalNumberOfRuns < Options.MaxNumberOfRuns) {
|
||||
MD.StartMutationSequence();
|
||||
memcpy(CurrentUnitData, U.data(), U.size());
|
||||
for (int i = 0; i < Options.MutateDepth; i++) {
|
||||
size_t NewSize = MD.Mutate(CurrentUnitData, U.size(), MaxMutationLen);
|
||||
assert(NewSize > 0 && NewSize <= MaxMutationLen);
|
||||
ExecuteCallback(CurrentUnitData, NewSize);
|
||||
PrintPulseAndReportSlowInput(CurrentUnitData, NewSize);
|
||||
TryDetectingAMemoryLeak(CurrentUnitData, NewSize,
|
||||
/*DuringInitialCorpusExecution*/ false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::AnnounceOutput(const uint8_t *Data, size_t Size) {
|
||||
if (SMR.IsServer()) {
|
||||
SMR.WriteByteArray(Data, Size);
|
||||
} else if (SMR.IsClient()) {
|
||||
SMR.PostClient();
|
||||
SMR.WaitServer();
|
||||
size_t OtherSize = SMR.ReadByteArraySize();
|
||||
uint8_t *OtherData = SMR.GetByteArray();
|
||||
if (Size != OtherSize || memcmp(Data, OtherData, Size) != 0) {
|
||||
size_t i = 0;
|
||||
for (i = 0; i < Min(Size, OtherSize); i++)
|
||||
if (Data[i] != OtherData[i])
|
||||
break;
|
||||
Printf("==%lu== ERROR: libFuzzer: equivalence-mismatch. Sizes: %zd %zd; "
|
||||
"offset %zd\n", GetPid(), Size, OtherSize, i);
|
||||
DumpCurrentUnit("mismatch-");
|
||||
Printf("SUMMARY: libFuzzer: equivalence-mismatch\n");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.ErrorExitCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
extern "C" {
|
||||
|
||||
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
assert(fuzzer::F);
|
||||
return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
// Experimental
|
||||
void LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size) {
|
||||
assert(fuzzer::F);
|
||||
fuzzer::F->AnnounceOutput(Data, Size);
|
||||
}
|
||||
} // extern "C"
|
@ -1,21 +0,0 @@
|
||||
//===- FuzzerMain.cpp - main() function and flags -------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// main() and flags.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
extern "C" {
|
||||
// This function should be defined by the user.
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
|
||||
} // extern "C"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return fuzzer::FuzzerDriver(&argc, &argv, LLVMFuzzerTestOneInput);
|
||||
}
|
@ -1,338 +0,0 @@
|
||||
//===- FuzzerMerge.cpp - merging corpora ----------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Merging corpora.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerMerge.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include "FuzzerUtil.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
bool Merger::Parse(const std::string &Str, bool ParseCoverage) {
|
||||
std::istringstream SS(Str);
|
||||
return Parse(SS, ParseCoverage);
|
||||
}
|
||||
|
||||
void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) {
|
||||
if (!Parse(IS, ParseCoverage)) {
|
||||
Printf("MERGE: failed to parse the control file (unexpected error)\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// The control file example:
|
||||
//
|
||||
// 3 # The number of inputs
|
||||
// 1 # The number of inputs in the first corpus, <= the previous number
|
||||
// file0
|
||||
// file1
|
||||
// file2 # One file name per line.
|
||||
// STARTED 0 123 # FileID, file size
|
||||
// DONE 0 1 4 6 8 # FileID COV1 COV2 ...
|
||||
// STARTED 1 456 # If DONE is missing, the input crashed while processing.
|
||||
// STARTED 2 567
|
||||
// DONE 2 8 9
|
||||
bool Merger::Parse(std::istream &IS, bool ParseCoverage) {
|
||||
LastFailure.clear();
|
||||
std::string Line;
|
||||
|
||||
// Parse NumFiles.
|
||||
if (!std::getline(IS, Line, '\n')) return false;
|
||||
std::istringstream L1(Line);
|
||||
size_t NumFiles = 0;
|
||||
L1 >> NumFiles;
|
||||
if (NumFiles == 0 || NumFiles > 10000000) return false;
|
||||
|
||||
// Parse NumFilesInFirstCorpus.
|
||||
if (!std::getline(IS, Line, '\n')) return false;
|
||||
std::istringstream L2(Line);
|
||||
NumFilesInFirstCorpus = NumFiles + 1;
|
||||
L2 >> NumFilesInFirstCorpus;
|
||||
if (NumFilesInFirstCorpus > NumFiles) return false;
|
||||
|
||||
// Parse file names.
|
||||
Files.resize(NumFiles);
|
||||
for (size_t i = 0; i < NumFiles; i++)
|
||||
if (!std::getline(IS, Files[i].Name, '\n'))
|
||||
return false;
|
||||
|
||||
// Parse STARTED and DONE lines.
|
||||
size_t ExpectedStartMarker = 0;
|
||||
const size_t kInvalidStartMarker = -1;
|
||||
size_t LastSeenStartMarker = kInvalidStartMarker;
|
||||
std::vector<uint32_t> TmpFeatures;
|
||||
while (std::getline(IS, Line, '\n')) {
|
||||
std::istringstream ISS1(Line);
|
||||
std::string Marker;
|
||||
size_t N;
|
||||
ISS1 >> Marker;
|
||||
ISS1 >> N;
|
||||
if (Marker == "STARTED") {
|
||||
// STARTED FILE_ID FILE_SIZE
|
||||
if (ExpectedStartMarker != N)
|
||||
return false;
|
||||
ISS1 >> Files[ExpectedStartMarker].Size;
|
||||
LastSeenStartMarker = ExpectedStartMarker;
|
||||
assert(ExpectedStartMarker < Files.size());
|
||||
ExpectedStartMarker++;
|
||||
} else if (Marker == "DONE") {
|
||||
// DONE FILE_ID COV1 COV2 COV3 ...
|
||||
size_t CurrentFileIdx = N;
|
||||
if (CurrentFileIdx != LastSeenStartMarker)
|
||||
return false;
|
||||
LastSeenStartMarker = kInvalidStartMarker;
|
||||
if (ParseCoverage) {
|
||||
TmpFeatures.clear(); // use a vector from outer scope to avoid resizes.
|
||||
while (ISS1 >> std::hex >> N)
|
||||
TmpFeatures.push_back(N);
|
||||
std::sort(TmpFeatures.begin(), TmpFeatures.end());
|
||||
Files[CurrentFileIdx].Features = TmpFeatures;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (LastSeenStartMarker != kInvalidStartMarker)
|
||||
LastFailure = Files[LastSeenStartMarker].Name;
|
||||
|
||||
FirstNotProcessedFile = ExpectedStartMarker;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Merger::ApproximateMemoryConsumption() const {
|
||||
size_t Res = 0;
|
||||
for (const auto &F: Files)
|
||||
Res += sizeof(F) + F.Features.size() * sizeof(F.Features[0]);
|
||||
return Res;
|
||||
}
|
||||
|
||||
// Decides which files need to be merged (add thost to NewFiles).
|
||||
// Returns the number of new features added.
|
||||
size_t Merger::Merge(const std::set<uint32_t> &InitialFeatures,
|
||||
std::vector<std::string> *NewFiles) {
|
||||
NewFiles->clear();
|
||||
assert(NumFilesInFirstCorpus <= Files.size());
|
||||
std::set<uint32_t> AllFeatures(InitialFeatures);
|
||||
|
||||
// What features are in the initial corpus?
|
||||
for (size_t i = 0; i < NumFilesInFirstCorpus; i++) {
|
||||
auto &Cur = Files[i].Features;
|
||||
AllFeatures.insert(Cur.begin(), Cur.end());
|
||||
}
|
||||
size_t InitialNumFeatures = AllFeatures.size();
|
||||
|
||||
// Remove all features that we already know from all other inputs.
|
||||
for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) {
|
||||
auto &Cur = Files[i].Features;
|
||||
std::vector<uint32_t> Tmp;
|
||||
std::set_difference(Cur.begin(), Cur.end(), AllFeatures.begin(),
|
||||
AllFeatures.end(), std::inserter(Tmp, Tmp.begin()));
|
||||
Cur.swap(Tmp);
|
||||
}
|
||||
|
||||
// Sort. Give preference to
|
||||
// * smaller files
|
||||
// * files with more features.
|
||||
std::sort(Files.begin() + NumFilesInFirstCorpus, Files.end(),
|
||||
[&](const MergeFileInfo &a, const MergeFileInfo &b) -> bool {
|
||||
if (a.Size != b.Size)
|
||||
return a.Size < b.Size;
|
||||
return a.Features.size() > b.Features.size();
|
||||
});
|
||||
|
||||
// One greedy pass: add the file's features to AllFeatures.
|
||||
// If new features were added, add this file to NewFiles.
|
||||
for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) {
|
||||
auto &Cur = Files[i].Features;
|
||||
// Printf("%s -> sz %zd ft %zd\n", Files[i].Name.c_str(),
|
||||
// Files[i].Size, Cur.size());
|
||||
size_t OldSize = AllFeatures.size();
|
||||
AllFeatures.insert(Cur.begin(), Cur.end());
|
||||
if (AllFeatures.size() > OldSize)
|
||||
NewFiles->push_back(Files[i].Name);
|
||||
}
|
||||
return AllFeatures.size() - InitialNumFeatures;
|
||||
}
|
||||
|
||||
void Merger::PrintSummary(std::ostream &OS) {
|
||||
for (auto &File : Files) {
|
||||
OS << std::hex;
|
||||
OS << File.Name << " size: " << File.Size << " features: ";
|
||||
for (auto Feature : File.Features)
|
||||
OS << " " << Feature;
|
||||
OS << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
std::set<uint32_t> Merger::AllFeatures() const {
|
||||
std::set<uint32_t> S;
|
||||
for (auto &File : Files)
|
||||
S.insert(File.Features.begin(), File.Features.end());
|
||||
return S;
|
||||
}
|
||||
|
||||
std::set<uint32_t> Merger::ParseSummary(std::istream &IS) {
|
||||
std::string Line, Tmp;
|
||||
std::set<uint32_t> Res;
|
||||
while (std::getline(IS, Line, '\n')) {
|
||||
size_t N;
|
||||
std::istringstream ISS1(Line);
|
||||
ISS1 >> Tmp; // Name
|
||||
ISS1 >> Tmp; // size:
|
||||
assert(Tmp == "size:" && "Corrupt summary file");
|
||||
ISS1 >> std::hex;
|
||||
ISS1 >> N; // File Size
|
||||
ISS1 >> Tmp; // features:
|
||||
assert(Tmp == "features:" && "Corrupt summary file");
|
||||
while (ISS1 >> std::hex >> N)
|
||||
Res.insert(N);
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
// Inner process. May crash if the target crashes.
|
||||
void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
|
||||
Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str());
|
||||
Merger M;
|
||||
std::ifstream IF(CFPath);
|
||||
M.ParseOrExit(IF, false);
|
||||
IF.close();
|
||||
if (!M.LastFailure.empty())
|
||||
Printf("MERGE-INNER: '%s' caused a failure at the previous merge step\n",
|
||||
M.LastFailure.c_str());
|
||||
|
||||
Printf("MERGE-INNER: %zd total files;"
|
||||
" %zd processed earlier; will process %zd files now\n",
|
||||
M.Files.size(), M.FirstNotProcessedFile,
|
||||
M.Files.size() - M.FirstNotProcessedFile);
|
||||
|
||||
std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app);
|
||||
for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) {
|
||||
auto U = FileToVector(M.Files[i].Name);
|
||||
if (U.size() > MaxInputLen) {
|
||||
U.resize(MaxInputLen);
|
||||
U.shrink_to_fit();
|
||||
}
|
||||
std::ostringstream StartedLine;
|
||||
// Write the pre-run marker.
|
||||
OF << "STARTED " << std::dec << i << " " << U.size() << "\n";
|
||||
OF.flush(); // Flush is important since ExecuteCommand may crash.
|
||||
// Run.
|
||||
TPC.ResetMaps();
|
||||
ExecuteCallback(U.data(), U.size());
|
||||
// Collect coverage.
|
||||
std::set<size_t> Features;
|
||||
TPC.CollectFeatures([&](size_t Feature) -> bool {
|
||||
Features.insert(Feature);
|
||||
return true;
|
||||
});
|
||||
// Show stats.
|
||||
if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)))
|
||||
PrintStats("pulse ");
|
||||
// Write the post-run marker and the coverage.
|
||||
OF << "DONE " << i;
|
||||
for (size_t F : Features)
|
||||
OF << " " << std::hex << F;
|
||||
OF << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Outer process. Does not call the target code and thus sohuld not fail.
|
||||
void Fuzzer::CrashResistantMerge(const std::vector<std::string> &Args,
|
||||
const std::vector<std::string> &Corpora,
|
||||
const char *CoverageSummaryInputPathOrNull,
|
||||
const char *CoverageSummaryOutputPathOrNull) {
|
||||
if (Corpora.size() <= 1) {
|
||||
Printf("Merge requires two or more corpus dirs\n");
|
||||
return;
|
||||
}
|
||||
std::vector<std::string> AllFiles;
|
||||
ListFilesInDirRecursive(Corpora[0], nullptr, &AllFiles, /*TopDir*/true);
|
||||
size_t NumFilesInFirstCorpus = AllFiles.size();
|
||||
for (size_t i = 1; i < Corpora.size(); i++)
|
||||
ListFilesInDirRecursive(Corpora[i], nullptr, &AllFiles, /*TopDir*/true);
|
||||
Printf("MERGE-OUTER: %zd files, %zd in the initial corpus\n",
|
||||
AllFiles.size(), NumFilesInFirstCorpus);
|
||||
auto CFPath = DirPlusFile(TmpDir(),
|
||||
"libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
|
||||
// Write the control file.
|
||||
RemoveFile(CFPath);
|
||||
std::ofstream ControlFile(CFPath);
|
||||
ControlFile << AllFiles.size() << "\n";
|
||||
ControlFile << NumFilesInFirstCorpus << "\n";
|
||||
for (auto &Path: AllFiles)
|
||||
ControlFile << Path << "\n";
|
||||
if (!ControlFile) {
|
||||
Printf("MERGE-OUTER: failed to write to the control file: %s\n",
|
||||
CFPath.c_str());
|
||||
exit(1);
|
||||
}
|
||||
ControlFile.close();
|
||||
|
||||
// Execute the inner process untill it passes.
|
||||
// Every inner process should execute at least one input.
|
||||
auto BaseCmd = SplitBefore("-ignore_remaining_args=1",
|
||||
CloneArgsWithoutX(Args, "keep-all-flags"));
|
||||
bool Success = false;
|
||||
for (size_t i = 1; i <= AllFiles.size(); i++) {
|
||||
Printf("MERGE-OUTER: attempt %zd\n", i);
|
||||
auto ExitCode = ExecuteCommand(BaseCmd.first + " -merge_control_file=" +
|
||||
CFPath + " " + BaseCmd.second);
|
||||
if (!ExitCode) {
|
||||
Printf("MERGE-OUTER: succesfull in %zd attempt(s)\n", i);
|
||||
Success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!Success) {
|
||||
Printf("MERGE-OUTER: zero succesfull attempts, exiting\n");
|
||||
exit(1);
|
||||
}
|
||||
// Read the control file and do the merge.
|
||||
Merger M;
|
||||
std::ifstream IF(CFPath);
|
||||
IF.seekg(0, IF.end);
|
||||
Printf("MERGE-OUTER: the control file has %zd bytes\n", (size_t)IF.tellg());
|
||||
IF.seekg(0, IF.beg);
|
||||
M.ParseOrExit(IF, true);
|
||||
IF.close();
|
||||
Printf("MERGE-OUTER: consumed %zdMb (%zdMb rss) to parse the control file\n",
|
||||
M.ApproximateMemoryConsumption() >> 20, GetPeakRSSMb());
|
||||
if (CoverageSummaryOutputPathOrNull) {
|
||||
Printf("MERGE-OUTER: writing coverage summary for %zd files to %s\n",
|
||||
M.Files.size(), CoverageSummaryOutputPathOrNull);
|
||||
std::ofstream SummaryOut(CoverageSummaryOutputPathOrNull);
|
||||
M.PrintSummary(SummaryOut);
|
||||
}
|
||||
std::vector<std::string> NewFiles;
|
||||
std::set<uint32_t> InitialFeatures;
|
||||
if (CoverageSummaryInputPathOrNull) {
|
||||
std::ifstream SummaryIn(CoverageSummaryInputPathOrNull);
|
||||
InitialFeatures = M.ParseSummary(SummaryIn);
|
||||
Printf("MERGE-OUTER: coverage summary loaded from %s, %zd features found\n",
|
||||
CoverageSummaryInputPathOrNull, InitialFeatures.size());
|
||||
}
|
||||
size_t NumNewFeatures = M.Merge(InitialFeatures, &NewFiles);
|
||||
Printf("MERGE-OUTER: %zd new files with %zd new features added\n",
|
||||
NewFiles.size(), NumNewFeatures);
|
||||
for (auto &F: NewFiles)
|
||||
WriteToOutputCorpus(FileToVector(F));
|
||||
// We are done, delete the control file.
|
||||
RemoveFile(CFPath);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
@ -1,80 +0,0 @@
|
||||
//===- FuzzerMerge.h - merging corpa ----------------------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Merging Corpora.
|
||||
//
|
||||
// The task:
|
||||
// Take the existing corpus (possibly empty) and merge new inputs into
|
||||
// it so that only inputs with new coverage ('features') are added.
|
||||
// The process should tolerate the crashes, OOMs, leaks, etc.
|
||||
//
|
||||
// Algorithm:
|
||||
// The outter process collects the set of files and writes their names
|
||||
// into a temporary "control" file, then repeatedly launches the inner
|
||||
// process until all inputs are processed.
|
||||
// The outer process does not actually execute the target code.
|
||||
//
|
||||
// The inner process reads the control file and sees a) list of all the inputs
|
||||
// and b) the last processed input. Then it starts processing the inputs one
|
||||
// by one. Before processing every input it writes one line to control file:
|
||||
// STARTED INPUT_ID INPUT_SIZE
|
||||
// After processing an input it write another line:
|
||||
// DONE INPUT_ID Feature1 Feature2 Feature3 ...
|
||||
// If a crash happens while processing an input the last line in the control
|
||||
// file will be "STARTED INPUT_ID" and so the next process will know
|
||||
// where to resume.
|
||||
//
|
||||
// Once all inputs are processed by the innner process(es) the outer process
|
||||
// reads the control files and does the merge based entirely on the contents
|
||||
// of control file.
|
||||
// It uses a single pass greedy algorithm choosing first the smallest inputs
|
||||
// within the same size the inputs that have more new features.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_MERGE_H
|
||||
#define LLVM_FUZZER_MERGE_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
struct MergeFileInfo {
|
||||
std::string Name;
|
||||
size_t Size = 0;
|
||||
std::vector<uint32_t> Features;
|
||||
};
|
||||
|
||||
struct Merger {
|
||||
std::vector<MergeFileInfo> Files;
|
||||
size_t NumFilesInFirstCorpus = 0;
|
||||
size_t FirstNotProcessedFile = 0;
|
||||
std::string LastFailure;
|
||||
|
||||
bool Parse(std::istream &IS, bool ParseCoverage);
|
||||
bool Parse(const std::string &Str, bool ParseCoverage);
|
||||
void ParseOrExit(std::istream &IS, bool ParseCoverage);
|
||||
void PrintSummary(std::ostream &OS);
|
||||
std::set<uint32_t> ParseSummary(std::istream &IS);
|
||||
size_t Merge(const std::set<uint32_t> &InitialFeatures,
|
||||
std::vector<std::string> *NewFiles);
|
||||
size_t Merge(std::vector<std::string> *NewFiles) {
|
||||
return Merge(std::set<uint32_t>{}, NewFiles);
|
||||
}
|
||||
size_t ApproximateMemoryConsumption() const;
|
||||
std::set<uint32_t> AllFeatures() const;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_MERGE_H
|
@ -1,533 +0,0 @@
|
||||
//===- FuzzerMutate.cpp - Mutate a test input -----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Mutate a test input.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerOptions.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
const size_t Dictionary::kMaxDictSize;
|
||||
|
||||
static void PrintASCII(const Word &W, const char *PrintAfter) {
|
||||
PrintASCII(W.data(), W.size(), PrintAfter);
|
||||
}
|
||||
|
||||
MutationDispatcher::MutationDispatcher(Random &Rand,
|
||||
const FuzzingOptions &Options)
|
||||
: Rand(Rand), Options(Options) {
|
||||
DefaultMutators.insert(
|
||||
DefaultMutators.begin(),
|
||||
{
|
||||
{&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"},
|
||||
{&MutationDispatcher::Mutate_InsertByte, "InsertByte"},
|
||||
{&MutationDispatcher::Mutate_InsertRepeatedBytes,
|
||||
"InsertRepeatedBytes"},
|
||||
{&MutationDispatcher::Mutate_ChangeByte, "ChangeByte"},
|
||||
{&MutationDispatcher::Mutate_ChangeBit, "ChangeBit"},
|
||||
{&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes"},
|
||||
{&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt"},
|
||||
{&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt"},
|
||||
{&MutationDispatcher::Mutate_CopyPart, "CopyPart"},
|
||||
{&MutationDispatcher::Mutate_CrossOver, "CrossOver"},
|
||||
{&MutationDispatcher::Mutate_AddWordFromManualDictionary,
|
||||
"ManualDict"},
|
||||
{&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary,
|
||||
"PersAutoDict"},
|
||||
});
|
||||
if(Options.UseCmp)
|
||||
DefaultMutators.push_back(
|
||||
{&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"});
|
||||
|
||||
if (EF->LLVMFuzzerCustomMutator)
|
||||
Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"});
|
||||
else
|
||||
Mutators = DefaultMutators;
|
||||
|
||||
if (EF->LLVMFuzzerCustomCrossOver)
|
||||
Mutators.push_back(
|
||||
{&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"});
|
||||
}
|
||||
|
||||
static char RandCh(Random &Rand) {
|
||||
if (Rand.RandBool()) return Rand(256);
|
||||
const char *Special = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00";
|
||||
return Special[Rand(sizeof(Special) - 1)];
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, Rand.Rand());
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (!Corpus || Corpus->size() < 2 || Size == 0)
|
||||
return 0;
|
||||
size_t Idx = Rand(Corpus->size());
|
||||
const Unit &Other = (*Corpus)[Idx];
|
||||
if (Other.empty())
|
||||
return 0;
|
||||
CustomCrossOverInPlaceHere.resize(MaxSize);
|
||||
auto &U = CustomCrossOverInPlaceHere;
|
||||
size_t NewSize = EF->LLVMFuzzerCustomCrossOver(
|
||||
Data, Size, Other.data(), Other.size(), U.data(), U.size(), Rand.Rand());
|
||||
if (!NewSize)
|
||||
return 0;
|
||||
assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit");
|
||||
memcpy(Data, U.data(), NewSize);
|
||||
return NewSize;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ShuffleBytes(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize || Size == 0) return 0;
|
||||
size_t ShuffleAmount =
|
||||
Rand(std::min(Size, (size_t)8)) + 1; // [1,8] and <= Size.
|
||||
size_t ShuffleStart = Rand(Size - ShuffleAmount);
|
||||
assert(ShuffleStart + ShuffleAmount <= Size);
|
||||
std::shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount, Rand);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_EraseBytes(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size <= 1) return 0;
|
||||
size_t N = Rand(Size / 2) + 1;
|
||||
assert(N < Size);
|
||||
size_t Idx = Rand(Size - N + 1);
|
||||
// Erase Data[Idx:Idx+N].
|
||||
memmove(Data + Idx, Data + Idx + N, Size - Idx - N);
|
||||
// Printf("Erase: %zd %zd => %zd; Idx %zd\n", N, Size, Size - N, Idx);
|
||||
return Size - N;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_InsertByte(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size >= MaxSize) return 0;
|
||||
size_t Idx = Rand(Size + 1);
|
||||
// Insert new value at Data[Idx].
|
||||
memmove(Data + Idx + 1, Data + Idx, Size - Idx);
|
||||
Data[Idx] = RandCh(Rand);
|
||||
return Size + 1;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_InsertRepeatedBytes(uint8_t *Data,
|
||||
size_t Size,
|
||||
size_t MaxSize) {
|
||||
const size_t kMinBytesToInsert = 3;
|
||||
if (Size + kMinBytesToInsert >= MaxSize) return 0;
|
||||
size_t MaxBytesToInsert = std::min(MaxSize - Size, (size_t)128);
|
||||
size_t N = Rand(MaxBytesToInsert - kMinBytesToInsert + 1) + kMinBytesToInsert;
|
||||
assert(Size + N <= MaxSize && N);
|
||||
size_t Idx = Rand(Size + 1);
|
||||
// Insert new values at Data[Idx].
|
||||
memmove(Data + Idx + N, Data + Idx, Size - Idx);
|
||||
// Give preference to 0x00 and 0xff.
|
||||
uint8_t Byte = Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255);
|
||||
for (size_t i = 0; i < N; i++)
|
||||
Data[Idx + i] = Byte;
|
||||
return Size + N;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeByte(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
size_t Idx = Rand(Size);
|
||||
Data[Idx] = RandCh(Rand);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeBit(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
size_t Idx = Rand(Size);
|
||||
Data[Idx] ^= 1 << Rand(8);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data,
|
||||
size_t Size,
|
||||
size_t MaxSize) {
|
||||
return AddWordFromDictionary(ManualDictionary, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize,
|
||||
DictionaryEntry &DE) {
|
||||
const Word &W = DE.GetW();
|
||||
bool UsePositionHint = DE.HasPositionHint() &&
|
||||
DE.GetPositionHint() + W.size() < Size &&
|
||||
Rand.RandBool();
|
||||
if (Rand.RandBool()) { // Insert W.
|
||||
if (Size + W.size() > MaxSize) return 0;
|
||||
size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1);
|
||||
memmove(Data + Idx + W.size(), Data + Idx, Size - Idx);
|
||||
memcpy(Data + Idx, W.data(), W.size());
|
||||
Size += W.size();
|
||||
} else { // Overwrite some bytes with W.
|
||||
if (W.size() > Size) return 0;
|
||||
size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size - W.size());
|
||||
memcpy(Data + Idx, W.data(), W.size());
|
||||
}
|
||||
return Size;
|
||||
}
|
||||
|
||||
// Somewhere in the past we have observed a comparison instructions
|
||||
// with arguments Arg1 Arg2. This function tries to guess a dictionary
|
||||
// entry that will satisfy that comparison.
|
||||
// It first tries to find one of the arguments (possibly swapped) in the
|
||||
// input and if it succeeds it creates a DE with a position hint.
|
||||
// Otherwise it creates a DE with one of the arguments w/o a position hint.
|
||||
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
|
||||
const void *Arg1, const void *Arg2,
|
||||
const void *Arg1Mutation, const void *Arg2Mutation,
|
||||
size_t ArgSize, const uint8_t *Data,
|
||||
size_t Size) {
|
||||
ScopedDoingMyOwnMemOrStr scoped_doing_my_own_mem_os_str;
|
||||
bool HandleFirst = Rand.RandBool();
|
||||
const void *ExistingBytes, *DesiredBytes;
|
||||
Word W;
|
||||
const uint8_t *End = Data + Size;
|
||||
for (int Arg = 0; Arg < 2; Arg++) {
|
||||
ExistingBytes = HandleFirst ? Arg1 : Arg2;
|
||||
DesiredBytes = HandleFirst ? Arg2Mutation : Arg1Mutation;
|
||||
HandleFirst = !HandleFirst;
|
||||
W.Set(reinterpret_cast<const uint8_t*>(DesiredBytes), ArgSize);
|
||||
const size_t kMaxNumPositions = 8;
|
||||
size_t Positions[kMaxNumPositions];
|
||||
size_t NumPositions = 0;
|
||||
for (const uint8_t *Cur = Data;
|
||||
Cur < End && NumPositions < kMaxNumPositions; Cur++) {
|
||||
Cur =
|
||||
(const uint8_t *)SearchMemory(Cur, End - Cur, ExistingBytes, ArgSize);
|
||||
if (!Cur) break;
|
||||
Positions[NumPositions++] = Cur - Data;
|
||||
}
|
||||
if (!NumPositions) continue;
|
||||
return DictionaryEntry(W, Positions[Rand(NumPositions)]);
|
||||
}
|
||||
DictionaryEntry DE(W);
|
||||
return DE;
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
|
||||
T Arg1, T Arg2, const uint8_t *Data, size_t Size) {
|
||||
if (Rand.RandBool()) Arg1 = Bswap(Arg1);
|
||||
if (Rand.RandBool()) Arg2 = Bswap(Arg2);
|
||||
T Arg1Mutation = Arg1 + Rand(-1, 1);
|
||||
T Arg2Mutation = Arg2 + Rand(-1, 1);
|
||||
return MakeDictionaryEntryFromCMP(&Arg1, &Arg2, &Arg1Mutation, &Arg2Mutation,
|
||||
sizeof(Arg1), Data, Size);
|
||||
}
|
||||
|
||||
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
|
||||
const Word &Arg1, const Word &Arg2, const uint8_t *Data, size_t Size) {
|
||||
return MakeDictionaryEntryFromCMP(Arg1.data(), Arg2.data(), Arg1.data(),
|
||||
Arg2.data(), Arg1.size(), Data, Size);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromTORC(
|
||||
uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
Word W;
|
||||
DictionaryEntry DE;
|
||||
switch (Rand(4)) {
|
||||
case 0: {
|
||||
auto X = TPC.TORC8.Get(Rand.Rand());
|
||||
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
|
||||
} break;
|
||||
case 1: {
|
||||
auto X = TPC.TORC4.Get(Rand.Rand());
|
||||
if ((X.A >> 16) == 0 && (X.B >> 16) == 0 && Rand.RandBool())
|
||||
DE = MakeDictionaryEntryFromCMP((uint16_t)X.A, (uint16_t)X.B, Data, Size);
|
||||
else
|
||||
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
|
||||
} break;
|
||||
case 2: {
|
||||
auto X = TPC.TORCW.Get(Rand.Rand());
|
||||
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
|
||||
} break;
|
||||
case 3: if (Options.UseMemmem) {
|
||||
auto X = TPC.MMT.Get(Rand.Rand());
|
||||
DE = DictionaryEntry(X);
|
||||
} break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
if (!DE.GetW().size()) return 0;
|
||||
Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
|
||||
if (!Size) return 0;
|
||||
DictionaryEntry &DERef =
|
||||
CmpDictionaryEntriesDeque[CmpDictionaryEntriesDequeIdx++ %
|
||||
kCmpDictionaryEntriesDequeSize];
|
||||
DERef = DE;
|
||||
CurrentDictionaryEntrySequence.push_back(&DERef);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary(
|
||||
uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
return AddWordFromDictionary(PersistentAutoDictionary, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::AddWordFromDictionary(Dictionary &D, uint8_t *Data,
|
||||
size_t Size, size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
if (D.empty()) return 0;
|
||||
DictionaryEntry &DE = D[Rand(D.size())];
|
||||
Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
|
||||
if (!Size) return 0;
|
||||
DE.IncUseCount();
|
||||
CurrentDictionaryEntrySequence.push_back(&DE);
|
||||
return Size;
|
||||
}
|
||||
|
||||
// Overwrites part of To[0,ToSize) with a part of From[0,FromSize).
|
||||
// Returns ToSize.
|
||||
size_t MutationDispatcher::CopyPartOf(const uint8_t *From, size_t FromSize,
|
||||
uint8_t *To, size_t ToSize) {
|
||||
// Copy From[FromBeg, FromBeg + CopySize) into To[ToBeg, ToBeg + CopySize).
|
||||
size_t ToBeg = Rand(ToSize);
|
||||
size_t CopySize = Rand(ToSize - ToBeg) + 1;
|
||||
assert(ToBeg + CopySize <= ToSize);
|
||||
CopySize = std::min(CopySize, FromSize);
|
||||
size_t FromBeg = Rand(FromSize - CopySize + 1);
|
||||
assert(FromBeg + CopySize <= FromSize);
|
||||
memmove(To + ToBeg, From + FromBeg, CopySize);
|
||||
return ToSize;
|
||||
}
|
||||
|
||||
// Inserts part of From[0,ToSize) into To.
|
||||
// Returns new size of To on success or 0 on failure.
|
||||
size_t MutationDispatcher::InsertPartOf(const uint8_t *From, size_t FromSize,
|
||||
uint8_t *To, size_t ToSize,
|
||||
size_t MaxToSize) {
|
||||
if (ToSize >= MaxToSize) return 0;
|
||||
size_t AvailableSpace = MaxToSize - ToSize;
|
||||
size_t MaxCopySize = std::min(AvailableSpace, FromSize);
|
||||
size_t CopySize = Rand(MaxCopySize) + 1;
|
||||
size_t FromBeg = Rand(FromSize - CopySize + 1);
|
||||
assert(FromBeg + CopySize <= FromSize);
|
||||
size_t ToInsertPos = Rand(ToSize + 1);
|
||||
assert(ToInsertPos + CopySize <= MaxToSize);
|
||||
size_t TailSize = ToSize - ToInsertPos;
|
||||
if (To == From) {
|
||||
MutateInPlaceHere.resize(MaxToSize);
|
||||
memcpy(MutateInPlaceHere.data(), From + FromBeg, CopySize);
|
||||
memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
|
||||
memmove(To + ToInsertPos, MutateInPlaceHere.data(), CopySize);
|
||||
} else {
|
||||
memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
|
||||
memmove(To + ToInsertPos, From + FromBeg, CopySize);
|
||||
}
|
||||
return ToSize + CopySize;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize || Size == 0) return 0;
|
||||
if (Rand.RandBool())
|
||||
return CopyPartOf(Data, Size, Data, Size);
|
||||
else
|
||||
return InsertPartOf(Data, Size, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
size_t B = Rand(Size);
|
||||
while (B < Size && !isdigit(Data[B])) B++;
|
||||
if (B == Size) return 0;
|
||||
size_t E = B;
|
||||
while (E < Size && isdigit(Data[E])) E++;
|
||||
assert(B < E);
|
||||
// now we have digits in [B, E).
|
||||
// strtol and friends don't accept non-zero-teminated data, parse it manually.
|
||||
uint64_t Val = Data[B] - '0';
|
||||
for (size_t i = B + 1; i < E; i++)
|
||||
Val = Val * 10 + Data[i] - '0';
|
||||
|
||||
// Mutate the integer value.
|
||||
switch(Rand(5)) {
|
||||
case 0: Val++; break;
|
||||
case 1: Val--; break;
|
||||
case 2: Val /= 2; break;
|
||||
case 3: Val *= 2; break;
|
||||
case 4: Val = Rand(Val * Val); break;
|
||||
default: assert(0);
|
||||
}
|
||||
// Just replace the bytes with the new ones, don't bother moving bytes.
|
||||
for (size_t i = B; i < E; i++) {
|
||||
size_t Idx = E + B - i - 1;
|
||||
assert(Idx >= B && Idx < E);
|
||||
Data[Idx] = (Val % 10) + '0';
|
||||
Val /= 10;
|
||||
}
|
||||
return Size;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
size_t ChangeBinaryInteger(uint8_t *Data, size_t Size, Random &Rand) {
|
||||
if (Size < sizeof(T)) return 0;
|
||||
size_t Off = Rand(Size - sizeof(T) + 1);
|
||||
assert(Off + sizeof(T) <= Size);
|
||||
T Val;
|
||||
if (Off < 64 && !Rand(4)) {
|
||||
Val = Size;
|
||||
if (Rand.RandBool())
|
||||
Val = Bswap(Val);
|
||||
} else {
|
||||
memcpy(&Val, Data + Off, sizeof(Val));
|
||||
T Add = Rand(21);
|
||||
Add -= 10;
|
||||
if (Rand.RandBool())
|
||||
Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endiannes.
|
||||
else
|
||||
Val = Val + Add; // Add assuming current endiannes.
|
||||
if (Add == 0 || Rand.RandBool()) // Maybe negate.
|
||||
Val = -Val;
|
||||
}
|
||||
memcpy(Data + Off, &Val, sizeof(Val));
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data,
|
||||
size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
switch (Rand(4)) {
|
||||
case 3: return ChangeBinaryInteger<uint64_t>(Data, Size, Rand);
|
||||
case 2: return ChangeBinaryInteger<uint32_t>(Data, Size, Rand);
|
||||
case 1: return ChangeBinaryInteger<uint16_t>(Data, Size, Rand);
|
||||
case 0: return ChangeBinaryInteger<uint8_t>(Data, Size, Rand);
|
||||
default: assert(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
if (!Corpus || Corpus->size() < 2 || Size == 0) return 0;
|
||||
size_t Idx = Rand(Corpus->size());
|
||||
const Unit &O = (*Corpus)[Idx];
|
||||
if (O.empty()) return 0;
|
||||
MutateInPlaceHere.resize(MaxSize);
|
||||
auto &U = MutateInPlaceHere;
|
||||
size_t NewSize = 0;
|
||||
switch(Rand(3)) {
|
||||
case 0:
|
||||
NewSize = CrossOver(Data, Size, O.data(), O.size(), U.data(), U.size());
|
||||
break;
|
||||
case 1:
|
||||
NewSize = InsertPartOf(O.data(), O.size(), U.data(), U.size(), MaxSize);
|
||||
if (!NewSize)
|
||||
NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size());
|
||||
break;
|
||||
case 2:
|
||||
NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size());
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
assert(NewSize > 0 && "CrossOver returned empty unit");
|
||||
assert(NewSize <= MaxSize && "CrossOver returned overisized unit");
|
||||
memcpy(Data, U.data(), NewSize);
|
||||
return NewSize;
|
||||
}
|
||||
|
||||
void MutationDispatcher::StartMutationSequence() {
|
||||
CurrentMutatorSequence.clear();
|
||||
CurrentDictionaryEntrySequence.clear();
|
||||
}
|
||||
|
||||
// Copy successful dictionary entries to PersistentAutoDictionary.
|
||||
void MutationDispatcher::RecordSuccessfulMutationSequence() {
|
||||
for (auto DE : CurrentDictionaryEntrySequence) {
|
||||
// PersistentAutoDictionary.AddWithSuccessCountOne(DE);
|
||||
DE->IncSuccessCount();
|
||||
assert(DE->GetW().size());
|
||||
// Linear search is fine here as this happens seldom.
|
||||
if (!PersistentAutoDictionary.ContainsWord(DE->GetW()))
|
||||
PersistentAutoDictionary.push_back({DE->GetW(), 1});
|
||||
}
|
||||
}
|
||||
|
||||
void MutationDispatcher::PrintRecommendedDictionary() {
|
||||
std::vector<DictionaryEntry> V;
|
||||
for (auto &DE : PersistentAutoDictionary)
|
||||
if (!ManualDictionary.ContainsWord(DE.GetW()))
|
||||
V.push_back(DE);
|
||||
if (V.empty()) return;
|
||||
Printf("###### Recommended dictionary. ######\n");
|
||||
for (auto &DE: V) {
|
||||
assert(DE.GetW().size());
|
||||
Printf("\"");
|
||||
PrintASCII(DE.GetW(), "\"");
|
||||
Printf(" # Uses: %zd\n", DE.GetUseCount());
|
||||
}
|
||||
Printf("###### End of recommended dictionary. ######\n");
|
||||
}
|
||||
|
||||
void MutationDispatcher::PrintMutationSequence() {
|
||||
Printf("MS: %zd ", CurrentMutatorSequence.size());
|
||||
for (auto M : CurrentMutatorSequence)
|
||||
Printf("%s-", M.Name);
|
||||
if (!CurrentDictionaryEntrySequence.empty()) {
|
||||
Printf(" DE: ");
|
||||
for (auto DE : CurrentDictionaryEntrySequence) {
|
||||
Printf("\"");
|
||||
PrintASCII(DE->GetW(), "\"-");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate(uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
return MutateImpl(Data, Size, MaxSize, Mutators);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
return MutateImpl(Data, Size, MaxSize, DefaultMutators);
|
||||
}
|
||||
|
||||
// Mutates Data in place, returns new size.
|
||||
size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize,
|
||||
const std::vector<Mutator> &Mutators) {
|
||||
assert(MaxSize > 0);
|
||||
// Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize),
|
||||
// in which case they will return 0.
|
||||
// Try several times before returning un-mutated data.
|
||||
for (int Iter = 0; Iter < 100; Iter++) {
|
||||
auto M = Mutators[Rand(Mutators.size())];
|
||||
size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize);
|
||||
if (NewSize && NewSize <= MaxSize) {
|
||||
if (Options.OnlyASCII)
|
||||
ToASCII(Data, NewSize);
|
||||
CurrentMutatorSequence.push_back(M);
|
||||
return NewSize;
|
||||
}
|
||||
}
|
||||
*Data = ' ';
|
||||
return 1; // Fallback, should not happen frequently.
|
||||
}
|
||||
|
||||
void MutationDispatcher::AddWordToManualDictionary(const Word &W) {
|
||||
ManualDictionary.push_back(
|
||||
{W, std::numeric_limits<size_t>::max()});
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
@ -1,150 +0,0 @@
|
||||
//===- FuzzerMutate.h - Internal header for the Fuzzer ----------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::MutationDispatcher
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_MUTATE_H
|
||||
#define LLVM_FUZZER_MUTATE_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerDictionary.h"
|
||||
#include "FuzzerOptions.h"
|
||||
#include "FuzzerRandom.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
class MutationDispatcher {
|
||||
public:
|
||||
MutationDispatcher(Random &Rand, const FuzzingOptions &Options);
|
||||
~MutationDispatcher() {}
|
||||
/// Indicate that we are about to start a new sequence of mutations.
|
||||
void StartMutationSequence();
|
||||
/// Print the current sequence of mutations.
|
||||
void PrintMutationSequence();
|
||||
/// Indicate that the current sequence of mutations was successfull.
|
||||
void RecordSuccessfulMutationSequence();
|
||||
/// Mutates data by invoking user-provided mutator.
|
||||
size_t Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by invoking user-provided crossover.
|
||||
size_t Mutate_CustomCrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by shuffling bytes.
|
||||
size_t Mutate_ShuffleBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by erasing bytes.
|
||||
size_t Mutate_EraseBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by inserting a byte.
|
||||
size_t Mutate_InsertByte(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by inserting several repeated bytes.
|
||||
size_t Mutate_InsertRepeatedBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by chanding one byte.
|
||||
size_t Mutate_ChangeByte(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by chanding one bit.
|
||||
size_t Mutate_ChangeBit(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by copying/inserting a part of data into a different place.
|
||||
size_t Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Mutates data by adding a word from the manual dictionary.
|
||||
size_t Mutate_AddWordFromManualDictionary(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
|
||||
/// Mutates data by adding a word from the TORC.
|
||||
size_t Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Mutates data by adding a word from the persistent automatic dictionary.
|
||||
size_t Mutate_AddWordFromPersistentAutoDictionary(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
|
||||
/// Tries to find an ASCII integer in Data, changes it to another ASCII int.
|
||||
size_t Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Change a 1-, 2-, 4-, or 8-byte integer in interesting ways.
|
||||
size_t Mutate_ChangeBinaryInteger(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// CrossOver Data with some other element of the corpus.
|
||||
size_t Mutate_CrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Applies one of the configured mutations.
|
||||
/// Returns the new size of data which could be up to MaxSize.
|
||||
size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Applies one of the default mutations. Provided as a service
|
||||
/// to mutation authors.
|
||||
size_t DefaultMutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Creates a cross-over of two pieces of Data, returns its size.
|
||||
size_t CrossOver(const uint8_t *Data1, size_t Size1, const uint8_t *Data2,
|
||||
size_t Size2, uint8_t *Out, size_t MaxOutSize);
|
||||
|
||||
void AddWordToManualDictionary(const Word &W);
|
||||
|
||||
void PrintRecommendedDictionary();
|
||||
|
||||
void SetCorpus(const InputCorpus *Corpus) { this->Corpus = Corpus; }
|
||||
|
||||
Random &GetRand() { return Rand; }
|
||||
|
||||
private:
|
||||
|
||||
struct Mutator {
|
||||
size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max);
|
||||
const char *Name;
|
||||
};
|
||||
|
||||
size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
const std::vector<Mutator> &Mutators);
|
||||
|
||||
size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
|
||||
size_t ToSize, size_t MaxToSize);
|
||||
size_t CopyPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
|
||||
size_t ToSize);
|
||||
size_t ApplyDictionaryEntry(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
DictionaryEntry &DE);
|
||||
|
||||
template <class T>
|
||||
DictionaryEntry MakeDictionaryEntryFromCMP(T Arg1, T Arg2,
|
||||
const uint8_t *Data, size_t Size);
|
||||
DictionaryEntry MakeDictionaryEntryFromCMP(const Word &Arg1, const Word &Arg2,
|
||||
const uint8_t *Data, size_t Size);
|
||||
DictionaryEntry MakeDictionaryEntryFromCMP(const void *Arg1, const void *Arg2,
|
||||
const void *Arg1Mutation,
|
||||
const void *Arg2Mutation,
|
||||
size_t ArgSize,
|
||||
const uint8_t *Data, size_t Size);
|
||||
|
||||
Random &Rand;
|
||||
const FuzzingOptions Options;
|
||||
|
||||
// Dictionary provided by the user via -dict=DICT_FILE.
|
||||
Dictionary ManualDictionary;
|
||||
// Temporary dictionary modified by the fuzzer itself,
|
||||
// recreated periodically.
|
||||
Dictionary TempAutoDictionary;
|
||||
// Persistent dictionary modified by the fuzzer, consists of
|
||||
// entries that led to successfull discoveries in the past mutations.
|
||||
Dictionary PersistentAutoDictionary;
|
||||
|
||||
std::vector<Mutator> CurrentMutatorSequence;
|
||||
std::vector<DictionaryEntry *> CurrentDictionaryEntrySequence;
|
||||
|
||||
static const size_t kCmpDictionaryEntriesDequeSize = 16;
|
||||
DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize];
|
||||
size_t CmpDictionaryEntriesDequeIdx = 0;
|
||||
|
||||
const InputCorpus *Corpus = nullptr;
|
||||
std::vector<uint8_t> MutateInPlaceHere;
|
||||
// CustomCrossOver needs its own buffer as a custom implementation may call
|
||||
// LLVMFuzzerMutate, which in turn may resize MutateInPlaceHere.
|
||||
std::vector<uint8_t> CustomCrossOverInPlaceHere;
|
||||
|
||||
std::vector<Mutator> Mutators;
|
||||
std::vector<Mutator> DefaultMutators;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_MUTATE_H
|
@ -1,68 +0,0 @@
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::FuzzingOptions
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_OPTIONS_H
|
||||
#define LLVM_FUZZER_OPTIONS_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
struct FuzzingOptions {
|
||||
int Verbosity = 1;
|
||||
size_t MaxLen = 0;
|
||||
bool ExperimentalLenControl = false;
|
||||
int UnitTimeoutSec = 300;
|
||||
int TimeoutExitCode = 77;
|
||||
int ErrorExitCode = 77;
|
||||
int MaxTotalTimeSec = 0;
|
||||
int RssLimitMb = 0;
|
||||
bool DoCrossOver = true;
|
||||
int MutateDepth = 5;
|
||||
bool UseCounters = false;
|
||||
bool UseIndirCalls = true;
|
||||
bool UseMemmem = true;
|
||||
bool UseCmp = false;
|
||||
bool UseValueProfile = false;
|
||||
bool Shrink = false;
|
||||
bool ReduceInputs = false;
|
||||
int ReloadIntervalSec = 1;
|
||||
bool ShuffleAtStartUp = true;
|
||||
bool PreferSmall = true;
|
||||
size_t MaxNumberOfRuns = -1L;
|
||||
int ReportSlowUnits = 10;
|
||||
bool OnlyASCII = false;
|
||||
std::string OutputCorpus;
|
||||
std::string ArtifactPrefix = "./";
|
||||
std::string ExactArtifactPath;
|
||||
std::string ExitOnSrcPos;
|
||||
std::string ExitOnItem;
|
||||
bool SaveArtifacts = true;
|
||||
bool PrintNEW = true; // Print a status line when new units are found;
|
||||
bool PrintNewCovPcs = false;
|
||||
bool PrintFinalStats = false;
|
||||
bool PrintCorpusStats = false;
|
||||
bool PrintCoverage = false;
|
||||
bool DumpCoverage = false;
|
||||
bool DetectLeaks = true;
|
||||
int TraceMalloc = 0;
|
||||
bool HandleAbrt = false;
|
||||
bool HandleBus = false;
|
||||
bool HandleFpe = false;
|
||||
bool HandleIll = false;
|
||||
bool HandleInt = false;
|
||||
bool HandleSegv = false;
|
||||
bool HandleTerm = false;
|
||||
bool HandleXfsz = false;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_OPTIONS_H
|
@ -1,34 +0,0 @@
|
||||
//===- FuzzerRandom.h - Internal header for the Fuzzer ----------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::Random
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_RANDOM_H
|
||||
#define LLVM_FUZZER_RANDOM_H
|
||||
|
||||
#include <random>
|
||||
|
||||
namespace fuzzer {
|
||||
class Random : public std::mt19937 {
|
||||
public:
|
||||
Random(unsigned int seed) : std::mt19937(seed) {}
|
||||
result_type operator()() { return this->std::mt19937::operator()(); }
|
||||
size_t Rand() { return this->operator()(); }
|
||||
size_t RandBool() { return Rand() % 2; }
|
||||
size_t operator()(size_t n) { return n ? Rand() % n : 0; }
|
||||
intptr_t operator()(intptr_t From, intptr_t To) {
|
||||
assert(From < To);
|
||||
intptr_t RangeSize = To - From + 1;
|
||||
return operator()(RangeSize) + From;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_RANDOM_H
|
@ -1,222 +0,0 @@
|
||||
//===- FuzzerSHA1.h - Private copy of the SHA1 implementation ---*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// This code is taken from public domain
|
||||
// (http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c)
|
||||
// and modified by adding anonymous namespace, adding an interface
|
||||
// function fuzzer::ComputeSHA1() and removing unnecessary code.
|
||||
//
|
||||
// lib/Fuzzer can not use SHA1 implementation from openssl because
|
||||
// openssl may not be available and because we may be fuzzing openssl itself.
|
||||
// For the same reason we do not want to depend on SHA1 from LLVM tree.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerSHA1.h"
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
/* This code is public-domain - it is based on libcrypt
|
||||
* placed in the public domain by Wei Dai and other contributors.
|
||||
*/
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace { // Added for LibFuzzer
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
# define SHA_BIG_ENDIAN
|
||||
#elif defined __LITTLE_ENDIAN__
|
||||
/* override */
|
||||
#elif defined __BYTE_ORDER
|
||||
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define SHA_BIG_ENDIAN
|
||||
# endif
|
||||
#else // ! defined __LITTLE_ENDIAN__
|
||||
# include <endian.h> // machine/endian.h
|
||||
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define SHA_BIG_ENDIAN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
/* header */
|
||||
|
||||
#define HASH_LENGTH 20
|
||||
#define BLOCK_LENGTH 64
|
||||
|
||||
typedef struct sha1nfo {
|
||||
uint32_t buffer[BLOCK_LENGTH/4];
|
||||
uint32_t state[HASH_LENGTH/4];
|
||||
uint32_t byteCount;
|
||||
uint8_t bufferOffset;
|
||||
uint8_t keyBuffer[BLOCK_LENGTH];
|
||||
uint8_t innerHash[HASH_LENGTH];
|
||||
} sha1nfo;
|
||||
|
||||
/* public API - prototypes - TODO: doxygen*/
|
||||
|
||||
/**
|
||||
*/
|
||||
void sha1_init(sha1nfo *s);
|
||||
/**
|
||||
*/
|
||||
void sha1_writebyte(sha1nfo *s, uint8_t data);
|
||||
/**
|
||||
*/
|
||||
void sha1_write(sha1nfo *s, const char *data, size_t len);
|
||||
/**
|
||||
*/
|
||||
uint8_t* sha1_result(sha1nfo *s);
|
||||
|
||||
|
||||
/* code */
|
||||
#define SHA1_K0 0x5a827999
|
||||
#define SHA1_K20 0x6ed9eba1
|
||||
#define SHA1_K40 0x8f1bbcdc
|
||||
#define SHA1_K60 0xca62c1d6
|
||||
|
||||
void sha1_init(sha1nfo *s) {
|
||||
s->state[0] = 0x67452301;
|
||||
s->state[1] = 0xefcdab89;
|
||||
s->state[2] = 0x98badcfe;
|
||||
s->state[3] = 0x10325476;
|
||||
s->state[4] = 0xc3d2e1f0;
|
||||
s->byteCount = 0;
|
||||
s->bufferOffset = 0;
|
||||
}
|
||||
|
||||
uint32_t sha1_rol32(uint32_t number, uint8_t bits) {
|
||||
return ((number << bits) | (number >> (32-bits)));
|
||||
}
|
||||
|
||||
void sha1_hashBlock(sha1nfo *s) {
|
||||
uint8_t i;
|
||||
uint32_t a,b,c,d,e,t;
|
||||
|
||||
a=s->state[0];
|
||||
b=s->state[1];
|
||||
c=s->state[2];
|
||||
d=s->state[3];
|
||||
e=s->state[4];
|
||||
for (i=0; i<80; i++) {
|
||||
if (i>=16) {
|
||||
t = s->buffer[(i+13)&15] ^ s->buffer[(i+8)&15] ^ s->buffer[(i+2)&15] ^ s->buffer[i&15];
|
||||
s->buffer[i&15] = sha1_rol32(t,1);
|
||||
}
|
||||
if (i<20) {
|
||||
t = (d ^ (b & (c ^ d))) + SHA1_K0;
|
||||
} else if (i<40) {
|
||||
t = (b ^ c ^ d) + SHA1_K20;
|
||||
} else if (i<60) {
|
||||
t = ((b & c) | (d & (b | c))) + SHA1_K40;
|
||||
} else {
|
||||
t = (b ^ c ^ d) + SHA1_K60;
|
||||
}
|
||||
t+=sha1_rol32(a,5) + e + s->buffer[i&15];
|
||||
e=d;
|
||||
d=c;
|
||||
c=sha1_rol32(b,30);
|
||||
b=a;
|
||||
a=t;
|
||||
}
|
||||
s->state[0] += a;
|
||||
s->state[1] += b;
|
||||
s->state[2] += c;
|
||||
s->state[3] += d;
|
||||
s->state[4] += e;
|
||||
}
|
||||
|
||||
void sha1_addUncounted(sha1nfo *s, uint8_t data) {
|
||||
uint8_t * const b = (uint8_t*) s->buffer;
|
||||
#ifdef SHA_BIG_ENDIAN
|
||||
b[s->bufferOffset] = data;
|
||||
#else
|
||||
b[s->bufferOffset ^ 3] = data;
|
||||
#endif
|
||||
s->bufferOffset++;
|
||||
if (s->bufferOffset == BLOCK_LENGTH) {
|
||||
sha1_hashBlock(s);
|
||||
s->bufferOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void sha1_writebyte(sha1nfo *s, uint8_t data) {
|
||||
++s->byteCount;
|
||||
sha1_addUncounted(s, data);
|
||||
}
|
||||
|
||||
void sha1_write(sha1nfo *s, const char *data, size_t len) {
|
||||
for (;len--;) sha1_writebyte(s, (uint8_t) *data++);
|
||||
}
|
||||
|
||||
void sha1_pad(sha1nfo *s) {
|
||||
// Implement SHA-1 padding (fips180-2 §5.1.1)
|
||||
|
||||
// Pad with 0x80 followed by 0x00 until the end of the block
|
||||
sha1_addUncounted(s, 0x80);
|
||||
while (s->bufferOffset != 56) sha1_addUncounted(s, 0x00);
|
||||
|
||||
// Append length in the last 8 bytes
|
||||
sha1_addUncounted(s, 0); // We're only using 32 bit lengths
|
||||
sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths
|
||||
sha1_addUncounted(s, 0); // So zero pad the top bits
|
||||
sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8
|
||||
sha1_addUncounted(s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as
|
||||
sha1_addUncounted(s, s->byteCount >> 13); // byte.
|
||||
sha1_addUncounted(s, s->byteCount >> 5);
|
||||
sha1_addUncounted(s, s->byteCount << 3);
|
||||
}
|
||||
|
||||
uint8_t* sha1_result(sha1nfo *s) {
|
||||
// Pad to complete the last block
|
||||
sha1_pad(s);
|
||||
|
||||
#ifndef SHA_BIG_ENDIAN
|
||||
// Swap byte order back
|
||||
int i;
|
||||
for (i=0; i<5; i++) {
|
||||
s->state[i]=
|
||||
(((s->state[i])<<24)& 0xff000000)
|
||||
| (((s->state[i])<<8) & 0x00ff0000)
|
||||
| (((s->state[i])>>8) & 0x0000ff00)
|
||||
| (((s->state[i])>>24)& 0x000000ff);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Return pointer to hash (20 characters)
|
||||
return (uint8_t*) s->state;
|
||||
}
|
||||
|
||||
} // namespace; Added for LibFuzzer
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
// The rest is added for LibFuzzer
|
||||
void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out) {
|
||||
sha1nfo s;
|
||||
sha1_init(&s);
|
||||
sha1_write(&s, (const char*)Data, Len);
|
||||
memcpy(Out, sha1_result(&s), HASH_LENGTH);
|
||||
}
|
||||
|
||||
std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]) {
|
||||
std::stringstream SS;
|
||||
for (int i = 0; i < kSHA1NumBytes; i++)
|
||||
SS << std::hex << std::setfill('0') << std::setw(2) << (unsigned)Sha1[i];
|
||||
return SS.str();
|
||||
}
|
||||
|
||||
std::string Hash(const Unit &U) {
|
||||
uint8_t Hash[kSHA1NumBytes];
|
||||
ComputeSHA1(U.data(), U.size(), Hash);
|
||||
return Sha1ToString(Hash);
|
||||
}
|
||||
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
//===- FuzzerSHA1.h - Internal header for the SHA1 utils --------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SHA1 utils.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_SHA1_H
|
||||
#define LLVM_FUZZER_SHA1_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include <cstddef>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
// Private copy of SHA1 implementation.
|
||||
static const int kSHA1NumBytes = 20;
|
||||
|
||||
// Computes SHA1 hash of 'Len' bytes in 'Data', writes kSHA1NumBytes to 'Out'.
|
||||
void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out);
|
||||
|
||||
std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]);
|
||||
|
||||
std::string Hash(const Unit &U);
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_SHA1_H
|
@ -1,69 +0,0 @@
|
||||
//===- FuzzerShmem.h - shared memory interface ------------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SharedMemoryRegion
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_SHMEM_H
|
||||
#define LLVM_FUZZER_SHMEM_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
class SharedMemoryRegion {
|
||||
public:
|
||||
bool Create(const char *Name);
|
||||
bool Open(const char *Name);
|
||||
bool Destroy(const char *Name);
|
||||
uint8_t *GetData() { return Data; }
|
||||
void PostServer() {Post(0);}
|
||||
void WaitServer() {Wait(0);}
|
||||
void PostClient() {Post(1);}
|
||||
void WaitClient() {Wait(1);}
|
||||
|
||||
size_t WriteByteArray(const uint8_t *Bytes, size_t N) {
|
||||
assert(N <= kShmemSize - sizeof(N));
|
||||
memcpy(GetData(), &N, sizeof(N));
|
||||
memcpy(GetData() + sizeof(N), Bytes, N);
|
||||
assert(N == ReadByteArraySize());
|
||||
return N;
|
||||
}
|
||||
size_t ReadByteArraySize() {
|
||||
size_t Res;
|
||||
memcpy(&Res, GetData(), sizeof(Res));
|
||||
return Res;
|
||||
}
|
||||
uint8_t *GetByteArray() { return GetData() + sizeof(size_t); }
|
||||
|
||||
bool IsServer() const { return Data && IAmServer; }
|
||||
bool IsClient() const { return Data && !IAmServer; }
|
||||
|
||||
private:
|
||||
|
||||
static const size_t kShmemSize = 1 << 22;
|
||||
bool IAmServer;
|
||||
std::string Path(const char *Name);
|
||||
std::string SemName(const char *Name, int Idx);
|
||||
void Post(int Idx);
|
||||
void Wait(int Idx);
|
||||
|
||||
bool Map(int fd);
|
||||
uint8_t *Data = nullptr;
|
||||
void *Semaphore[2];
|
||||
};
|
||||
|
||||
extern SharedMemoryRegion SMR;
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_SHMEM_H
|
@ -1,103 +0,0 @@
|
||||
//===- FuzzerShmemPosix.cpp - Posix shared memory ---------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SharedMemoryRegion
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_POSIX
|
||||
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerShmem.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <semaphore.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
std::string SharedMemoryRegion::Path(const char *Name) {
|
||||
return DirPlusFile(TmpDir(), Name);
|
||||
}
|
||||
|
||||
std::string SharedMemoryRegion::SemName(const char *Name, int Idx) {
|
||||
std::string Res(Name);
|
||||
return Res + (char)('0' + Idx);
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Map(int fd) {
|
||||
Data =
|
||||
(uint8_t *)mmap(0, kShmemSize, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (Data == (uint8_t*)-1)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Create(const char *Name) {
|
||||
int fd = open(Path(Name).c_str(), O_CREAT | O_RDWR, 0777);
|
||||
if (fd < 0) return false;
|
||||
if (ftruncate(fd, kShmemSize) < 0) return false;
|
||||
if (!Map(fd))
|
||||
return false;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
sem_unlink(SemName(Name, i).c_str());
|
||||
Semaphore[i] = sem_open(SemName(Name, i).c_str(), O_CREAT, 0644, 0);
|
||||
if (Semaphore[i] == (void *)-1)
|
||||
return false;
|
||||
}
|
||||
IAmServer = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Open(const char *Name) {
|
||||
int fd = open(Path(Name).c_str(), O_RDWR);
|
||||
if (fd < 0) return false;
|
||||
struct stat stat_res;
|
||||
if (0 != fstat(fd, &stat_res))
|
||||
return false;
|
||||
assert(stat_res.st_size == kShmemSize);
|
||||
if (!Map(fd))
|
||||
return false;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Semaphore[i] = sem_open(SemName(Name, i).c_str(), 0);
|
||||
if (Semaphore[i] == (void *)-1)
|
||||
return false;
|
||||
}
|
||||
IAmServer = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Destroy(const char *Name) {
|
||||
return 0 == unlink(Path(Name).c_str());
|
||||
}
|
||||
|
||||
void SharedMemoryRegion::Post(int Idx) {
|
||||
assert(Idx == 0 || Idx == 1);
|
||||
sem_post((sem_t*)Semaphore[Idx]);
|
||||
}
|
||||
|
||||
void SharedMemoryRegion::Wait(int Idx) {
|
||||
assert(Idx == 0 || Idx == 1);
|
||||
for (int i = 0; i < 10 && sem_wait((sem_t*)Semaphore[Idx]); i++) {
|
||||
// sem_wait may fail if interrupted by a signal.
|
||||
sleep(i);
|
||||
if (i)
|
||||
Printf("%s: sem_wait[%d] failed %s\n", i < 9 ? "WARNING" : "ERROR", i,
|
||||
strerror(errno));
|
||||
if (i == 9) abort();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_POSIX
|
@ -1,64 +0,0 @@
|
||||
//===- FuzzerShmemWindows.cpp - Posix shared memory -------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SharedMemoryRegion
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_WINDOWS
|
||||
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerShmem.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
std::string SharedMemoryRegion::Path(const char *Name) {
|
||||
return DirPlusFile(TmpDir(), Name);
|
||||
}
|
||||
|
||||
std::string SharedMemoryRegion::SemName(const char *Name, int Idx) {
|
||||
std::string Res(Name);
|
||||
return Res + (char)('0' + Idx);
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Map(int fd) {
|
||||
assert(0 && "UNIMPLEMENTED");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Create(const char *Name) {
|
||||
assert(0 && "UNIMPLEMENTED");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Open(const char *Name) {
|
||||
assert(0 && "UNIMPLEMENTED");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Destroy(const char *Name) {
|
||||
assert(0 && "UNIMPLEMENTED");
|
||||
return false;
|
||||
}
|
||||
|
||||
void SharedMemoryRegion::Post(int Idx) {
|
||||
assert(0 && "UNIMPLEMENTED");
|
||||
}
|
||||
|
||||
void SharedMemoryRegion::Wait(int Idx) {
|
||||
Semaphore[1] = nullptr;
|
||||
assert(0 && "UNIMPLEMENTED");
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
@ -1,501 +0,0 @@
|
||||
//===- FuzzerTracePC.cpp - PC tracing--------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Trace PCs.
|
||||
// This module implements __sanitizer_cov_trace_pc_guard[_init],
|
||||
// the callback required for -fsanitize-coverage=trace-pc-guard instrumentation.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerTracePC.h"
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerDictionary.h"
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerUtil.h"
|
||||
#include "FuzzerValueBitMap.h"
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
// The coverage counters and PCs.
|
||||
// These are declared as global variables named "__sancov_*" to simplify
|
||||
// experiments with inlined instrumentation.
|
||||
alignas(64) ATTRIBUTE_INTERFACE
|
||||
uint8_t __sancov_trace_pc_guard_8bit_counters[fuzzer::TracePC::kNumPCs];
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
uintptr_t __sancov_trace_pc_pcs[fuzzer::TracePC::kNumPCs];
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
TracePC TPC;
|
||||
|
||||
int ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr;
|
||||
|
||||
uint8_t *TracePC::Counters() const {
|
||||
return __sancov_trace_pc_guard_8bit_counters;
|
||||
}
|
||||
|
||||
uintptr_t *TracePC::PCs() const {
|
||||
return __sancov_trace_pc_pcs;
|
||||
}
|
||||
|
||||
size_t TracePC::GetTotalPCCoverage() {
|
||||
size_t Res = 0;
|
||||
for (size_t i = 1, N = GetNumPCs(); i < N; i++)
|
||||
if (PCs()[i])
|
||||
Res++;
|
||||
return Res;
|
||||
}
|
||||
|
||||
|
||||
void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) {
|
||||
if (Start == Stop) return;
|
||||
if (NumModulesWithInline8bitCounters &&
|
||||
ModuleCounters[NumModulesWithInline8bitCounters-1].Start == Start) return;
|
||||
assert(NumModulesWithInline8bitCounters <
|
||||
sizeof(ModuleCounters) / sizeof(ModuleCounters[0]));
|
||||
ModuleCounters[NumModulesWithInline8bitCounters++] = {Start, Stop};
|
||||
NumInline8bitCounters += Stop - Start;
|
||||
}
|
||||
|
||||
void TracePC::HandleInit(uint32_t *Start, uint32_t *Stop) {
|
||||
if (Start == Stop || *Start) return;
|
||||
assert(NumModules < sizeof(Modules) / sizeof(Modules[0]));
|
||||
for (uint32_t *P = Start; P < Stop; P++) {
|
||||
NumGuards++;
|
||||
if (NumGuards == kNumPCs) {
|
||||
RawPrint(
|
||||
"WARNING: The binary has too many instrumented PCs.\n"
|
||||
" You may want to reduce the size of the binary\n"
|
||||
" for more efficient fuzzing and precise coverage data\n");
|
||||
}
|
||||
*P = NumGuards % kNumPCs;
|
||||
}
|
||||
Modules[NumModules].Start = Start;
|
||||
Modules[NumModules].Stop = Stop;
|
||||
NumModules++;
|
||||
}
|
||||
|
||||
void TracePC::PrintModuleInfo() {
|
||||
Printf("INFO: Loaded %zd modules (%zd guards): ", NumModules, NumGuards);
|
||||
for (size_t i = 0; i < NumModules; i++)
|
||||
Printf("[%p, %p), ", Modules[i].Start, Modules[i].Stop);
|
||||
Printf("\n");
|
||||
if (NumModulesWithInline8bitCounters) {
|
||||
Printf("INFO: Loaded %zd modules with %zd inline 8-bit counters\n",
|
||||
NumModulesWithInline8bitCounters, NumInline8bitCounters);
|
||||
for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++)
|
||||
Printf("[%p, %p), ", ModuleCounters[i].Start, ModuleCounters[i].Stop);
|
||||
Printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) {
|
||||
const uintptr_t kBits = 12;
|
||||
const uintptr_t kMask = (1 << kBits) - 1;
|
||||
uintptr_t Idx = (Caller & kMask) | ((Callee & kMask) << kBits);
|
||||
ValueProfileMap.AddValueModPrime(Idx);
|
||||
}
|
||||
|
||||
void TracePC::InitializePrintNewPCs() {
|
||||
if (!DoPrintNewPCs) return;
|
||||
assert(!PrintedPCs);
|
||||
PrintedPCs = new std::set<uintptr_t>;
|
||||
for (size_t i = 1; i < GetNumPCs(); i++)
|
||||
if (PCs()[i])
|
||||
PrintedPCs->insert(PCs()[i]);
|
||||
}
|
||||
|
||||
void TracePC::PrintNewPCs() {
|
||||
if (!DoPrintNewPCs) return;
|
||||
assert(PrintedPCs);
|
||||
for (size_t i = 1; i < GetNumPCs(); i++)
|
||||
if (PCs()[i] && PrintedPCs->insert(PCs()[i]).second)
|
||||
PrintPC("\tNEW_PC: %p %F %L\n", "\tNEW_PC: %p\n", PCs()[i]);
|
||||
}
|
||||
|
||||
void TracePC::PrintCoverage() {
|
||||
if (!EF->__sanitizer_symbolize_pc ||
|
||||
!EF->__sanitizer_get_module_and_offset_for_pc) {
|
||||
Printf("INFO: __sanitizer_symbolize_pc or "
|
||||
"__sanitizer_get_module_and_offset_for_pc is not available,"
|
||||
" not printing coverage\n");
|
||||
return;
|
||||
}
|
||||
std::map<std::string, std::vector<uintptr_t>> CoveredPCsPerModule;
|
||||
std::map<std::string, uintptr_t> ModuleOffsets;
|
||||
std::set<std::string> CoveredDirs, CoveredFiles, CoveredFunctions,
|
||||
CoveredLines;
|
||||
Printf("COVERAGE:\n");
|
||||
for (size_t i = 1; i < GetNumPCs(); i++) {
|
||||
uintptr_t PC = PCs()[i];
|
||||
if (!PC) continue;
|
||||
std::string FileStr = DescribePC("%s", PC);
|
||||
if (!IsInterestingCoverageFile(FileStr)) continue;
|
||||
std::string FixedPCStr = DescribePC("%p", PC);
|
||||
std::string FunctionStr = DescribePC("%F", PC);
|
||||
std::string LineStr = DescribePC("%l", PC);
|
||||
char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++?
|
||||
void *OffsetRaw = nullptr;
|
||||
if (!EF->__sanitizer_get_module_and_offset_for_pc(
|
||||
reinterpret_cast<void *>(PC), ModulePathRaw,
|
||||
sizeof(ModulePathRaw), &OffsetRaw))
|
||||
continue;
|
||||
std::string Module = ModulePathRaw;
|
||||
uintptr_t FixedPC = std::stoull(FixedPCStr, 0, 16);
|
||||
uintptr_t PcOffset = reinterpret_cast<uintptr_t>(OffsetRaw);
|
||||
ModuleOffsets[Module] = FixedPC - PcOffset;
|
||||
CoveredPCsPerModule[Module].push_back(PcOffset);
|
||||
CoveredFunctions.insert(FunctionStr);
|
||||
CoveredFiles.insert(FileStr);
|
||||
CoveredDirs.insert(DirName(FileStr));
|
||||
if (!CoveredLines.insert(FileStr + ":" + LineStr).second)
|
||||
continue;
|
||||
Printf("COVERED: %s %s:%s\n", FunctionStr.c_str(),
|
||||
FileStr.c_str(), LineStr.c_str());
|
||||
}
|
||||
|
||||
std::string CoveredDirsStr;
|
||||
for (auto &Dir : CoveredDirs) {
|
||||
if (!CoveredDirsStr.empty())
|
||||
CoveredDirsStr += ",";
|
||||
CoveredDirsStr += Dir;
|
||||
}
|
||||
Printf("COVERED_DIRS: %s\n", CoveredDirsStr.c_str());
|
||||
|
||||
for (auto &M : CoveredPCsPerModule) {
|
||||
std::set<std::string> UncoveredFiles, UncoveredFunctions;
|
||||
std::map<std::string, std::set<int> > UncoveredLines; // Func+File => lines
|
||||
auto &ModuleName = M.first;
|
||||
auto &CoveredOffsets = M.second;
|
||||
uintptr_t ModuleOffset = ModuleOffsets[ModuleName];
|
||||
std::sort(CoveredOffsets.begin(), CoveredOffsets.end());
|
||||
Printf("MODULE_WITH_COVERAGE: %s\n", ModuleName.c_str());
|
||||
// sancov does not yet fully support DSOs.
|
||||
// std::string Cmd = "sancov -print-coverage-pcs " + ModuleName;
|
||||
std::string Cmd = DisassembleCmd(ModuleName) + " | " +
|
||||
SearchRegexCmd("call.*__sanitizer_cov_trace_pc_guard");
|
||||
std::string SanCovOutput;
|
||||
if (!ExecuteCommandAndReadOutput(Cmd, &SanCovOutput)) {
|
||||
Printf("INFO: Command failed: %s\n", Cmd.c_str());
|
||||
continue;
|
||||
}
|
||||
std::istringstream ISS(SanCovOutput);
|
||||
std::string S;
|
||||
while (std::getline(ISS, S, '\n')) {
|
||||
size_t PcOffsetEnd = S.find(':');
|
||||
if (PcOffsetEnd == std::string::npos)
|
||||
continue;
|
||||
S.resize(PcOffsetEnd);
|
||||
uintptr_t PcOffset = std::stoull(S, 0, 16);
|
||||
if (!std::binary_search(CoveredOffsets.begin(), CoveredOffsets.end(),
|
||||
PcOffset)) {
|
||||
uintptr_t PC = ModuleOffset + PcOffset;
|
||||
auto FileStr = DescribePC("%s", PC);
|
||||
if (!IsInterestingCoverageFile(FileStr)) continue;
|
||||
if (CoveredFiles.count(FileStr) == 0) {
|
||||
UncoveredFiles.insert(FileStr);
|
||||
continue;
|
||||
}
|
||||
auto FunctionStr = DescribePC("%F", PC);
|
||||
if (CoveredFunctions.count(FunctionStr) == 0) {
|
||||
UncoveredFunctions.insert(FunctionStr);
|
||||
continue;
|
||||
}
|
||||
std::string LineStr = DescribePC("%l", PC);
|
||||
uintptr_t Line = std::stoi(LineStr);
|
||||
std::string FileLineStr = FileStr + ":" + LineStr;
|
||||
if (CoveredLines.count(FileLineStr) == 0)
|
||||
UncoveredLines[FunctionStr + " " + FileStr].insert(Line);
|
||||
}
|
||||
}
|
||||
for (auto &FileLine: UncoveredLines)
|
||||
for (int Line : FileLine.second)
|
||||
Printf("UNCOVERED_LINE: %s:%d\n", FileLine.first.c_str(), Line);
|
||||
for (auto &Func : UncoveredFunctions)
|
||||
Printf("UNCOVERED_FUNC: %s\n", Func.c_str());
|
||||
for (auto &File : UncoveredFiles)
|
||||
Printf("UNCOVERED_FILE: %s\n", File.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) {
|
||||
// TODO: this implementation is x86 only.
|
||||
// see sanitizer_common GetPreviousInstructionPc for full implementation.
|
||||
return PC - 1;
|
||||
}
|
||||
|
||||
void TracePC::DumpCoverage() {
|
||||
if (EF->__sanitizer_dump_coverage) {
|
||||
std::vector<uintptr_t> PCsCopy(GetNumPCs());
|
||||
for (size_t i = 0; i < GetNumPCs(); i++)
|
||||
PCsCopy[i] = PCs()[i] ? GetPreviousInstructionPc(PCs()[i]) : 0;
|
||||
EF->__sanitizer_dump_coverage(PCsCopy.data(), PCsCopy.size());
|
||||
}
|
||||
}
|
||||
|
||||
// Value profile.
|
||||
// We keep track of various values that affect control flow.
|
||||
// These values are inserted into a bit-set-based hash map.
|
||||
// Every new bit in the map is treated as a new coverage.
|
||||
//
|
||||
// For memcmp/strcmp/etc the interesting value is the length of the common
|
||||
// prefix of the parameters.
|
||||
// For cmp instructions the interesting value is a XOR of the parameters.
|
||||
// The interesting value is mixed up with the PC and is then added to the map.
|
||||
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2,
|
||||
size_t n, bool StopAtZero) {
|
||||
if (!n) return;
|
||||
size_t Len = std::min(n, Word::GetMaxSize());
|
||||
const uint8_t *A1 = reinterpret_cast<const uint8_t *>(s1);
|
||||
const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2);
|
||||
uint8_t B1[Word::kMaxSize];
|
||||
uint8_t B2[Word::kMaxSize];
|
||||
// Copy the data into locals in this non-msan-instrumented function
|
||||
// to avoid msan complaining further.
|
||||
size_t Hash = 0; // Compute some simple hash of both strings.
|
||||
for (size_t i = 0; i < Len; i++) {
|
||||
B1[i] = A1[i];
|
||||
B2[i] = A2[i];
|
||||
size_t T = B1[i];
|
||||
Hash ^= (T << 8) | B2[i];
|
||||
}
|
||||
size_t I = 0;
|
||||
for (; I < Len; I++)
|
||||
if (B1[I] != B2[I] || (StopAtZero && B1[I] == 0))
|
||||
break;
|
||||
size_t PC = reinterpret_cast<size_t>(caller_pc);
|
||||
size_t Idx = (PC & 4095) | (I << 12);
|
||||
ValueProfileMap.AddValue(Idx);
|
||||
TORCW.Insert(Idx ^ Hash, Word(B1, Len), Word(B2, Len));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
ATTRIBUTE_TARGET_POPCNT ALWAYS_INLINE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void TracePC::HandleCmp(uintptr_t PC, T Arg1, T Arg2) {
|
||||
uint64_t ArgXor = Arg1 ^ Arg2;
|
||||
uint64_t ArgDistance = __builtin_popcountll(ArgXor) + 1; // [1,65]
|
||||
uintptr_t Idx = ((PC & 4095) + 1) * ArgDistance;
|
||||
if (sizeof(T) == 4)
|
||||
TORC4.Insert(ArgXor, Arg1, Arg2);
|
||||
else if (sizeof(T) == 8)
|
||||
TORC8.Insert(ArgXor, Arg1, Arg2);
|
||||
ValueProfileMap.AddValue(Idx);
|
||||
}
|
||||
|
||||
static size_t InternalStrnlen(const char *S, size_t MaxLen) {
|
||||
size_t Len = 0;
|
||||
for (; Len < MaxLen && S[Len]; Len++) {}
|
||||
return Len;
|
||||
}
|
||||
|
||||
// Finds min of (strlen(S1), strlen(S2)).
|
||||
// Needed bacause one of these strings may actually be non-zero terminated.
|
||||
static size_t InternalStrnlen2(const char *S1, const char *S2) {
|
||||
size_t Len = 0;
|
||||
for (; S1[Len] && S2[Len]; Len++) {}
|
||||
return Len;
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
extern "C" {
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uint32_t Idx = *Guard;
|
||||
__sancov_trace_pc_pcs[Idx] = PC;
|
||||
__sancov_trace_pc_guard_8bit_counters[Idx]++;
|
||||
}
|
||||
|
||||
// Best-effort support for -fsanitize-coverage=trace-pc, which is available
|
||||
// in both Clang and GCC.
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void __sanitizer_cov_trace_pc() {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t Idx = PC & (((uintptr_t)1 << fuzzer::TracePC::kTracePcBits) - 1);
|
||||
__sancov_trace_pc_pcs[Idx] = PC;
|
||||
__sancov_trace_pc_guard_8bit_counters[Idx]++;
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) {
|
||||
fuzzer::TPC.HandleInit(Start, Stop);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
void __sanitizer_cov_8bit_counters_init(uint8_t *Start, uint8_t *Stop) {
|
||||
fuzzer::TPC.HandleInline8bitCountersInit(Start, Stop);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCallerCallee(PC, Callee);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) {
|
||||
uint64_t N = Cases[0];
|
||||
uint64_t ValSizeInBits = Cases[1];
|
||||
uint64_t *Vals = Cases + 2;
|
||||
// Skip the most common and the most boring case.
|
||||
if (Vals[N - 1] < 256 && Val < 256)
|
||||
return;
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
size_t i;
|
||||
uint64_t Token = 0;
|
||||
for (i = 0; i < N; i++) {
|
||||
Token = Val ^ Vals[i];
|
||||
if (Val < Vals[i])
|
||||
break;
|
||||
}
|
||||
|
||||
if (ValSizeInBits == 16)
|
||||
fuzzer::TPC.HandleCmp(PC + i, static_cast<uint16_t>(Token), (uint16_t)(0));
|
||||
else if (ValSizeInBits == 32)
|
||||
fuzzer::TPC.HandleCmp(PC + i, static_cast<uint32_t>(Token), (uint32_t)(0));
|
||||
else
|
||||
fuzzer::TPC.HandleCmp(PC + i, Token, (uint64_t)(0));
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_div4(uint32_t Val) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Val, (uint32_t)0);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_div8(uint64_t Val) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Val, (uint64_t)0);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_gep(uintptr_t Idx) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Idx, (uintptr_t)0);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1,
|
||||
const void *s2, size_t n, int result) {
|
||||
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
|
||||
if (result == 0) return; // No reason to mutate.
|
||||
if (n <= 1) return; // Not interesting.
|
||||
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/false);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1,
|
||||
const char *s2, size_t n, int result) {
|
||||
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
|
||||
if (result == 0) return; // No reason to mutate.
|
||||
size_t Len1 = fuzzer::InternalStrnlen(s1, n);
|
||||
size_t Len2 = fuzzer::InternalStrnlen(s2, n);
|
||||
n = std::min(n, Len1);
|
||||
n = std::min(n, Len2);
|
||||
if (n <= 1) return; // Not interesting.
|
||||
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/true);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1,
|
||||
const char *s2, int result) {
|
||||
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
|
||||
if (result == 0) return; // No reason to mutate.
|
||||
size_t N = fuzzer::InternalStrnlen2(s1, s2);
|
||||
if (N <= 1) return; // Not interesting.
|
||||
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, N, /*StopAtZero*/true);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1,
|
||||
const char *s2, size_t n, int result) {
|
||||
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
|
||||
return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1,
|
||||
const char *s2, int result) {
|
||||
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
|
||||
return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1,
|
||||
const char *s2, char *result) {
|
||||
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
|
||||
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1,
|
||||
const char *s2, char *result) {
|
||||
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
|
||||
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1,
|
||||
const void *s2, size_t len2, void *result) {
|
||||
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
|
||||
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), len2);
|
||||
}
|
||||
} // extern "C"
|
@ -1,210 +0,0 @@
|
||||
//===- FuzzerTracePC.h - Internal header for the Fuzzer ---------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::TracePC
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_TRACE_PC
|
||||
#define LLVM_FUZZER_TRACE_PC
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerDictionary.h"
|
||||
#include "FuzzerValueBitMap.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
// TableOfRecentCompares (TORC) remembers the most recently performed
|
||||
// comparisons of type T.
|
||||
// We record the arguments of CMP instructions in this table unconditionally
|
||||
// because it seems cheaper this way than to compute some expensive
|
||||
// conditions inside __sanitizer_cov_trace_cmp*.
|
||||
// After the unit has been executed we may decide to use the contents of
|
||||
// this table to populate a Dictionary.
|
||||
template<class T, size_t kSizeT>
|
||||
struct TableOfRecentCompares {
|
||||
static const size_t kSize = kSizeT;
|
||||
struct Pair {
|
||||
T A, B;
|
||||
};
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void Insert(size_t Idx, const T &Arg1, const T &Arg2) {
|
||||
Idx = Idx % kSize;
|
||||
Table[Idx].A = Arg1;
|
||||
Table[Idx].B = Arg2;
|
||||
}
|
||||
|
||||
Pair Get(size_t I) { return Table[I % kSize]; }
|
||||
|
||||
Pair Table[kSize];
|
||||
};
|
||||
|
||||
template <size_t kSizeT>
|
||||
struct MemMemTable {
|
||||
static const size_t kSize = kSizeT;
|
||||
Word MemMemWords[kSize];
|
||||
Word EmptyWord;
|
||||
|
||||
void Add(const uint8_t *Data, size_t Size) {
|
||||
if (Size <= 2) return;
|
||||
Size = std::min(Size, Word::GetMaxSize());
|
||||
size_t Idx = SimpleFastHash(Data, Size) % kSize;
|
||||
MemMemWords[Idx].Set(Data, Size);
|
||||
}
|
||||
const Word &Get(size_t Idx) {
|
||||
for (size_t i = 0; i < kSize; i++) {
|
||||
const Word &W = MemMemWords[(Idx + i) % kSize];
|
||||
if (W.size()) return W;
|
||||
}
|
||||
EmptyWord.Set(nullptr, 0);
|
||||
return EmptyWord;
|
||||
}
|
||||
};
|
||||
|
||||
class TracePC {
|
||||
public:
|
||||
static const size_t kNumPCs = 1 << 21;
|
||||
// How many bits of PC are used from __sanitizer_cov_trace_pc.
|
||||
static const size_t kTracePcBits = 18;
|
||||
|
||||
void HandleInit(uint32_t *Start, uint32_t *Stop);
|
||||
void HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop);
|
||||
void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee);
|
||||
template <class T> void HandleCmp(uintptr_t PC, T Arg1, T Arg2);
|
||||
size_t GetTotalPCCoverage();
|
||||
void SetUseCounters(bool UC) { UseCounters = UC; }
|
||||
void SetUseValueProfile(bool VP) { UseValueProfile = VP; }
|
||||
void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; }
|
||||
template <class Callback> void CollectFeatures(Callback CB) const;
|
||||
|
||||
void ResetMaps() {
|
||||
ValueProfileMap.Reset();
|
||||
memset(Counters(), 0, GetNumPCs());
|
||||
ClearExtraCounters();
|
||||
}
|
||||
|
||||
void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize);
|
||||
void PrintFeatureSet();
|
||||
|
||||
void PrintModuleInfo();
|
||||
|
||||
void PrintCoverage();
|
||||
void DumpCoverage();
|
||||
|
||||
void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2,
|
||||
size_t n, bool StopAtZero);
|
||||
|
||||
TableOfRecentCompares<uint32_t, 32> TORC4;
|
||||
TableOfRecentCompares<uint64_t, 32> TORC8;
|
||||
TableOfRecentCompares<Word, 32> TORCW;
|
||||
MemMemTable<1024> MMT;
|
||||
|
||||
void PrintNewPCs();
|
||||
void InitializePrintNewPCs();
|
||||
size_t GetNumPCs() const {
|
||||
return NumGuards == 0 ? (1 << kTracePcBits) : Min(kNumPCs, NumGuards + 1);
|
||||
}
|
||||
uintptr_t GetPC(size_t Idx) {
|
||||
assert(Idx < GetNumPCs());
|
||||
return PCs()[Idx];
|
||||
}
|
||||
|
||||
private:
|
||||
bool UseCounters = false;
|
||||
bool UseValueProfile = false;
|
||||
bool DoPrintNewPCs = false;
|
||||
|
||||
struct Module {
|
||||
uint32_t *Start, *Stop;
|
||||
};
|
||||
|
||||
Module Modules[4096];
|
||||
size_t NumModules; // linker-initialized.
|
||||
size_t NumGuards; // linker-initialized.
|
||||
|
||||
struct { uint8_t *Start, *Stop; } ModuleCounters[4096];
|
||||
size_t NumModulesWithInline8bitCounters; // linker-initialized.
|
||||
size_t NumInline8bitCounters;
|
||||
|
||||
uint8_t *Counters() const;
|
||||
uintptr_t *PCs() const;
|
||||
|
||||
std::set<uintptr_t> *PrintedPCs;
|
||||
|
||||
ValueBitMap ValueProfileMap;
|
||||
};
|
||||
|
||||
template <class Callback> // void Callback(size_t Idx, uint8_t Value);
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void ForEachNonZeroByte(const uint8_t *Begin, const uint8_t *End,
|
||||
size_t FirstFeature, Callback Handle8bitCounter) {
|
||||
typedef uintptr_t LargeType;
|
||||
const size_t Step = sizeof(LargeType) / sizeof(uint8_t);
|
||||
const size_t StepMask = Step - 1;
|
||||
auto P = Begin;
|
||||
// Iterate by 1 byte until either the alignment boundary or the end.
|
||||
for (; reinterpret_cast<uintptr_t>(P) & StepMask && P < End; P++)
|
||||
if (uint8_t V = *P)
|
||||
Handle8bitCounter(FirstFeature + P - Begin, V);
|
||||
|
||||
// Iterate by Step bytes at a time.
|
||||
for (; P < End; P += Step)
|
||||
if (LargeType Bundle = *reinterpret_cast<const LargeType *>(P))
|
||||
for (size_t I = 0; I < Step; I++, Bundle >>= 8)
|
||||
if (uint8_t V = Bundle & 0xff)
|
||||
Handle8bitCounter(FirstFeature + P - Begin + I, V);
|
||||
|
||||
// Iterate by 1 byte until the end.
|
||||
for (; P < End; P++)
|
||||
if (uint8_t V = *P)
|
||||
Handle8bitCounter(FirstFeature + P - Begin, V);
|
||||
}
|
||||
|
||||
template <class Callback> // bool Callback(size_t Feature)
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
__attribute__((noinline))
|
||||
void TracePC::CollectFeatures(Callback HandleFeature) const {
|
||||
uint8_t *Counters = this->Counters();
|
||||
size_t N = GetNumPCs();
|
||||
auto Handle8bitCounter = [&](size_t Idx, uint8_t Counter) {
|
||||
assert(Counter);
|
||||
unsigned Bit = 0;
|
||||
/**/ if (Counter >= 128) Bit = 7;
|
||||
else if (Counter >= 32) Bit = 6;
|
||||
else if (Counter >= 16) Bit = 5;
|
||||
else if (Counter >= 8) Bit = 4;
|
||||
else if (Counter >= 4) Bit = 3;
|
||||
else if (Counter >= 3) Bit = 2;
|
||||
else if (Counter >= 2) Bit = 1;
|
||||
HandleFeature(Idx * 8 + Bit);
|
||||
};
|
||||
|
||||
size_t FirstFeature = 0;
|
||||
ForEachNonZeroByte(Counters, Counters + N, FirstFeature, Handle8bitCounter);
|
||||
FirstFeature += N * 8;
|
||||
for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
|
||||
ForEachNonZeroByte(ModuleCounters[i].Start, ModuleCounters[i].Stop,
|
||||
FirstFeature, Handle8bitCounter);
|
||||
FirstFeature += 8 * (ModuleCounters[i].Stop - ModuleCounters[i].Start);
|
||||
}
|
||||
|
||||
ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(), FirstFeature,
|
||||
Handle8bitCounter);
|
||||
|
||||
if (UseValueProfile)
|
||||
ValueProfileMap.ForEach([&](size_t Idx) {
|
||||
HandleFeature(N * 8 + Idx);
|
||||
});
|
||||
}
|
||||
|
||||
extern TracePC TPC;
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_TRACE_PC
|
@ -1,225 +0,0 @@
|
||||
//===- FuzzerUtil.cpp - Misc utils ----------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Misc utils.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerUtil.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <thread>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
void PrintHexArray(const uint8_t *Data, size_t Size,
|
||||
const char *PrintAfter) {
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
Printf("0x%x,", (unsigned)Data[i]);
|
||||
Printf("%s", PrintAfter);
|
||||
}
|
||||
|
||||
void Print(const Unit &v, const char *PrintAfter) {
|
||||
PrintHexArray(v.data(), v.size(), PrintAfter);
|
||||
}
|
||||
|
||||
void PrintASCIIByte(uint8_t Byte) {
|
||||
if (Byte == '\\')
|
||||
Printf("\\\\");
|
||||
else if (Byte == '"')
|
||||
Printf("\\\"");
|
||||
else if (Byte >= 32 && Byte < 127)
|
||||
Printf("%c", Byte);
|
||||
else
|
||||
Printf("\\x%02x", Byte);
|
||||
}
|
||||
|
||||
void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter) {
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
PrintASCIIByte(Data[i]);
|
||||
Printf("%s", PrintAfter);
|
||||
}
|
||||
|
||||
void PrintASCII(const Unit &U, const char *PrintAfter) {
|
||||
PrintASCII(U.data(), U.size(), PrintAfter);
|
||||
}
|
||||
|
||||
bool ToASCII(uint8_t *Data, size_t Size) {
|
||||
bool Changed = false;
|
||||
for (size_t i = 0; i < Size; i++) {
|
||||
uint8_t &X = Data[i];
|
||||
auto NewX = X;
|
||||
NewX &= 127;
|
||||
if (!isspace(NewX) && !isprint(NewX))
|
||||
NewX = ' ';
|
||||
Changed |= NewX != X;
|
||||
X = NewX;
|
||||
}
|
||||
return Changed;
|
||||
}
|
||||
|
||||
bool IsASCII(const Unit &U) { return IsASCII(U.data(), U.size()); }
|
||||
|
||||
bool IsASCII(const uint8_t *Data, size_t Size) {
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
if (!(isprint(Data[i]) || isspace(Data[i]))) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) {
|
||||
U->clear();
|
||||
if (Str.empty()) return false;
|
||||
size_t L = 0, R = Str.size() - 1; // We are parsing the range [L,R].
|
||||
// Skip spaces from both sides.
|
||||
while (L < R && isspace(Str[L])) L++;
|
||||
while (R > L && isspace(Str[R])) R--;
|
||||
if (R - L < 2) return false;
|
||||
// Check the closing "
|
||||
if (Str[R] != '"') return false;
|
||||
R--;
|
||||
// Find the opening "
|
||||
while (L < R && Str[L] != '"') L++;
|
||||
if (L >= R) return false;
|
||||
assert(Str[L] == '\"');
|
||||
L++;
|
||||
assert(L <= R);
|
||||
for (size_t Pos = L; Pos <= R; Pos++) {
|
||||
uint8_t V = (uint8_t)Str[Pos];
|
||||
if (!isprint(V) && !isspace(V)) return false;
|
||||
if (V =='\\') {
|
||||
// Handle '\\'
|
||||
if (Pos + 1 <= R && (Str[Pos + 1] == '\\' || Str[Pos + 1] == '"')) {
|
||||
U->push_back(Str[Pos + 1]);
|
||||
Pos++;
|
||||
continue;
|
||||
}
|
||||
// Handle '\xAB'
|
||||
if (Pos + 3 <= R && Str[Pos + 1] == 'x'
|
||||
&& isxdigit(Str[Pos + 2]) && isxdigit(Str[Pos + 3])) {
|
||||
char Hex[] = "0xAA";
|
||||
Hex[2] = Str[Pos + 2];
|
||||
Hex[3] = Str[Pos + 3];
|
||||
U->push_back(strtol(Hex, nullptr, 16));
|
||||
Pos += 3;
|
||||
continue;
|
||||
}
|
||||
return false; // Invalid escape.
|
||||
} else {
|
||||
// Any other character.
|
||||
U->push_back(V);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseDictionaryFile(const std::string &Text, std::vector<Unit> *Units) {
|
||||
if (Text.empty()) {
|
||||
Printf("ParseDictionaryFile: file does not exist or is empty\n");
|
||||
return false;
|
||||
}
|
||||
std::istringstream ISS(Text);
|
||||
Units->clear();
|
||||
Unit U;
|
||||
int LineNo = 0;
|
||||
std::string S;
|
||||
while (std::getline(ISS, S, '\n')) {
|
||||
LineNo++;
|
||||
size_t Pos = 0;
|
||||
while (Pos < S.size() && isspace(S[Pos])) Pos++; // Skip spaces.
|
||||
if (Pos == S.size()) continue; // Empty line.
|
||||
if (S[Pos] == '#') continue; // Comment line.
|
||||
if (ParseOneDictionaryEntry(S, &U)) {
|
||||
Units->push_back(U);
|
||||
} else {
|
||||
Printf("ParseDictionaryFile: error in line %d\n\t\t%s\n", LineNo,
|
||||
S.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Base64(const Unit &U) {
|
||||
static const char Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
std::string Res;
|
||||
size_t i;
|
||||
for (i = 0; i + 2 < U.size(); i += 3) {
|
||||
uint32_t x = (U[i] << 16) + (U[i + 1] << 8) + U[i + 2];
|
||||
Res += Table[(x >> 18) & 63];
|
||||
Res += Table[(x >> 12) & 63];
|
||||
Res += Table[(x >> 6) & 63];
|
||||
Res += Table[x & 63];
|
||||
}
|
||||
if (i + 1 == U.size()) {
|
||||
uint32_t x = (U[i] << 16);
|
||||
Res += Table[(x >> 18) & 63];
|
||||
Res += Table[(x >> 12) & 63];
|
||||
Res += "==";
|
||||
} else if (i + 2 == U.size()) {
|
||||
uint32_t x = (U[i] << 16) + (U[i + 1] << 8);
|
||||
Res += Table[(x >> 18) & 63];
|
||||
Res += Table[(x >> 12) & 63];
|
||||
Res += Table[(x >> 6) & 63];
|
||||
Res += "=";
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) {
|
||||
if (!EF->__sanitizer_symbolize_pc) return "<can not symbolize>";
|
||||
char PcDescr[1024];
|
||||
EF->__sanitizer_symbolize_pc(reinterpret_cast<void*>(PC),
|
||||
SymbolizedFMT, PcDescr, sizeof(PcDescr));
|
||||
PcDescr[sizeof(PcDescr) - 1] = 0; // Just in case.
|
||||
return PcDescr;
|
||||
}
|
||||
|
||||
void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) {
|
||||
if (EF->__sanitizer_symbolize_pc)
|
||||
Printf("%s", DescribePC(SymbolizedFMT, PC).c_str());
|
||||
else
|
||||
Printf(FallbackFMT, PC);
|
||||
}
|
||||
|
||||
unsigned NumberOfCpuCores() {
|
||||
unsigned N = std::thread::hardware_concurrency();
|
||||
if (!N) {
|
||||
Printf("WARNING: std::thread::hardware_concurrency not well defined for "
|
||||
"your platform. Assuming CPU count of 1.\n");
|
||||
N = 1;
|
||||
}
|
||||
return N;
|
||||
}
|
||||
|
||||
bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out) {
|
||||
FILE *Pipe = OpenProcessPipe(Command.c_str(), "r");
|
||||
if (!Pipe) return false;
|
||||
char Buff[1024];
|
||||
size_t N;
|
||||
while ((N = fread(Buff, 1, sizeof(Buff), Pipe)) > 0)
|
||||
Out->append(Buff, N);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t SimpleFastHash(const uint8_t *Data, size_t Size) {
|
||||
size_t Res = 0;
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
Res = Res * 11 + Data[i];
|
||||
return Res;
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
@ -1,86 +0,0 @@
|
||||
//===- FuzzerUtil.h - Internal header for the Fuzzer Utils ------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Util functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_UTIL_H
|
||||
#define LLVM_FUZZER_UTIL_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
void PrintHexArray(const Unit &U, const char *PrintAfter = "");
|
||||
|
||||
void PrintHexArray(const uint8_t *Data, size_t Size,
|
||||
const char *PrintAfter = "");
|
||||
|
||||
void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter = "");
|
||||
|
||||
void PrintASCII(const Unit &U, const char *PrintAfter = "");
|
||||
|
||||
// Changes U to contain only ASCII (isprint+isspace) characters.
|
||||
// Returns true iff U has been changed.
|
||||
bool ToASCII(uint8_t *Data, size_t Size);
|
||||
|
||||
bool IsASCII(const Unit &U);
|
||||
|
||||
bool IsASCII(const uint8_t *Data, size_t Size);
|
||||
|
||||
std::string Base64(const Unit &U);
|
||||
|
||||
void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC);
|
||||
|
||||
std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC);
|
||||
|
||||
unsigned NumberOfCpuCores();
|
||||
|
||||
bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out);
|
||||
|
||||
// Platform specific functions.
|
||||
void SetSignalHandler(const FuzzingOptions& Options);
|
||||
|
||||
void SleepSeconds(int Seconds);
|
||||
|
||||
unsigned long GetPid();
|
||||
|
||||
size_t GetPeakRSSMb();
|
||||
|
||||
int ExecuteCommand(const std::string &Command);
|
||||
|
||||
FILE *OpenProcessPipe(const char *Command, const char *Mode);
|
||||
|
||||
const void *SearchMemory(const void *haystack, size_t haystacklen,
|
||||
const void *needle, size_t needlelen);
|
||||
|
||||
std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
|
||||
const char *X1, const char *X2);
|
||||
|
||||
inline std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
|
||||
const char *X) {
|
||||
return CloneArgsWithoutX(Args, X, X);
|
||||
}
|
||||
|
||||
inline std::pair<std::string, std::string> SplitBefore(std::string X,
|
||||
std::string S) {
|
||||
auto Pos = S.find(X);
|
||||
if (Pos == std::string::npos)
|
||||
return std::make_pair(S, "");
|
||||
return std::make_pair(S.substr(0, Pos), S.substr(Pos));
|
||||
}
|
||||
|
||||
std::string DisassembleCmd(const std::string &FileName);
|
||||
|
||||
std::string SearchRegexCmd(const std::string &Regex);
|
||||
|
||||
size_t SimpleFastHash(const uint8_t *Data, size_t Size);
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_UTIL_H
|
@ -1,161 +0,0 @@
|
||||
//===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Misc utils for Darwin.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_APPLE
|
||||
|
||||
#include "FuzzerIO.h"
|
||||
#include <mutex>
|
||||
#include <signal.h>
|
||||
#include <spawn.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
// There is no header for this on macOS so declare here
|
||||
extern "C" char **environ;
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
static std::mutex SignalMutex;
|
||||
// Global variables used to keep track of how signal handling should be
|
||||
// restored. They should **not** be accessed without holding `SignalMutex`.
|
||||
static int ActiveThreadCount = 0;
|
||||
static struct sigaction OldSigIntAction;
|
||||
static struct sigaction OldSigQuitAction;
|
||||
static sigset_t OldBlockedSignalsSet;
|
||||
|
||||
// This is a reimplementation of Libc's `system()`. On Darwin the Libc
|
||||
// implementation contains a mutex which prevents it from being used
|
||||
// concurrently. This implementation **can** be used concurrently. It sets the
|
||||
// signal handlers when the first thread enters and restores them when the last
|
||||
// thread finishes execution of the function and ensures this is not racey by
|
||||
// using a mutex.
|
||||
int ExecuteCommand(const std::string &Command) {
|
||||
posix_spawnattr_t SpawnAttributes;
|
||||
if (posix_spawnattr_init(&SpawnAttributes))
|
||||
return -1;
|
||||
// Block and ignore signals of the current process when the first thread
|
||||
// enters.
|
||||
{
|
||||
std::lock_guard<std::mutex> Lock(SignalMutex);
|
||||
if (ActiveThreadCount == 0) {
|
||||
static struct sigaction IgnoreSignalAction;
|
||||
sigset_t BlockedSignalsSet;
|
||||
memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction));
|
||||
IgnoreSignalAction.sa_handler = SIG_IGN;
|
||||
|
||||
if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) {
|
||||
Printf("Failed to ignore SIGINT\n");
|
||||
(void)posix_spawnattr_destroy(&SpawnAttributes);
|
||||
return -1;
|
||||
}
|
||||
if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) {
|
||||
Printf("Failed to ignore SIGQUIT\n");
|
||||
// Try our best to restore the signal handlers.
|
||||
(void)sigaction(SIGINT, &OldSigIntAction, NULL);
|
||||
(void)posix_spawnattr_destroy(&SpawnAttributes);
|
||||
return -1;
|
||||
}
|
||||
|
||||
(void)sigemptyset(&BlockedSignalsSet);
|
||||
(void)sigaddset(&BlockedSignalsSet, SIGCHLD);
|
||||
if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) ==
|
||||
-1) {
|
||||
Printf("Failed to block SIGCHLD\n");
|
||||
// Try our best to restore the signal handlers.
|
||||
(void)sigaction(SIGQUIT, &OldSigQuitAction, NULL);
|
||||
(void)sigaction(SIGINT, &OldSigIntAction, NULL);
|
||||
(void)posix_spawnattr_destroy(&SpawnAttributes);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
++ActiveThreadCount;
|
||||
}
|
||||
|
||||
// NOTE: Do not introduce any new `return` statements past this
|
||||
// point. It is important that `ActiveThreadCount` always be decremented
|
||||
// when leaving this function.
|
||||
|
||||
// Make sure the child process uses the default handlers for the
|
||||
// following signals rather than inheriting what the parent has.
|
||||
sigset_t DefaultSigSet;
|
||||
(void)sigemptyset(&DefaultSigSet);
|
||||
(void)sigaddset(&DefaultSigSet, SIGQUIT);
|
||||
(void)sigaddset(&DefaultSigSet, SIGINT);
|
||||
(void)posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet);
|
||||
// Make sure the child process doesn't block SIGCHLD
|
||||
(void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet);
|
||||
short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
|
||||
(void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags);
|
||||
|
||||
pid_t Pid;
|
||||
char **Environ = environ; // Read from global
|
||||
const char *CommandCStr = Command.c_str();
|
||||
char *const Argv[] = {
|
||||
strdup("sh"),
|
||||
strdup("-c"),
|
||||
strdup(CommandCStr),
|
||||
NULL
|
||||
};
|
||||
int ErrorCode = 0, ProcessStatus = 0;
|
||||
// FIXME: We probably shouldn't hardcode the shell path.
|
||||
ErrorCode = posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes,
|
||||
Argv, Environ);
|
||||
(void)posix_spawnattr_destroy(&SpawnAttributes);
|
||||
if (!ErrorCode) {
|
||||
pid_t SavedPid = Pid;
|
||||
do {
|
||||
// Repeat until call completes uninterrupted.
|
||||
Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0);
|
||||
} while (Pid == -1 && errno == EINTR);
|
||||
if (Pid == -1) {
|
||||
// Fail for some other reason.
|
||||
ProcessStatus = -1;
|
||||
}
|
||||
} else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) {
|
||||
// Fork failure.
|
||||
ProcessStatus = -1;
|
||||
} else {
|
||||
// Shell execution failure.
|
||||
ProcessStatus = W_EXITCODE(127, 0);
|
||||
}
|
||||
for (unsigned i = 0, n = sizeof(Argv) / sizeof(Argv[0]); i < n; ++i)
|
||||
free(Argv[i]);
|
||||
|
||||
// Restore the signal handlers of the current process when the last thread
|
||||
// using this function finishes.
|
||||
{
|
||||
std::lock_guard<std::mutex> Lock(SignalMutex);
|
||||
--ActiveThreadCount;
|
||||
if (ActiveThreadCount == 0) {
|
||||
bool FailedRestore = false;
|
||||
if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) {
|
||||
Printf("Failed to restore SIGINT handling\n");
|
||||
FailedRestore = true;
|
||||
}
|
||||
if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) {
|
||||
Printf("Failed to restore SIGQUIT handling\n");
|
||||
FailedRestore = true;
|
||||
}
|
||||
if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) {
|
||||
Printf("Failed to unblock SIGCHLD\n");
|
||||
FailedRestore = true;
|
||||
}
|
||||
if (FailedRestore)
|
||||
ProcessStatus = -1;
|
||||
}
|
||||
}
|
||||
return ProcessStatus;
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_APPLE
|
@ -1,24 +0,0 @@
|
||||
//===- FuzzerUtilLinux.cpp - Misc utils for Linux. ------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Misc utils for Linux.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_LINUX
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
int ExecuteCommand(const std::string &Command) {
|
||||
return system(Command.c_str());
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_LINUX
|
@ -1,144 +0,0 @@
|
||||
//===- FuzzerUtilPosix.cpp - Misc utils for Posix. ------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Misc utils implementation using Posix API.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_POSIX
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <iomanip>
|
||||
#include <signal.h>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
static void AlarmHandler(int, siginfo_t *, void *) {
|
||||
Fuzzer::StaticAlarmCallback();
|
||||
}
|
||||
|
||||
static void CrashHandler(int, siginfo_t *, void *) {
|
||||
Fuzzer::StaticCrashSignalCallback();
|
||||
}
|
||||
|
||||
static void InterruptHandler(int, siginfo_t *, void *) {
|
||||
Fuzzer::StaticInterruptCallback();
|
||||
}
|
||||
|
||||
static void FileSizeExceedHandler(int, siginfo_t *, void *) {
|
||||
Fuzzer::StaticFileSizeExceedCallback();
|
||||
}
|
||||
|
||||
static void SetSigaction(int signum,
|
||||
void (*callback)(int, siginfo_t *, void *)) {
|
||||
struct sigaction sigact = {};
|
||||
if (sigaction(signum, nullptr, &sigact)) {
|
||||
Printf("libFuzzer: sigaction failed with %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
if (sigact.sa_flags & SA_SIGINFO) {
|
||||
if (sigact.sa_sigaction)
|
||||
return;
|
||||
} else {
|
||||
if (sigact.sa_handler != SIG_DFL && sigact.sa_handler != SIG_IGN &&
|
||||
sigact.sa_handler != SIG_ERR)
|
||||
return;
|
||||
}
|
||||
|
||||
sigact = {};
|
||||
sigact.sa_sigaction = callback;
|
||||
if (sigaction(signum, &sigact, 0)) {
|
||||
Printf("libFuzzer: sigaction failed with %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void SetTimer(int Seconds) {
|
||||
struct itimerval T {
|
||||
{Seconds, 0}, { Seconds, 0 }
|
||||
};
|
||||
if (setitimer(ITIMER_REAL, &T, nullptr)) {
|
||||
Printf("libFuzzer: setitimer failed with %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
SetSigaction(SIGALRM, AlarmHandler);
|
||||
}
|
||||
|
||||
void SetSignalHandler(const FuzzingOptions& Options) {
|
||||
if (Options.UnitTimeoutSec > 0)
|
||||
SetTimer(Options.UnitTimeoutSec / 2 + 1);
|
||||
if (Options.HandleInt)
|
||||
SetSigaction(SIGINT, InterruptHandler);
|
||||
if (Options.HandleTerm)
|
||||
SetSigaction(SIGTERM, InterruptHandler);
|
||||
if (Options.HandleSegv)
|
||||
SetSigaction(SIGSEGV, CrashHandler);
|
||||
if (Options.HandleBus)
|
||||
SetSigaction(SIGBUS, CrashHandler);
|
||||
if (Options.HandleAbrt)
|
||||
SetSigaction(SIGABRT, CrashHandler);
|
||||
if (Options.HandleIll)
|
||||
SetSigaction(SIGILL, CrashHandler);
|
||||
if (Options.HandleFpe)
|
||||
SetSigaction(SIGFPE, CrashHandler);
|
||||
if (Options.HandleXfsz)
|
||||
SetSigaction(SIGXFSZ, FileSizeExceedHandler);
|
||||
}
|
||||
|
||||
void SleepSeconds(int Seconds) {
|
||||
sleep(Seconds); // Use C API to avoid coverage from instrumented libc++.
|
||||
}
|
||||
|
||||
unsigned long GetPid() { return (unsigned long)getpid(); }
|
||||
|
||||
size_t GetPeakRSSMb() {
|
||||
struct rusage usage;
|
||||
if (getrusage(RUSAGE_SELF, &usage))
|
||||
return 0;
|
||||
if (LIBFUZZER_LINUX) {
|
||||
// ru_maxrss is in KiB
|
||||
return usage.ru_maxrss >> 10;
|
||||
} else if (LIBFUZZER_APPLE) {
|
||||
// ru_maxrss is in bytes
|
||||
return usage.ru_maxrss >> 20;
|
||||
}
|
||||
assert(0 && "GetPeakRSSMb() is not implemented for your platform");
|
||||
return 0;
|
||||
}
|
||||
|
||||
FILE *OpenProcessPipe(const char *Command, const char *Mode) {
|
||||
return popen(Command, Mode);
|
||||
}
|
||||
|
||||
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
|
||||
size_t PattLen) {
|
||||
return memmem(Data, DataLen, Patt, PattLen);
|
||||
}
|
||||
|
||||
std::string DisassembleCmd(const std::string &FileName) {
|
||||
return "objdump -d " + FileName;
|
||||
}
|
||||
|
||||
std::string SearchRegexCmd(const std::string &Regex) {
|
||||
return "grep '" + Regex + "'";
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_POSIX
|
@ -1,193 +0,0 @@
|
||||
//===- FuzzerUtilWindows.cpp - Misc utils for Windows. --------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Misc utils implementation for Windows.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_WINDOWS
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <iomanip>
|
||||
#include <signal.h>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <windows.h>
|
||||
|
||||
// This must be included after windows.h.
|
||||
#include <Psapi.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
static const FuzzingOptions* HandlerOpt = nullptr;
|
||||
|
||||
static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) {
|
||||
switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
||||
case EXCEPTION_STACK_OVERFLOW:
|
||||
if (HandlerOpt->HandleSegv)
|
||||
Fuzzer::StaticCrashSignalCallback();
|
||||
break;
|
||||
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
||||
case EXCEPTION_IN_PAGE_ERROR:
|
||||
if (HandlerOpt->HandleBus)
|
||||
Fuzzer::StaticCrashSignalCallback();
|
||||
break;
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||
case EXCEPTION_PRIV_INSTRUCTION:
|
||||
if (HandlerOpt->HandleIll)
|
||||
Fuzzer::StaticCrashSignalCallback();
|
||||
break;
|
||||
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||
case EXCEPTION_FLT_INEXACT_RESULT:
|
||||
case EXCEPTION_FLT_INVALID_OPERATION:
|
||||
case EXCEPTION_FLT_OVERFLOW:
|
||||
case EXCEPTION_FLT_STACK_CHECK:
|
||||
case EXCEPTION_FLT_UNDERFLOW:
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
case EXCEPTION_INT_OVERFLOW:
|
||||
if (HandlerOpt->HandleFpe)
|
||||
Fuzzer::StaticCrashSignalCallback();
|
||||
break;
|
||||
// TODO: handle (Options.HandleXfsz)
|
||||
}
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
BOOL WINAPI CtrlHandler(DWORD dwCtrlType) {
|
||||
switch (dwCtrlType) {
|
||||
case CTRL_C_EVENT:
|
||||
if (HandlerOpt->HandleInt)
|
||||
Fuzzer::StaticInterruptCallback();
|
||||
return TRUE;
|
||||
case CTRL_BREAK_EVENT:
|
||||
if (HandlerOpt->HandleTerm)
|
||||
Fuzzer::StaticInterruptCallback();
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CALLBACK AlarmHandler(PVOID, BOOLEAN) {
|
||||
Fuzzer::StaticAlarmCallback();
|
||||
}
|
||||
|
||||
class TimerQ {
|
||||
HANDLE TimerQueue;
|
||||
public:
|
||||
TimerQ() : TimerQueue(NULL) {};
|
||||
~TimerQ() {
|
||||
if (TimerQueue)
|
||||
DeleteTimerQueueEx(TimerQueue, NULL);
|
||||
};
|
||||
void SetTimer(int Seconds) {
|
||||
if (!TimerQueue) {
|
||||
TimerQueue = CreateTimerQueue();
|
||||
if (!TimerQueue) {
|
||||
Printf("libFuzzer: CreateTimerQueue failed.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
HANDLE Timer;
|
||||
if (!CreateTimerQueueTimer(&Timer, TimerQueue, AlarmHandler, NULL,
|
||||
Seconds*1000, Seconds*1000, 0)) {
|
||||
Printf("libFuzzer: CreateTimerQueueTimer failed.\n");
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
static TimerQ Timer;
|
||||
|
||||
static void CrashHandler(int) { Fuzzer::StaticCrashSignalCallback(); }
|
||||
|
||||
void SetSignalHandler(const FuzzingOptions& Options) {
|
||||
HandlerOpt = &Options;
|
||||
|
||||
if (Options.UnitTimeoutSec > 0)
|
||||
Timer.SetTimer(Options.UnitTimeoutSec / 2 + 1);
|
||||
|
||||
if (Options.HandleInt || Options.HandleTerm)
|
||||
if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) {
|
||||
DWORD LastError = GetLastError();
|
||||
Printf("libFuzzer: SetConsoleCtrlHandler failed (Error code: %lu).\n",
|
||||
LastError);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (Options.HandleSegv || Options.HandleBus || Options.HandleIll ||
|
||||
Options.HandleFpe)
|
||||
SetUnhandledExceptionFilter(ExceptionHandler);
|
||||
|
||||
if (Options.HandleAbrt)
|
||||
if (SIG_ERR == signal(SIGABRT, CrashHandler)) {
|
||||
Printf("libFuzzer: signal failed with %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void SleepSeconds(int Seconds) { Sleep(Seconds * 1000); }
|
||||
|
||||
unsigned long GetPid() { return GetCurrentProcessId(); }
|
||||
|
||||
size_t GetPeakRSSMb() {
|
||||
PROCESS_MEMORY_COUNTERS info;
|
||||
if (!GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)))
|
||||
return 0;
|
||||
return info.PeakWorkingSetSize >> 20;
|
||||
}
|
||||
|
||||
FILE *OpenProcessPipe(const char *Command, const char *Mode) {
|
||||
return _popen(Command, Mode);
|
||||
}
|
||||
|
||||
int ExecuteCommand(const std::string &Command) {
|
||||
return system(Command.c_str());
|
||||
}
|
||||
|
||||
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
|
||||
size_t PattLen) {
|
||||
// TODO: make this implementation more efficient.
|
||||
const char *Cdata = (const char *)Data;
|
||||
const char *Cpatt = (const char *)Patt;
|
||||
|
||||
if (!Data || !Patt || DataLen == 0 || PattLen == 0 || DataLen < PattLen)
|
||||
return NULL;
|
||||
|
||||
if (PattLen == 1)
|
||||
return memchr(Data, *Cpatt, DataLen);
|
||||
|
||||
const char *End = Cdata + DataLen - PattLen + 1;
|
||||
|
||||
for (const char *It = Cdata; It < End; ++It)
|
||||
if (It[0] == Cpatt[0] && memcmp(It, Cpatt, PattLen) == 0)
|
||||
return It;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string DisassembleCmd(const std::string &FileName) {
|
||||
if (ExecuteCommand("dumpbin /summary > nul") == 0)
|
||||
return "dumpbin /disasm " + FileName;
|
||||
Printf("libFuzzer: couldn't find tool to disassemble (dumpbin)\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::string SearchRegexCmd(const std::string &Regex) {
|
||||
return "findstr /r \"" + Regex + "\"";
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
@ -1,94 +0,0 @@
|
||||
//===- FuzzerValueBitMap.h - INTERNAL - Bit map -----------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ValueBitMap.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_VALUE_BIT_MAP_H
|
||||
#define LLVM_FUZZER_VALUE_BIT_MAP_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
// A bit map containing kMapSizeInWords bits.
|
||||
struct ValueBitMap {
|
||||
static const size_t kMapSizeInBits = 1 << 16;
|
||||
static const size_t kMapPrimeMod = 65371; // Largest Prime < kMapSizeInBits;
|
||||
static const size_t kBitsInWord = (sizeof(uintptr_t) * 8);
|
||||
static const size_t kMapSizeInWords = kMapSizeInBits / kBitsInWord;
|
||||
public:
|
||||
|
||||
// Clears all bits.
|
||||
void Reset() { memset(Map, 0, sizeof(Map)); }
|
||||
|
||||
// Computes a hash function of Value and sets the corresponding bit.
|
||||
// Returns true if the bit was changed from 0 to 1.
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
inline bool AddValue(uintptr_t Value) {
|
||||
uintptr_t Idx = Value % kMapSizeInBits;
|
||||
uintptr_t WordIdx = Idx / kBitsInWord;
|
||||
uintptr_t BitIdx = Idx % kBitsInWord;
|
||||
uintptr_t Old = Map[WordIdx];
|
||||
uintptr_t New = Old | (1UL << BitIdx);
|
||||
Map[WordIdx] = New;
|
||||
return New != Old;
|
||||
}
|
||||
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
inline bool AddValueModPrime(uintptr_t Value) {
|
||||
return AddValue(Value % kMapPrimeMod);
|
||||
}
|
||||
|
||||
inline bool Get(uintptr_t Idx) {
|
||||
assert(Idx < kMapSizeInBits);
|
||||
uintptr_t WordIdx = Idx / kBitsInWord;
|
||||
uintptr_t BitIdx = Idx % kBitsInWord;
|
||||
return Map[WordIdx] & (1UL << BitIdx);
|
||||
}
|
||||
|
||||
size_t GetNumBitsSinceLastMerge() const { return NumBits; }
|
||||
|
||||
// Merges 'Other' into 'this', clears 'Other', updates NumBits,
|
||||
// returns true if new bits were added.
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
bool MergeFrom(ValueBitMap &Other) {
|
||||
uintptr_t Res = 0;
|
||||
size_t OldNumBits = NumBits;
|
||||
for (size_t i = 0; i < kMapSizeInWords; i++) {
|
||||
auto O = Other.Map[i];
|
||||
auto M = Map[i];
|
||||
if (O) {
|
||||
Map[i] = (M |= O);
|
||||
Other.Map[i] = 0;
|
||||
}
|
||||
if (M)
|
||||
Res += __builtin_popcountll(M);
|
||||
}
|
||||
NumBits = Res;
|
||||
return OldNumBits < NumBits;
|
||||
}
|
||||
|
||||
template <class Callback>
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void ForEach(Callback CB) const {
|
||||
for (size_t i = 0; i < kMapSizeInWords; i++)
|
||||
if (uintptr_t M = Map[i])
|
||||
for (size_t j = 0; j < sizeof(M) * 8; j++)
|
||||
if (M & ((uintptr_t)1 << j))
|
||||
CB(i * sizeof(M) * 8 + j);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t NumBits = 0;
|
||||
uintptr_t Map[kMapSizeInWords] __attribute__((aligned(512)));
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_VALUE_BIT_MAP_H
|
@ -1,335 +0,0 @@
|
||||
//===- afl_driver.cpp - a glue between AFL and libFuzzer --------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/* This file allows to fuzz libFuzzer-style target functions
|
||||
(LLVMFuzzerTestOneInput) with AFL using AFL's persistent (in-process) mode.
|
||||
|
||||
Usage:
|
||||
################################################################################
|
||||
cat << EOF > test_fuzzer.cc
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
if (size > 0 && data[0] == 'H')
|
||||
if (size > 1 && data[1] == 'I')
|
||||
if (size > 2 && data[2] == '!')
|
||||
__builtin_trap();
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
# Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang.
|
||||
clang -g -fsanitize-coverage=trace-pc-guard test_fuzzer.cc -c
|
||||
# Build afl-llvm-rt.o.c from the AFL distribution.
|
||||
clang -c -w $AFL_HOME/llvm_mode/afl-llvm-rt.o.c
|
||||
# Build this file, link it with afl-llvm-rt.o.o and the target code.
|
||||
clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o
|
||||
# Run AFL:
|
||||
rm -rf IN OUT; mkdir IN OUT; echo z > IN/z;
|
||||
$AFL_HOME/afl-fuzz -i IN -o OUT ./a.out
|
||||
################################################################################
|
||||
Environment Variables:
|
||||
There are a few environment variables that can be set to use features that
|
||||
afl-fuzz doesn't have.
|
||||
|
||||
AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file
|
||||
specified. If the file does not exist, it is created. This is useful for getting
|
||||
stack traces (when using ASAN for example) or original error messages on hard to
|
||||
reproduce bugs.
|
||||
|
||||
AFL_DRIVER_EXTRA_STATS_FILENAME: Setting this causes afl_driver to write extra
|
||||
statistics to the file specified. Currently these are peak_rss_mb
|
||||
(the peak amount of virtual memory used in MB) and slowest_unit_time_secs. If
|
||||
the file does not exist it is created. If the file does exist then
|
||||
afl_driver assumes it was restarted by afl-fuzz and will try to read old
|
||||
statistics from the file. If that fails then the process will quit.
|
||||
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
// Platform detection. Copied from FuzzerInternal.h
|
||||
#ifdef __linux__
|
||||
#define LIBFUZZER_LINUX 1
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#elif __APPLE__
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_APPLE 1
|
||||
#else
|
||||
#error "Support for your platform has not been implemented"
|
||||
#endif
|
||||
|
||||
// Used to avoid repeating error checking boilerplate. If cond is false, a
|
||||
// fatal error has occured in the program. In this event print error_message
|
||||
// to stderr and abort(). Otherwise do nothing. Note that setting
|
||||
// AFL_DRIVER_STDERR_DUPLICATE_FILENAME may cause error_message to be appended
|
||||
// to the file as well, if the error occurs after the duplication is performed.
|
||||
#define CHECK_ERROR(cond, error_message) \
|
||||
if (!(cond)) { \
|
||||
fprintf(stderr, (error_message)); \
|
||||
abort(); \
|
||||
}
|
||||
|
||||
// libFuzzer interface is thin, so we don't include any libFuzzer headers.
|
||||
extern "C" {
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
|
||||
__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv);
|
||||
}
|
||||
|
||||
// Notify AFL about persistent mode.
|
||||
static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##";
|
||||
extern "C" int __afl_persistent_loop(unsigned int);
|
||||
static volatile char suppress_warning2 = AFL_PERSISTENT[0];
|
||||
|
||||
// Notify AFL about deferred forkserver.
|
||||
static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##";
|
||||
extern "C" void __afl_manual_init();
|
||||
static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0];
|
||||
|
||||
// Input buffer.
|
||||
static const size_t kMaxAflInputSize = 1 << 20;
|
||||
static uint8_t AflInputBuf[kMaxAflInputSize];
|
||||
|
||||
// Variables we need for writing to the extra stats file.
|
||||
static FILE *extra_stats_file = NULL;
|
||||
static uint32_t previous_peak_rss = 0;
|
||||
static time_t slowest_unit_time_secs = 0;
|
||||
static const int kNumExtraStats = 2;
|
||||
static const char *kExtraStatsFormatString = "peak_rss_mb : %u\n"
|
||||
"slowest_unit_time_sec : %u\n";
|
||||
|
||||
// Copied from FuzzerUtil.cpp.
|
||||
size_t GetPeakRSSMb() {
|
||||
struct rusage usage;
|
||||
if (getrusage(RUSAGE_SELF, &usage))
|
||||
return 0;
|
||||
if (LIBFUZZER_LINUX) {
|
||||
// ru_maxrss is in KiB
|
||||
return usage.ru_maxrss >> 10;
|
||||
} else if (LIBFUZZER_APPLE) {
|
||||
// ru_maxrss is in bytes
|
||||
return usage.ru_maxrss >> 20;
|
||||
}
|
||||
assert(0 && "GetPeakRSSMb() is not implemented for your platform");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Based on SetSigaction in FuzzerUtil.cpp
|
||||
static void SetSigaction(int signum,
|
||||
void (*callback)(int, siginfo_t *, void *)) {
|
||||
struct sigaction sigact;
|
||||
memset(&sigact, 0, sizeof(sigact));
|
||||
sigact.sa_sigaction = callback;
|
||||
if (sigaction(signum, &sigact, 0)) {
|
||||
fprintf(stderr, "libFuzzer: sigaction failed with %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Write extra stats to the file specified by the user. If none is specified
|
||||
// this function will never be called.
|
||||
static void write_extra_stats() {
|
||||
uint32_t peak_rss = GetPeakRSSMb();
|
||||
|
||||
if (peak_rss < previous_peak_rss)
|
||||
peak_rss = previous_peak_rss;
|
||||
|
||||
int chars_printed = fprintf(extra_stats_file, kExtraStatsFormatString,
|
||||
peak_rss, slowest_unit_time_secs);
|
||||
|
||||
CHECK_ERROR(chars_printed != 0, "Failed to write extra_stats_file");
|
||||
|
||||
CHECK_ERROR(fclose(extra_stats_file) == 0,
|
||||
"Failed to close extra_stats_file");
|
||||
}
|
||||
|
||||
// Call write_extra_stats before we exit.
|
||||
static void crash_handler(int, siginfo_t *, void *) {
|
||||
// Make sure we don't try calling write_extra_stats again if we crashed while
|
||||
// trying to call it.
|
||||
static bool first_crash = true;
|
||||
CHECK_ERROR(first_crash,
|
||||
"Crashed in crash signal handler. This is a bug in the fuzzer.");
|
||||
|
||||
first_crash = false;
|
||||
write_extra_stats();
|
||||
}
|
||||
|
||||
// If the user has specified an extra_stats_file through the environment
|
||||
// variable AFL_DRIVER_EXTRA_STATS_FILENAME, then perform necessary set up
|
||||
// to write stats to it on exit. If no file is specified, do nothing. Otherwise
|
||||
// install signal and exit handlers to write to the file when the process exits.
|
||||
// Then if the file doesn't exist create it and set extra stats to 0. But if it
|
||||
// does exist then read the initial values of the extra stats from the file
|
||||
// and check that the file is writable.
|
||||
static void maybe_initialize_extra_stats() {
|
||||
// If AFL_DRIVER_EXTRA_STATS_FILENAME isn't set then we have nothing to do.
|
||||
char *extra_stats_filename = getenv("AFL_DRIVER_EXTRA_STATS_FILENAME");
|
||||
if (!extra_stats_filename)
|
||||
return;
|
||||
|
||||
// Open the file and find the previous peak_rss_mb value.
|
||||
// This is necessary because the fuzzing process is restarted after N
|
||||
// iterations are completed. So we may need to get this value from a previous
|
||||
// process to be accurate.
|
||||
extra_stats_file = fopen(extra_stats_filename, "r");
|
||||
|
||||
// If extra_stats_file already exists: read old stats from it.
|
||||
if (extra_stats_file) {
|
||||
int matches = fscanf(extra_stats_file, kExtraStatsFormatString,
|
||||
&previous_peak_rss, &slowest_unit_time_secs);
|
||||
|
||||
// Make sure we have read a real extra stats file and that we have used it
|
||||
// to set slowest_unit_time_secs and previous_peak_rss.
|
||||
CHECK_ERROR(matches == kNumExtraStats, "Extra stats file is corrupt");
|
||||
|
||||
CHECK_ERROR(fclose(extra_stats_file) == 0, "Failed to close file");
|
||||
|
||||
// Now open the file for writing.
|
||||
extra_stats_file = fopen(extra_stats_filename, "w");
|
||||
CHECK_ERROR(extra_stats_file,
|
||||
"Failed to open extra stats file for writing");
|
||||
} else {
|
||||
// Looks like this is the first time in a fuzzing job this is being called.
|
||||
extra_stats_file = fopen(extra_stats_filename, "w+");
|
||||
CHECK_ERROR(extra_stats_file, "failed to create extra stats file");
|
||||
}
|
||||
|
||||
// Make sure that crash_handler gets called on any kind of fatal error.
|
||||
int crash_signals[] = {SIGSEGV, SIGBUS, SIGABRT, SIGILL, SIGFPE, SIGINT,
|
||||
SIGTERM};
|
||||
|
||||
const size_t num_signals = sizeof(crash_signals) / sizeof(crash_signals[0]);
|
||||
|
||||
for (size_t idx = 0; idx < num_signals; idx++)
|
||||
SetSigaction(crash_signals[idx], crash_handler);
|
||||
|
||||
// Make sure it gets called on other kinds of exits.
|
||||
atexit(write_extra_stats);
|
||||
}
|
||||
|
||||
// If the user asks us to duplicate stderr, then do it.
|
||||
static void maybe_duplicate_stderr() {
|
||||
char* stderr_duplicate_filename =
|
||||
getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
|
||||
|
||||
if (!stderr_duplicate_filename)
|
||||
return;
|
||||
|
||||
FILE* stderr_duplicate_stream =
|
||||
freopen(stderr_duplicate_filename, "a+", stderr);
|
||||
|
||||
if (!stderr_duplicate_stream) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
// Define LLVMFuzzerMutate to avoid link failures for targets that use it
|
||||
// with libFuzzer's LLVMFuzzerCustomMutator.
|
||||
extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
assert(false && "LLVMFuzzerMutate should not be called from afl_driver");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Execute any files provided as parameters.
|
||||
int ExecuteFilesOnyByOne(int argc, char **argv) {
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::ifstream in(argv[i]);
|
||||
in.seekg(0, in.end);
|
||||
size_t length = in.tellg();
|
||||
in.seekg (0, in.beg);
|
||||
std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
|
||||
// Allocate exactly length bytes so that we reliably catch buffer overflows.
|
||||
std::vector<char> bytes(length);
|
||||
in.read(bytes.data(), bytes.size());
|
||||
assert(in);
|
||||
LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
|
||||
bytes.size());
|
||||
std::cout << "Execution successfull" << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
fprintf(stderr,
|
||||
"======================= INFO =========================\n"
|
||||
"This binary is built for AFL-fuzz.\n"
|
||||
"To run the target function on individual input(s) execute this:\n"
|
||||
" %s < INPUT_FILE\n"
|
||||
"or\n"
|
||||
" %s INPUT_FILE1 [INPUT_FILE2 ... ]\n"
|
||||
"To fuzz with afl-fuzz execute this:\n"
|
||||
" afl-fuzz [afl-flags] %s [-N]\n"
|
||||
"afl-fuzz will run N iterations before "
|
||||
"re-spawning the process (default: 1000)\n"
|
||||
"======================================================\n",
|
||||
argv[0], argv[0], argv[0]);
|
||||
if (LLVMFuzzerInitialize)
|
||||
LLVMFuzzerInitialize(&argc, &argv);
|
||||
// Do any other expensive one-time initialization here.
|
||||
|
||||
maybe_duplicate_stderr();
|
||||
maybe_initialize_extra_stats();
|
||||
|
||||
__afl_manual_init();
|
||||
|
||||
int N = 1000;
|
||||
if (argc == 2 && argv[1][0] == '-')
|
||||
N = atoi(argv[1] + 1);
|
||||
else if(argc == 2 && (N = atoi(argv[1])) > 0)
|
||||
fprintf(stderr, "WARNING: using the deprecated call style `%s %d`\n",
|
||||
argv[0], N);
|
||||
else if (argc > 1)
|
||||
return ExecuteFilesOnyByOne(argc, argv);
|
||||
|
||||
assert(N > 0);
|
||||
time_t unit_time_secs;
|
||||
int num_runs = 0;
|
||||
while (__afl_persistent_loop(N)) {
|
||||
ssize_t n_read = read(0, AflInputBuf, kMaxAflInputSize);
|
||||
if (n_read > 0) {
|
||||
// Copy AflInputBuf into a separate buffer to let asan find buffer
|
||||
// overflows. Don't use unique_ptr/etc to avoid extra dependencies.
|
||||
uint8_t *copy = new uint8_t[n_read];
|
||||
memcpy(copy, AflInputBuf, n_read);
|
||||
|
||||
struct timeval unit_start_time;
|
||||
CHECK_ERROR(gettimeofday(&unit_start_time, NULL) == 0,
|
||||
"Calling gettimeofday failed");
|
||||
|
||||
num_runs++;
|
||||
LLVMFuzzerTestOneInput(copy, n_read);
|
||||
|
||||
struct timeval unit_stop_time;
|
||||
CHECK_ERROR(gettimeofday(&unit_stop_time, NULL) == 0,
|
||||
"Calling gettimeofday failed");
|
||||
|
||||
// Update slowest_unit_time_secs if we see a new max.
|
||||
unit_time_secs = unit_stop_time.tv_sec - unit_start_time.tv_sec;
|
||||
if (slowest_unit_time_secs < unit_time_secs)
|
||||
slowest_unit_time_secs = unit_time_secs;
|
||||
|
||||
delete[] copy;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "%s: successfully executed %d input(s)\n", argv[0], num_runs);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user