Improved error messages with better assertions
authorBenjamin Segovia <segovia.benjamin@gmail.com>
Mon, 20 Feb 2012 02:52:19 +0000 (02:52 +0000)
committerKeith Packard <keithp@keithp.com>
Fri, 10 Aug 2012 23:15:22 +0000 (16:15 -0700)
backend/src/ir/context.cpp
backend/src/ir/context.hpp
backend/src/ir/function.cpp
backend/src/ir/function.hpp
backend/src/ir/instruction.cpp
backend/src/ir/instruction.hpp
backend/src/ir/register.hpp
backend/src/sys/assert.cpp
backend/src/sys/assert.hpp
backend/src/sys/platform.hpp

index 2294f5e..5822c04 100644 (file)
@@ -35,7 +35,7 @@ namespace ir {
   }
 
   void Context::endFunction(void) {
-    GBE_ASSERT(fn != NULL);
+    GBE_ASSERTM(fn != NULL, "No function to end");
     if (fnStack.size() != 0) {
       fn = fnStack.back();
       fnStack.pop_back();
@@ -44,22 +44,24 @@ namespace ir {
   }
 
   Register Context::reg(RegisterData::Family family) {
-    GBE_ASSERT(fn != NULL);
+    GBE_ASSERTM(fn != NULL, "No function currently defined");
     return fn->file.append(family);
   }
 
   void Context::input(Register reg) {
-    GBE_ASSERT(fn != NULL && reg < fn->file.regNum());
+    GBE_ASSERTM(fn != NULL, "No function currently defined");
+    GBE_ASSERTM(reg < fn->file.regNum(), "Out-of-bound register");
     fn->input.push_back(reg);
   }
 
   void Context::output(Register reg) {
-    GBE_ASSERT(fn != NULL && reg < fn->file.regNum());
+    GBE_ASSERTM(fn != NULL, "No function currently defined");
+    GBE_ASSERTM(reg < fn->file.regNum(), "Out-of-bound register");
     fn->output.push_back(reg);
   }
 
   void Context::startBlock(void) {
-    GBE_ASSERT(fn != NULL);
+    GBE_ASSERTM(fn != NULL, "No function currently defined");
     this->bb = GBE_NEW(BasicBlock, *fn);
     fn->blocks.push_back(bb);
   }
@@ -68,18 +70,34 @@ namespace ir {
     this->bb = NULL;
   }
 
-  void Context::append(const Instruction &insn) {
+  void Context::append(const Instruction &insn)
+  {
+    GBE_ASSERTM(fn != NULL, "No function currently defined");
 
     // Start a new block if this is a label
     if (insn.isMemberOf<LabelInstruction>() == true) {
       this->endBlock();
       this->startBlock();
+      const LabelIndex index = cast<LabelInstruction>(insn).getLabelIndex();
+      GBE_ASSERTM(index < fn->labelNum(), "Out-of-bound label");
+      GBE_ASSERTM(fn->labels[index] == NULL, "Label used in a previous block");
+      fn->labels[index] = bb;
+    }
+    // We create a new label for a new block if the user did not do it
+    else if (bb == NULL) {
+      this->startBlock();
+      const LabelIndex index = fn->newLabel();
+      const Instruction insn = ir::LABEL(index);
+      this->append(insn);
     }
 
     // Append the instruction in the stream
-    GBE_ASSERT(fn != NULL && bb != NULL);
     Instruction *insnPtr = fn->newInstruction();
     *insnPtr = insn;
+#ifndef NDEBUG
+    std::string whyNot;
+    GBE_ASSERTM(insn.wellFormed(*fn, whyNot), whyNot.c_str());
+#endif /* NDEBUG */
     bb->append(*insnPtr);
 
     // Close the current block if this is a branch
index b4ef213..4bf4fd6 100644 (file)
@@ -95,9 +95,9 @@ namespace ir {
 
 #define DECL_CMP(NAME)                              \
     void NAME(Type type,                            \
-              Register dst,                    \
-              Register src0,                   \
-              Register src1)                   \
+              Register dst,                         \
+              Register src0,                        \
+              Register src1)                        \
     {                                               \
       this->CMP(type, CMP_##NAME, dst, src0, src1); \
     }
@@ -120,11 +120,12 @@ DECL_CMP(GE)
     Function *fn;             //!< Current function we are processing
     BasicBlock *bb;           //!< Current basic block we are filling
     vector<Function*> fnStack;//!< Stack of functions still to finish
+    GBE_CLASS(Context);
   };
 
   template <typename... Args>
   INLINE Tuple Context::tuple(Args...args) {
-    GBE_ASSERT(fn != NULL);
+    GBE_ASSERTM(fn != NULL, "No function currently defined");
     return fn->file.appendTuple(args...);
   }
 
@@ -132,7 +133,7 @@ DECL_CMP(GE)
 #define DECL_INSN(NAME, FAMILY)                                   \
   template <typename... Args>                                     \
   INLINE void Context::NAME(Args...args) {                        \
-    GBE_ASSERT(fn != NULL);                                       \
+    GBE_ASSERTM(fn != NULL, "No function currently defined");     \
     const Instruction insn = gbe::ir::NAME(args...);              \
     this->append(insn);                                           \
   }
index a319dba..cb5d700 100644 (file)
@@ -32,6 +32,13 @@ namespace ir {
     for (auto it = blocks.begin(); it != blocks.end(); ++it)
       GBE_DELETE(*it);
   }
+  LabelIndex Function::newLabel(void) {
+    GBE_ASSERTM(labels.size() < 0xffff,
+                "Too many labels are defined (65536 only are supported)");
+    const LabelIndex index(labels.size());
+    labels.push_back(NULL);
+    return index;
+  }
   BasicBlock::BasicBlock(Function &fn) : fn(fn) {}
   BasicBlock::~BasicBlock(void) {
     for (auto it = instructions.begin(); it != instructions.end(); ++it)
index 32cd8e2..f199b2f 100644 (file)
@@ -76,7 +76,7 @@ namespace ir {
     }
     /*! Get the given value ie immediate from the function */
     INLINE Immediate getImmediate(uint32_t ID) const {
-      GBE_ASSERT(ID < immediates.size());
+      GBE_ASSERTM(ID < immediates.size(), "Out-of-bound immediate");
       return immediates[ID];
     }
     /*! Allocate a new instruction (with the growing pool) */
@@ -87,6 +87,8 @@ namespace ir {
     INLINE void deleteInstruction(Instruction *insn) {
       insnPool.deallocate(insn);
     }
+    /*! Create a new label (still not bound to a basic block) */
+    LabelIndex newLabel(void);
     /*! Number of registers in the register file */
     INLINE uint32_t regNum(void) const { return file.regNum(); }
     /*! Number of register tuples in the register file */
@@ -97,13 +99,13 @@ namespace ir {
     INLINE uint32_t immediateNum(void) const { return immediates.size(); }
 
   private:
-    friend class Context;          //!< Can freely modify a function
-    vector<Register> input;   //!< Input registers of the function
-    vector<Register> output;  //!< Output registers of the function
-    vector<BasicBlock*> labels;    //!< Each label points to a basic block
-    vector<Immediate> immediates;  //!< All immediate values in the function
-    vector<BasicBlock*> blocks;    //!< All chained basic blocks
-    RegisterFile file;             //!< RegisterDatas used by the instructions
+    friend class Context;         //!< Can freely modify a function
+    vector<Register> input;       //!< Input registers of the function
+    vector<Register> output;      //!< Output registers of the function
+    vector<BasicBlock*> labels;   //!< Each label points to a basic block
+    vector<Immediate> immediates; //!< All immediate values in the function
+    vector<BasicBlock*> blocks;   //!< All chained basic blocks
+    RegisterFile file;            //!< RegisterDatas used by the instructions
     GrowingPool<Instruction> insnPool; //!< For fast instruction allocation
     GBE_CLASS(Function);
   };
index 45ab094..9393848 100644 (file)
@@ -71,16 +71,16 @@ namespace ir {
       INLINE uint32_t getSrcNum(void) const { return srcNum; }
       INLINE uint32_t getDstNum(void) const { return 1; }
       INLINE Register getDstIndex(const Function &fn, uint32_t ID) const {
-        assert(ID == 0);
+        GBE_ASSERTM(ID == 0, "Only one destination for the instruction");
         return dst;
       }
       INLINE Register getSrcIndex(const Function &fn, uint32_t ID) const {
-        assert(ID <= srcNum);
+        GBE_ASSERTM(ID < srcNum, "Out-of-bound source");
         return src[ID];
       }
       INLINE Type getType(void) const { return this->type; }
       INLINE bool wellFormed(const Function &fn, std::string &whyNot) const;
-      Type type;                //!< Type of the instruction
+      Type type;           //!< Type of the instruction
       Register dst;        //!< Index of the register in the register file
       Register src[srcNum];//!< Indices of the sources
     };
@@ -136,16 +136,16 @@ namespace ir {
       INLINE uint32_t getSrcNum(void) const { return 3; }
       INLINE uint32_t getDstNum(void) const { return 1; }
       INLINE Register getDstIndex(const Function &fn, uint32_t ID) const {
-        assert(ID == 0);
+        GBE_ASSERTM(ID == 0, "Only one destination for the instruction");
         return dst;
       }
       INLINE Register getSrcIndex(const Function &fn, uint32_t ID) const {
-        assert(ID <= 3);
+        GBE_ASSERTM(ID < 3, "Out-of-bound source register");
         return fn.getRegister(src, ID);
       }
       INLINE Type getType(void) const { return this->type; }
       INLINE bool wellFormed(const Function &fn, std::string &whyNot) const;
-      Type type;          //!< Type of the instruction
+      Type type;     //!< Type of the instruction
       Register dst;  //!< Dst is the register index
       Tuple src;     //!< 3 sources do not fit in 8 bytes -> use a tuple
     };
@@ -191,18 +191,18 @@ namespace ir {
       INLINE uint32_t getSrcNum(void) const { return 1; }
       INLINE uint32_t getDstNum(void) const { return 1; }
       INLINE Register getDstIndex(const Function &fn, uint32_t ID) const {
-        assert(ID == 0);
+        GBE_ASSERTM(ID == 0, "Only one destination for the convert instruction");
         return dst;
       }
       INLINE Register getSrcIndex(const Function &fn, uint32_t ID) const {
-        assert(ID == 0);
+        GBE_ASSERTM(ID == 0, "Only one source for the convert instruction");
         return src;
       }
       INLINE bool wellFormed(const Function &fn, std::string &whyNot) const;
       Register dst;  //!< Converted value
       Register src;  //!< To convert
-      Type dstType;       //!< Type to convert to
-      Type srcType;       //!< Type to convert from
+      Type dstType;  //!< Type to convert to
+      Type srcType;  //!< Type to convert from
     };
 
     class BranchInstruction : public BasePolicy, public NoDstPolicy
@@ -221,14 +221,15 @@ namespace ir {
       }
       INLINE uint32_t getSrcNum(void) const { return hasPredicate ? 1 : 0; }
       INLINE Register getSrcIndex(const Function &fn, uint32_t ID) const {
-        assert(ID == 0 && hasPredicate);
+        GBE_ASSERTM(hasPredicate, "No source for unpredicated branches");
+        GBE_ASSERTM(ID == 0, "Only one source for the branch instruction");
         return predicate;
       }
       INLINE bool isPredicated(void) const { return hasPredicate; }
       INLINE bool wellFormed(const Function &fn, std::string &why) const;
-      Register predicate;  //!< Predication means conditional branch
-      LabelIndex labelIndex;    //!< Index of the label the branch targets
-      bool hasPredicate;        //!< Is it predicated?
+      Register predicate;    //!< Predication means conditional branch
+      LabelIndex labelIndex; //!< Index of the label the branch targets
+      bool hasPredicate;     //!< Is it predicated?
     };
 
     class LoadInstruction : public BasePolicy
@@ -248,12 +249,12 @@ namespace ir {
         this->valueNum = valueNum;
       }
       INLINE Register getSrcIndex(const Function &fn, uint32_t ID) const {
-        assert(ID == 0u);
+        GBE_ASSERTM(ID == 0, "Only one source for the load instruction");
         return offset;
       }
       INLINE uint32_t getSrcNum(void) const { return 1; }
       INLINE Register getDstIndex(const Function &fn, uint32_t ID) const {
-        assert(ID < valueNum);
+        GBE_ASSERTM(ID < valueNum, "Out-of-bound source register");
         return fn.getRegister(values, ID);
       }
       INLINE uint32_t getDstNum(void) const { return valueNum; }
@@ -262,10 +263,10 @@ namespace ir {
       INLINE MemorySpace getAddressSpace(void) const { return memSpace; }
       INLINE bool wellFormed(const Function &fn, std::string &why) const;
       Type type;            //!< Type to store
-      Register offset; //!< First source is the offset where to store
-      Tuple values;    //!< Values to load
+      Register offset;      //!< First source is the offset where to store
+      Tuple values;         //!< Values to load
       MemorySpace memSpace; //!< Where to store
-      uint16_t valueNum;      //!< Number of values to store
+      uint16_t valueNum;    //!< Number of values to store
     };
 
     class StoreInstruction : public BasePolicy, public NoDstPolicy
@@ -285,7 +286,7 @@ namespace ir {
         this->valueNum = valueNum;
       }
       INLINE Register getSrcIndex(const Function &fn, uint32_t ID) const {
-        assert(ID < valueNum + 1u); // offset + values to store
+        GBE_ASSERTM(ID < valueNum + 1u, "Out-of-bound source register for store");
         if (ID == 0u)
           return offset;
         else
@@ -297,8 +298,8 @@ namespace ir {
       INLINE MemorySpace getAddressSpace(void) const { return memSpace; }
       INLINE bool wellFormed(const Function &fn, std::string &why) const;
       Type type;            //!< Type to store
-      Register offset; //!< First source is the offset where to store
-      Tuple values;    //!< Values to store
+      Register offset;      //!< First source is the offset where to store
+      Tuple values;         //!< Values to store
       MemorySpace memSpace; //!< Where to store
       uint16_t valueNum;    //!< Number of values to store
     };
@@ -327,14 +328,14 @@ namespace ir {
       }
       INLINE uint32_t getDstNum(void) const{ return 1; }
       INLINE Register getDstIndex(const Function &fn, uint32_t ID) const {
-        assert(ID == 0);
+        GBE_ASSERTM(ID == 0, "Only one destination is supported for load immediate");
         return dst;
       }
       INLINE Type getType(void) const { return this->type; }
       bool wellFormed(const Function &fn, std::string &why) const;
-      Register dst;              //!< RegisterData to store into
-      ImmediateIndex immediateIndex;  //!< Index in the vector of immediates
-      Type type;                      //!< Type of the immediate
+      Register dst;                  //!< RegisterData to store into
+      ImmediateIndex immediateIndex; //!< Index in the vector of immediates
+      Type type;                     //!< Type of the immediate
     };
 
     class FenceInstruction : public BasePolicy, public NoSrcPolicy, public NoDstPolicy
@@ -355,6 +356,7 @@ namespace ir {
         this->opcode = OP_LABEL;
         this->labelIndex = labelIndex;
       }
+      INLINE LabelIndex getLabelIndex(void) const { return labelIndex; }
       INLINE bool wellFormed(const Function &fn, std::string &why) const;
       LabelIndex labelIndex;  //!< Index of the label
     };
@@ -684,6 +686,7 @@ DECL_MEM_FN(LoadInstruction, uint32_t, getValueNum(void), getValueNum())
 DECL_MEM_FN(LoadInstruction, MemorySpace, getAddressSpace(void), getAddressSpace())
 DECL_MEM_FN(LoadImmInstruction, Immediate, getImmediate(const Function &fn), getImmediate(fn))
 DECL_MEM_FN(LoadImmInstruction, Type, getType(void), getType())
+DECL_MEM_FN(LabelInstruction, LabelIndex, getLabelIndex(void), getLabelIndex())
 DECL_MEM_FN(BranchInstruction, bool, isPredicated(void), isPredicated())
 
 #undef DECL_MEM_FN
index f6d0cd0..b140c81 100644 (file)
@@ -201,9 +201,10 @@ namespace ir {
   };
 
   /*! Load immediate instruction loads an typed immediate value into the given
-   * register. Since double and uint64_t values will not fit into an instruction,
-   * the immediate themselves are stored in the function core. Contrary to
-   * regular load instructions, there is only one destination possible
+   *  register. Since double and uint64_t values will not fit into an
+   *  instruction, the immediate themselves are stored in the function core.
+   *  Contrary to regular load instructions, there is only one destination
+   *  possible
    */
   class LoadImmInstruction : public Instruction {
   public:
@@ -224,12 +225,12 @@ namespace ir {
     bool isPredicated(void) const;
     /*! Return the predicate register (if predicated) */
     RegisterData getPredicate(const Function &fn) const {
-      assert(this->isPredicated() == true);
+      GBE_ASSERTM(this->isPredicated() == true, "Branch is not predicated");
       return this->getSrc(fn, 0);
     }
     /*! Return the predicate register index (if predicated) */
     Register getPredicateIndex(const Function &fn) const {
-      assert(this->isPredicated() == true);
+      GBE_ASSERTM(this->isPredicated() == true, "Branch is not predicated");
       return this->getSrcIndex(fn, 0);
     }
     /*! Return true if the given instruction is an instance of this class */
@@ -241,6 +242,8 @@ namespace ir {
    */
   class LabelInstruction : public Instruction {
   public:
+    /*! Return the label index of the instruction */
+    LabelIndex getLabelIndex(void) const;
     /*! Return true if the given instruction is an instance of this class */
     static bool isClassOf(const Instruction &insn);
   };
@@ -266,22 +269,22 @@ namespace ir {
    */
   template <typename T>
   INLINE T *cast(Instruction *insn) {
-    assert(insn->isMemberOf<T>() == true);
+    GBE_ASSERTM(insn->isMemberOf<T>() == true, "Invalid instruction cast");
     return reinterpret_cast<T*>(insn);
   }
   template <typename T>
   INLINE const T *cast(const Instruction *insn) {
-    assert(insn->isMemberOf<T>() == true);
+    GBE_ASSERTM(insn->isMemberOf<T>() == true, "Invalid instruction cast");
     return reinterpret_cast<const T*>(insn);
   }
   template <typename T>
   INLINE T &cast(Instruction &insn) {
-    assert(insn.isMemberOf<T>() == true);
+    GBE_ASSERTM(insn.isMemberOf<T>() == true, "Invalid instruction cast");
     return reinterpret_cast<T&>(insn);
   }
   template <typename T>
   INLINE const T &cast(const Instruction &insn) {
-    assert(insn.isMemberOf<T>() == true);
+    GBE_ASSERTM(insn.isMemberOf<T>() == true, "Invalid instruction cast");
     return reinterpret_cast<const T&>(insn);
   }
 
index 63fa183..c6d355b 100644 (file)
@@ -74,9 +74,10 @@ namespace ir {
   class RegisterFile
   {
   public:
-    /*! Return the index of a newly allocated register register */
+    /*! Return the index of a newly allocated register */
     INLINE Register append(RegisterData::Family family) {
-      GBE_ASSERT(regNum() <= MAX_INDEX);
+      GBE_ASSERTM(regNum() <= MAX_INDEX,
+                  "Too many defined registers (only 65536 are supported)");
       const uint16_t index = regNum();
       const RegisterData reg(family);
       regs.push_back(reg);
@@ -86,7 +87,7 @@ namespace ir {
     template <typename First, typename... Rest>
     INLINE Tuple appendTuple(First first, Rest... rest) {
       const Tuple index = Tuple(regTuples.size());
-      GBE_ASSERT(first < regNum());
+      GBE_ASSERTM(first < regNum(), "Out-of-bound register");
       regTuples.push_back(first);
       appendTuple(rest...);
       return index;
@@ -95,12 +96,13 @@ namespace ir {
     INLINE void appendTuple(void) {}
     /*! Return a copy of the register at index */
     INLINE RegisterData get(Register index) const {
-      GBE_ASSERT(index < regNum());
+      GBE_ASSERTM(index < regNum(), "Out-of-bound register");
       return regs[index];
     }
     /*! Get the register index from the tuple */
     INLINE Register get(Tuple index, uint32_t which) const {
-      GBE_ASSERT(uint16_t(index) + which < regTuples.size());
+      GBE_ASSERTM(uint16_t(index) + which < regTuples.size(),
+                  "Out-of-bound index in the tuple file");
       return regTuples[uint16_t(index) + which];
     }
     /*! Number of registers in the register file */
@@ -108,9 +110,9 @@ namespace ir {
     /*! Number of tuples in the register file */
     INLINE uint32_t tupleNum(void) const { return regTuples.size(); }
   private:
-    vector<RegisterData> regs;           //!< All the registers together
-    vector<Register> regTuples; //!< Tuples are used for many src / dst
-    enum { MAX_INDEX = 0xffff };     //!< register and tuple indices are short
+    vector<RegisterData> regs;   //!< All the registers together
+    vector<Register> regTuples;  //!< Tuples are used for many src / dst
+    enum { MAX_INDEX = 0xffff }; //!< register and tuple indices are short
     GBE_CLASS(RegisterFile);
   };
 
index ff17f54..9d5ae2a 100644 (file)
 
 #include "assert.hpp"
 #include "exception.hpp"
+#include <cassert>
+
 namespace gbe
 {
-  void onFailedAssert(const char *file, const char *fn, int line)
+  void onFailedAssertion(const char *msg, const char *file, const char *fn, int line)
   {
     char lineString[256];
     sprintf(lineString, "%i", line);
-    const std::string msg = "file " + std::string(file)
+    assert(msg != NULL && file != NULL && fn != NULL);
+    const std::string str = "Compiler error: "
+                          + std::string(msg) + "\n  at file "
+                          + std::string(file)
                           + ", function " + std::string(fn)
                           + ", line " + std::string(lineString);
-    throw Exception(msg);
+    throw Exception(str);
   }
 } /* namespace gbe */
 
@@ -48,10 +53,12 @@ namespace gbe
 
 namespace gbe
 {
-  void onFailedAssert(const char *file, const char *fn, int32_t line)
+  void onFailedAssertion(const char *msg, const char *file, const char *fn, int32_t line)
   {
-    fprintf(stderr, " ASSERTION FAILED: file %s, function %s, line %i\n",
-            file, fn, line);
+    assert(msg != NULL && file != NULL && fn != NULL);
+    fprintf(stderr, "ASSERTION FAILED: %s\n"
+                    "  at file %s, function %s, line %i\n",
+                    msg,  file, fn, line);
     fflush(stdout);
     DEBUGBREAK();
     _exit(-1);
index 9b031c0..553e391 100644 (file)
@@ -28,7 +28,7 @@
 namespace gbe
 {
   /*! To ensure that condition truth. Optional message is supported */
-  void onFailedAssert(const char *file, const char *fn, int line);
+  void onFailedAssertion(const char *msg, const char *file, const char *fn, int line);
 } /* namespace gbe */
 
 #endif /* __GBE_ASSERT_HPP__ */
index 88f217e..f74f7fc 100644 (file)
 
 /*! Run-time assertion */
 #ifndef NDEBUG
-#define GBE_ASSERT(EXPR) do {                              \
-  if (UNLIKELY(!(EXPR)))                                   \
-    gbe::onFailedAssert(__FILE__, __FUNCTION__, __LINE__); \
+#define GBE_ASSERT(EXPR) do {                                       \
+  if (UNLIKELY(!(EXPR)))                                            \
+    gbe::onFailedAssertion(#EXPR, __FILE__, __FUNCTION__, __LINE__);\
+} while (0)
+#define GBE_ASSERTM(EXPR, MSG) do {                                 \
+  if (UNLIKELY(!(EXPR)))                                            \
+    gbe::onFailedAssertion(MSG, __FILE__, __FUNCTION__, __LINE__);  \
 } while (0)
 #else
 #define GBE_ASSERT(EXPR) do { } while (0)
+#define GBE_ASSERT_M(EXPR) do { } while (0)
 #endif
 
 /*! Fatal error macros */