Commit 6ef79494 by Andrew Scull

Add UBSAN build option and fix undefined behaviour errors.

1. The assembler tried to write to unaligned addresses but memcpy fixes this. 2. The InstKind and OperandKind enums allowed target specific kinds that did not fall in the defined range. Defining the maximum target kind in the enum sorts this problem. BUG= R=stichnot@chromium.org Review URL: https://codereview.chromium.org/1311653003 .
parent 2c688f62
...@@ -96,6 +96,18 @@ else ...@@ -96,6 +96,18 @@ else
OBJDIR := $(OBJDIR)+Asserts OBJDIR := $(OBJDIR)+Asserts
endif endif
ifdef UBSAN
OBJDIR := $(OBJDIR)+UBSan
CXX_EXTRA += -fsanitize=undefined -fno-sanitize=vptr -fno-sanitize=nonnull-attribute
LD_EXTRA += -fsanitize=undefined
endif
ifdef UBSAN_TRAP
OBJDIR := $(OBJDIR)+UBSan_Trap
CXX_EXTRA += -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error -fno-sanitize=vptr -fno-sanitize=nonnull-attribute
LD_EXTRA += -fsanitize=undefined-trap
endif
ifdef TSAN ifdef TSAN
OBJDIR := $(OBJDIR)+TSan OBJDIR := $(OBJDIR)+TSan
CXX_EXTRA += -fsanitize=thread CXX_EXTRA += -fsanitize=thread
...@@ -108,6 +120,12 @@ ifdef ASAN ...@@ -108,6 +120,12 @@ ifdef ASAN
LD_EXTRA += -fsanitize=address LD_EXTRA += -fsanitize=address
endif endif
ifdef MSAN
OBJDIR := $(OBJDIR)+MSan
CXX_EXTRA += -fsanitize=memory
LD_EXTRA += -fsanitize=memory
endif
SB_OBJDIR := $(OBJDIR)+Sandboxed SB_OBJDIR := $(OBJDIR)+Sandboxed
$(info -----------------------------------------------) $(info -----------------------------------------------)
......
...@@ -97,24 +97,31 @@ public: ...@@ -97,24 +97,31 @@ public:
AssemblerBuffer(Assembler &); AssemblerBuffer(Assembler &);
~AssemblerBuffer(); ~AssemblerBuffer();
/// Basic support for emitting, loading, and storing. /// \name Basic support for emitting, loading, and storing.
/// @{
// These use memcpy instead of assignment to avoid undefined behaviour of
// assigning to unaligned addresses. Since the size of the copy is known the
// compiler can inline the memcpy with simple moves.
template <typename T> void emit(T Value) { template <typename T> void emit(T Value) {
assert(hasEnsuredCapacity()); assert(hasEnsuredCapacity());
*reinterpret_cast<T *>(Cursor) = Value; memcpy(reinterpret_cast<void *>(Cursor), &Value, sizeof(T));
Cursor += sizeof(T); Cursor += sizeof(T);
} }
template <typename T> T load(intptr_t Position) const { template <typename T> T load(intptr_t Position) const {
assert(Position >= 0 && assert(Position >= 0 &&
Position <= (size() - static_cast<intptr_t>(sizeof(T)))); Position <= (size() - static_cast<intptr_t>(sizeof(T))));
return *reinterpret_cast<T *>(Contents + Position); T Value;
memcpy(&Value, reinterpret_cast<void *>(Contents + Position), sizeof(T));
return Value;
} }
template <typename T> void store(intptr_t Position, T Value) { template <typename T> void store(intptr_t Position, T Value) {
assert(Position >= 0 && assert(Position >= 0 &&
Position <= (size() - static_cast<intptr_t>(sizeof(T)))); Position <= (size() - static_cast<intptr_t>(sizeof(T))));
*reinterpret_cast<T *>(Contents + Position) = Value; memcpy(reinterpret_cast<void *>(Contents + Position), &Value, sizeof(T));
} }
/// @{
/// Emit a fixup at the current location. /// Emit a fixup at the current location.
void emitFixup(AssemblerFixup *Fixup) { Fixup->set_position(size()); } void emitFixup(AssemblerFixup *Fixup) { Fixup->set_position(size()); }
......
...@@ -745,7 +745,7 @@ LLVM2ICEGlobalsConverter::convertGlobalsToIce(Module *Mod) { ...@@ -745,7 +745,7 @@ LLVM2ICEGlobalsConverter::convertGlobalsToIce(Module *Mod) {
addGlobalInitializer(*VarDecl, Initializer); addGlobalInitializer(*VarDecl, Initializer);
} }
} }
return std::move(VariableDeclarations); return VariableDeclarations;
} }
void LLVM2ICEGlobalsConverter::addGlobalInitializer( void LLVM2ICEGlobalsConverter::addGlobalInitializer(
......
...@@ -24,18 +24,16 @@ ...@@ -24,18 +24,16 @@
#include "IceTypes.h" #include "IceTypes.h"
// TODO: The Cfg structure, and instructions in particular, need to be // TODO: The Cfg structure, and instructions in particular, need to be
// validated for things like valid operand types, valid branch // validated for things like valid operand types, valid branch targets, proper
// targets, proper ordering of Phi and non-Phi instructions, etc. // ordering of Phi and non-Phi instructions, etc. Most of the validity
// Most of the validity checking will be done in the bitcode reader. // checking will be done in the bitcode reader. We need a list of everything
// We need a list of everything that should be validated, and tests // that should be validated, and tests for each.
// for each.
namespace Ice { namespace Ice {
/// Base instruction class for ICE. Inst has two subclasses: /// Base instruction class for ICE. Inst has two subclasses: InstHighLevel and
/// InstHighLevel and InstTarget. High-level ICE instructions inherit /// InstTarget. High-level ICE instructions inherit from InstHighLevel, and
/// from InstHighLevel, and low-level (target-specific) ICE /// low-level (target-specific) ICE instructions inherit from InstTarget.
/// instructions inherit from InstTarget.
class Inst : public llvm::ilist_node<Inst> { class Inst : public llvm::ilist_node<Inst> {
Inst() = delete; Inst() = delete;
Inst(const Inst &) = delete; Inst(const Inst &) = delete;
...@@ -68,13 +66,14 @@ public: ...@@ -68,13 +66,14 @@ public:
FakeUse, // not part of LLVM/PNaCl bitcode FakeUse, // not part of LLVM/PNaCl bitcode
FakeKill, // not part of LLVM/PNaCl bitcode FakeKill, // not part of LLVM/PNaCl bitcode
JumpTable, // not part of LLVM/PNaCl bitcode JumpTable, // not part of LLVM/PNaCl bitcode
Target // target-specific low-level ICE // Anything >= Target is an InstTarget subclass. Note that the value-spaces
// Anything >= Target is an InstTarget subclass. // are shared across targets. To avoid confusion over the definition of
// Note that the value-spaces are shared across targets. // shared values, an object specific to one target should never be passed
// To avoid confusion over the definition of shared values, // to a different target.
// an object specific to one target should never be passed Target,
// to a different target. Target_Max = std::numeric_limits<uint8_t>::max(),
}; };
static_assert(Target <= Target_Max, "Must not be above max.");
InstKind getKind() const { return Kind; } InstKind getKind() const { return Kind; }
InstNumberT getNumber() const { return Number; } InstNumberT getNumber() const { return Number; }
...@@ -107,13 +106,13 @@ public: ...@@ -107,13 +106,13 @@ public:
bool isLastUse(const Operand *Src) const; bool isLastUse(const Operand *Src) const;
void spliceLivenessInfo(Inst *OrigInst, Inst *SpliceAssn); void spliceLivenessInfo(Inst *OrigInst, Inst *SpliceAssn);
/// Returns a list of out-edges corresponding to a terminator /// Returns a list of out-edges corresponding to a terminator instruction,
/// instruction, which is the last instruction of the block. /// which is the last instruction of the block. The list must not contain
/// The list must not contain duplicates. /// duplicates.
virtual NodeList getTerminatorEdges() const { virtual NodeList getTerminatorEdges() const {
// All valid terminator instructions override this method. For // All valid terminator instructions override this method. For the default
// the default implementation, we assert in case some CfgNode // implementation, we assert in case some CfgNode is constructed without a
// is constructed without a terminator instruction at the end. // terminator instruction at the end.
llvm_unreachable( llvm_unreachable(
"getTerminatorEdges() called on a non-terminator instruction"); "getTerminatorEdges() called on a non-terminator instruction");
return NodeList(); return NodeList();
...@@ -131,19 +130,18 @@ public: ...@@ -131,19 +130,18 @@ public:
virtual bool isSimpleAssign() const { return false; } virtual bool isSimpleAssign() const { return false; }
void livenessLightweight(Cfg *Func, LivenessBV &Live); void livenessLightweight(Cfg *Func, LivenessBV &Live);
/// Calculates liveness for this instruction. Returns true if this // Calculates liveness for this instruction. Returns true if this
/// instruction is (tentatively) still live and should be retained, /// instruction is (tentatively) still live and should be retained, and false
/// and false if this instruction is (tentatively) dead and should be /// if this instruction is (tentatively) dead and should be deleted. The
/// deleted. The decision is tentative until the liveness dataflow /// decision is tentative until the liveness dataflow algorithm has converged,
/// algorithm has converged, and then a separate pass permanently /// and then a separate pass permanently deletes dead instructions.
/// deletes dead instructions.
bool liveness(InstNumberT InstNumber, LivenessBV &Live, Liveness *Liveness, bool liveness(InstNumberT InstNumber, LivenessBV &Live, Liveness *Liveness,
LiveBeginEndMap *LiveBegin, LiveBeginEndMap *LiveEnd); LiveBeginEndMap *LiveBegin, LiveBeginEndMap *LiveEnd);
/// Get the number of native instructions that this instruction /// Get the number of native instructions that this instruction ultimately
/// ultimately emits. By default, high-level instructions don't /// emits. By default, high-level instructions don't result in any native
/// result in any native instructions, and a target-specific /// instructions, and a target-specific instruction results in a single native
/// instruction results in a single native instruction. /// instruction.
virtual uint32_t getEmitInstCount() const { return 0; } virtual uint32_t getEmitInstCount() const { return 0; }
// TODO(stichnot): Change Inst back to abstract once the g++ build // TODO(stichnot): Change Inst back to abstract once the g++ build
// issue is fixed. llvm::ilist<Ice::Inst> doesn't work under g++ // issue is fixed. llvm::ilist<Ice::Inst> doesn't work under g++
...@@ -960,6 +958,7 @@ protected: ...@@ -960,6 +958,7 @@ protected:
InstTarget(Cfg *Func, InstKind Kind, SizeT MaxSrcs, Variable *Dest) InstTarget(Cfg *Func, InstKind Kind, SizeT MaxSrcs, Variable *Dest)
: Inst(Func, Kind, MaxSrcs, Dest) { : Inst(Func, Kind, MaxSrcs, Dest) {
assert(Kind >= Target); assert(Kind >= Target);
assert(Kind <= Target_Max);
} }
}; };
......
...@@ -8,11 +8,10 @@ ...@@ -8,11 +8,10 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// ///
/// \file /// \file
/// This file declares the Operand class and its target-independent /// This file declares the Operand class and its target-independent subclasses.
/// subclasses. The main classes are Variable, which represents an /// The main classes are Variable, which represents an LLVM variable that is
/// LLVM variable that is either register- or stack-allocated, and the /// either register- or stack-allocated, and the Constant hierarchy, which
/// Constant hierarchy, which represents integer, floating-point, /// represents integer, floating-point, and/or symbolic constants.
/// and/or symbolic constants.
/// ///
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
...@@ -32,7 +31,7 @@ class Operand { ...@@ -32,7 +31,7 @@ class Operand {
Operand &operator=(const Operand &) = delete; Operand &operator=(const Operand &) = delete;
public: public:
static const size_t MaxTargetKinds = 10; static constexpr size_t MaxTargetKinds = 10;
enum OperandKind { enum OperandKind {
kConst_Base, kConst_Base,
kConstInteger32, kConstInteger32,
...@@ -42,24 +41,25 @@ public: ...@@ -42,24 +41,25 @@ public:
kConstRelocatable, kConstRelocatable,
kConstUndef, kConstUndef,
kConst_Target, // leave space for target-specific constant kinds kConst_Target, // leave space for target-specific constant kinds
kConst_Num = kConst_Target + MaxTargetKinds, kConst_Max = kConst_Target + MaxTargetKinds,
kVariable, kVariable,
kVariable_Target, // leave space for target-specific variable kinds kVariable_Target, // leave space for target-specific variable kinds
kVariable_Num = kVariable_Target + MaxTargetKinds, kVariable_Max = kVariable_Target + MaxTargetKinds,
// Target-specific operand classes use kTarget as the starting // Target-specific operand classes use kTarget as the starting
// point for their Kind enum space. Note that the value-spaces are shared // point for their Kind enum space. Note that the value-spaces are shared
// across targets. To avoid confusion over the definition of shared // across targets. To avoid confusion over the definition of shared
// values, an object specific to one target should never be passed // values, an object specific to one target should never be passed
// to a different target. // to a different target.
kTarget kTarget,
kTarget_Max = std::numeric_limits<uint8_t>::max(),
}; };
static_assert(kTarget <= kTarget_Max, "Must not be above max.");
OperandKind getKind() const { return Kind; } OperandKind getKind() const { return Kind; }
Type getType() const { return Ty; } Type getType() const { return Ty; }
/// Every Operand keeps an array of the Variables referenced in /// Every Operand keeps an array of the Variables referenced in the operand.
/// the operand. This is so that the liveness operations can get /// This is so that the liveness operations can get quick access to the
/// quick access to the variables of interest, without having to dig /// variables of interest, without having to dig so far into the operand.
/// so far into the operand.
SizeT getNumVars() const { return NumVars; } SizeT getNumVars() const { return NumVars; }
Variable *getVar(SizeT I) const { Variable *getVar(SizeT I) const {
assert(I < getNumVars()); assert(I < getNumVars());
...@@ -86,7 +86,10 @@ public: ...@@ -86,7 +86,10 @@ public:
/// @} /// @}
protected: protected:
Operand(OperandKind Kind, Type Ty) : Ty(Ty), Kind(Kind) {} Operand(OperandKind Kind, Type Ty) : Ty(Ty), Kind(Kind) {
// It is undefined behavior to have a larger value in the enum
assert(Kind <= kTarget_Max);
}
virtual ~Operand() = default; virtual ~Operand() = default;
const Type Ty; const Type Ty;
...@@ -118,7 +121,7 @@ public: ...@@ -118,7 +121,7 @@ public:
static bool classof(const Operand *Operand) { static bool classof(const Operand *Operand) {
OperandKind Kind = Operand->getKind(); OperandKind Kind = Operand->getKind();
return Kind >= kConst_Base && Kind <= kConst_Num; return Kind >= kConst_Base && Kind <= kConst_Max;
} }
/// Judge if this given immediate should be randomized or pooled /// Judge if this given immediate should be randomized or pooled
...@@ -508,7 +511,7 @@ public: ...@@ -508,7 +511,7 @@ public:
static bool classof(const Operand *Operand) { static bool classof(const Operand *Operand) {
OperandKind Kind = Operand->getKind(); OperandKind Kind = Operand->getKind();
return Kind >= kVariable && Kind <= kVariable_Num; return Kind >= kVariable && Kind <= kVariable_Max;
} }
protected: protected:
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
#include "IceRNG.h" #include "IceRNG.h"
#include <time.h> #include <ctime>
namespace Ice { namespace Ice {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment