Merge pull request #2633 from whitequark/cxxrtl-no-top
[yosys.git] / backends / cxxrtl / cxxrtl.h
index 4528a7d8fbe21ec5c8d34f08a6d4273664efa4e7..0e55c46c223986aea5bdf57009f9c671bd9ddebd 100644 (file)
 #include <map>
 #include <algorithm>
 #include <memory>
+#include <functional>
 #include <sstream>
 
 #include <backends/cxxrtl/cxxrtl_capi.h>
 
+#ifndef __has_attribute
+#      define __has_attribute(x) 0
+#endif
+
 // CXXRTL essentially uses the C++ compiler as a hygienic macro engine that feeds an instruction selector.
 // It generates a lot of specialized template functions with relatively large bodies that, when inlined
 // into the caller and (for those with loops) unrolled, often expose many new optimization opportunities.
 // Because of this, most of the CXXRTL runtime must be always inlined for best performance.
-#ifndef __has_attribute
-#      define __has_attribute(x) 0
-#endif
 #if __has_attribute(always_inline)
 #define CXXRTL_ALWAYS_INLINE inline __attribute__((__always_inline__))
 #else
 #define CXXRTL_ALWAYS_INLINE inline
 #endif
+// Conversely, some functions in the generated code are extremely large yet very cold, with both of these
+// properties being extreme enough to confuse C++ compilers into spending pathological amounts of time
+// on a futile (the code becomes worse) attempt to optimize the least important parts of code.
+#if __has_attribute(optnone)
+#define CXXRTL_EXTREMELY_COLD __attribute__((__optnone__))
+#elif __has_attribute(optimize)
+#define CXXRTL_EXTREMELY_COLD __attribute__((__optimize__(0)))
+#else
+#define CXXRTL_EXTREMELY_COLD
+#endif
+
+// CXXRTL uses assert() to check for C++ contract violations (which may result in e.g. undefined behavior
+// of the simulation code itself), and CXXRTL_ASSERT to check for RTL contract violations (which may at
+// most result in undefined simulation results).
+//
+// Though by default, CXXRTL_ASSERT() expands to assert(), it may be overridden e.g. when integrating
+// the simulation into another process that should survive violating RTL contracts.
+#ifndef CXXRTL_ASSERT
+#ifndef CXXRTL_NDEBUG
+#define CXXRTL_ASSERT(x) assert(x)
+#else
+#define CXXRTL_ASSERT(x)
+#endif
+#endif
 
 namespace cxxrtl {
 
@@ -291,6 +317,14 @@ struct value : public expr_base<value<Bits>> {
                return sext_cast<NewBits>()(*this);
        }
 
+       // Bit replication is far more efficient than the equivalent concatenation.
+       template<size_t Count>
+       CXXRTL_ALWAYS_INLINE
+       value<Bits * Count> repeat() const {
+               static_assert(Bits == 1, "repeat() is implemented only for 1-bit values");
+               return *this ? value<Bits * Count>().bit_not() : value<Bits * Count>();
+       }
+
        // Operations with run-time parameters (offsets, amounts, etc).
        //
        // These operations are used for computations.
@@ -645,7 +679,7 @@ struct wire {
        value<Bits> next;
 
        wire() = default;
-       constexpr wire(const value<Bits> &init) : curr(init), next(init) {}
+       explicit constexpr wire(const value<Bits> &init) : curr(init), next(init) {}
        template<typename... Init>
        explicit constexpr wire(Init ...init) : curr{init...}, next{init...} {}
 
@@ -829,6 +863,9 @@ typedef std::map<std::string, metadata> metadata_map;
 // Tag class to disambiguate values/wires and their aliases.
 struct debug_alias {};
 
+// Tag declaration to disambiguate values and debug outlines.
+using debug_outline = ::_cxxrtl_outline;
+
 // This structure is intended for consumption via foreign function interfaces, like Python's ctypes.
 // Because of this it uses a C-style layout that is easy to parse rather than more idiomatic C++.
 //
@@ -837,10 +874,11 @@ struct debug_alias {};
 struct debug_item : ::cxxrtl_object {
        // Object types.
        enum : uint32_t {
-               VALUE  = CXXRTL_VALUE,
-               WIRE   = CXXRTL_WIRE,
-               MEMORY = CXXRTL_MEMORY,
-               ALIAS  = CXXRTL_ALIAS,
+               VALUE   = CXXRTL_VALUE,
+               WIRE    = CXXRTL_WIRE,
+               MEMORY  = CXXRTL_MEMORY,
+               ALIAS   = CXXRTL_ALIAS,
+               OUTLINE = CXXRTL_OUTLINE,
        };
 
        // Object flags.
@@ -867,6 +905,7 @@ struct debug_item : ::cxxrtl_object {
                zero_at = 0;
                curr    = item.data;
                next    = item.data;
+               outline = nullptr;
        }
 
        template<size_t Bits>
@@ -881,6 +920,7 @@ struct debug_item : ::cxxrtl_object {
                zero_at = 0;
                curr    = const_cast<chunk_t*>(item.data);
                next    = nullptr;
+               outline = nullptr;
        }
 
        template<size_t Bits>
@@ -896,6 +936,7 @@ struct debug_item : ::cxxrtl_object {
                zero_at = 0;
                curr    = item.curr.data;
                next    = item.next.data;
+               outline = nullptr;
        }
 
        template<size_t Width>
@@ -910,6 +951,7 @@ struct debug_item : ::cxxrtl_object {
                zero_at = zero_offset;
                curr    = item.data.empty() ? nullptr : item.data[0].data;
                next    = nullptr;
+               outline = nullptr;
        }
 
        template<size_t Bits>
@@ -924,6 +966,7 @@ struct debug_item : ::cxxrtl_object {
                zero_at = 0;
                curr    = const_cast<chunk_t*>(item.data);
                next    = nullptr;
+               outline = nullptr;
        }
 
        template<size_t Bits>
@@ -939,6 +982,22 @@ struct debug_item : ::cxxrtl_object {
                zero_at = 0;
                curr    = const_cast<chunk_t*>(item.curr.data);
                next    = nullptr;
+               outline = nullptr;
+       }
+
+       template<size_t Bits>
+       debug_item(debug_outline &group, const value<Bits> &item, size_t lsb_offset = 0) {
+               static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
+                             "value<Bits> is not compatible with C layout");
+               type    = OUTLINE;
+               flags   = DRIVEN_COMB;
+               width   = Bits;
+               lsb_at  = lsb_offset;
+               depth   = 1;
+               zero_at = 0;
+               curr    = const_cast<chunk_t*>(item.data);
+               next    = nullptr;
+               outline = &group;
        }
 };
 static_assert(std::is_standard_layout<debug_item>::value, "debug_item is not compatible with C layout");
@@ -1015,11 +1074,16 @@ struct module {
 
 } // namespace cxxrtl
 
-// Internal structure used to communicate with the implementation of the C interface.
+// Internal structures used to communicate with the implementation of the C interface.
+
 typedef struct _cxxrtl_toplevel {
        std::unique_ptr<cxxrtl::module> module;
 } *cxxrtl_toplevel;
 
+typedef struct _cxxrtl_outline {
+       std::function<void()> eval;
+} *cxxrtl_outline;
+
 // Definitions of internal Yosys cells. Other than the functions in this namespace, CXXRTL is fully generic
 // and indepenent of Yosys implementation details.
 //
@@ -1153,49 +1217,49 @@ value<BitsY> xnor_ss(const value<BitsA> &a, const value<BitsB> &b) {
 template<size_t BitsY, size_t BitsA, size_t BitsB>
 CXXRTL_ALWAYS_INLINE
 value<BitsY> shl_uu(const value<BitsA> &a, const value<BitsB> &b) {
-       return a.template zcast<BitsY>().template shl(b);
+       return a.template zcast<BitsY>().shl(b);
 }
 
 template<size_t BitsY, size_t BitsA, size_t BitsB>
 CXXRTL_ALWAYS_INLINE
 value<BitsY> shl_su(const value<BitsA> &a, const value<BitsB> &b) {
-       return a.template scast<BitsY>().template shl(b);
+       return a.template scast<BitsY>().shl(b);
 }
 
 template<size_t BitsY, size_t BitsA, size_t BitsB>
 CXXRTL_ALWAYS_INLINE
 value<BitsY> sshl_uu(const value<BitsA> &a, const value<BitsB> &b) {
-       return a.template zcast<BitsY>().template shl(b);
+       return a.template zcast<BitsY>().shl(b);
 }
 
 template<size_t BitsY, size_t BitsA, size_t BitsB>
 CXXRTL_ALWAYS_INLINE
 value<BitsY> sshl_su(const value<BitsA> &a, const value<BitsB> &b) {
-       return a.template scast<BitsY>().template shl(b);
+       return a.template scast<BitsY>().shl(b);
 }
 
 template<size_t BitsY, size_t BitsA, size_t BitsB>
 CXXRTL_ALWAYS_INLINE
 value<BitsY> shr_uu(const value<BitsA> &a, const value<BitsB> &b) {
-       return a.template shr(b).template zcast<BitsY>();
+       return a.shr(b).template zcast<BitsY>();
 }
 
 template<size_t BitsY, size_t BitsA, size_t BitsB>
 CXXRTL_ALWAYS_INLINE
 value<BitsY> shr_su(const value<BitsA> &a, const value<BitsB> &b) {
-       return a.template shr(b).template scast<BitsY>();
+       return a.shr(b).template scast<BitsY>();
 }
 
 template<size_t BitsY, size_t BitsA, size_t BitsB>
 CXXRTL_ALWAYS_INLINE
 value<BitsY> sshr_uu(const value<BitsA> &a, const value<BitsB> &b) {
-       return a.template shr(b).template zcast<BitsY>();
+       return a.shr(b).template zcast<BitsY>();
 }
 
 template<size_t BitsY, size_t BitsA, size_t BitsB>
 CXXRTL_ALWAYS_INLINE
 value<BitsY> sshr_su(const value<BitsA> &a, const value<BitsB> &b) {
-       return a.template sshr(b).template scast<BitsY>();
+       return a.sshr(b).template scast<BitsY>();
 }
 
 template<size_t BitsY, size_t BitsA, size_t BitsB>