Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-15 09:06:15

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file orange/univ/detail/LogicStack.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include "corecel/Assert.hh"
0010 #include "corecel/Types.hh"
0011 
0012 namespace celeritas
0013 {
0014 namespace detail
0015 {
0016 //---------------------------------------------------------------------------//
0017 /*!
0018  * Simple, fixed-max-size (no heap allocation) stack.
0019  *
0020  * This uses a bit field where the "top" of the stack is the least significant
0021  * bit.
0022  *
0023  * For a simple shape (the intersection of surfaces+senses), the max stack
0024  * depth is 1. For a combination of those, the stack depth is 2. This logic
0025  * stack can hold up to 32 entries (minimum size of size_type).
0026  *
0027  * The underlying code is highly optimized. For example, calling 'apply_and'
0028  * inside the 'evaluate' function (on GCC 5 with -O2) results in: \verbatim
0029         movl    %eax, %ecx  # stack, temp
0030         shrl    %eax        # D.44391
0031         andl    $1,   %ecx  #, temp
0032         andl    %ecx, %eax  # temp, stack
0033  * \endverbatim
0034  *
0035  * Furthermore, and delightfully, if LogicStack is local to a function, and
0036  * assertions are off, all operations on size_ are optimized out completely! So
0037  * there's no penalty to adding that extra safety check.
0038  */
0039 class LogicStack
0040 {
0041   public:
0042     //@{
0043     //! Typedefs
0044     using value_type = bool;
0045     using size_type = celeritas::size_type;
0046     //@}
0047 
0048   public:
0049     //! Default constructor
0050     CELER_FORCEINLINE_FUNCTION LogicStack() {}
0051 
0052     //! Greatest number of boolean values allowed on the stack
0053     static CELER_CONSTEXPR_FUNCTION size_type max_stack_depth()
0054     {
0055         return sizeof(size_type) * 8;
0056     }
0057 
0058     //// ACCESSORS ////
0059 
0060     //! Number of elements on the stack
0061     CELER_FORCEINLINE_FUNCTION size_type size() const { return size_; }
0062 
0063     // Whether any elements exist
0064     CELER_FORCEINLINE_FUNCTION bool empty() const;
0065 
0066     // Access the top value of the stack
0067     CELER_FORCEINLINE_FUNCTION value_type top() const;
0068 
0069     // Access a single bit (zero is deepest level of stack), used by ostream
0070     CELER_FORCEINLINE_FUNCTION value_type operator[](size_type index) const;
0071 
0072     //// MUTATORS ////
0073 
0074     // Push a boolean onto the stack
0075     CELER_FORCEINLINE_FUNCTION void push(value_type v);
0076 
0077     // Pop a value off the stack
0078     CELER_FORCEINLINE_FUNCTION value_type pop();
0079 
0080     // Negate the value on the top of the stack
0081     CELER_FORCEINLINE_FUNCTION void apply_not();
0082 
0083     // Apply boolean 'and' to the top of the stack
0084     CELER_FORCEINLINE_FUNCTION void apply_and();
0085 
0086     // Apply boolean 'or' to the top of the stack
0087     CELER_FORCEINLINE_FUNCTION void apply_or();
0088 
0089   private:
0090     //! Get the least significant bit
0091     static CELER_CONSTEXPR_FUNCTION size_type lsb(size_type val)
0092     {
0093         return val & size_type(1);
0094     }
0095 
0096     //! Shift right by one
0097     static CELER_CONSTEXPR_FUNCTION size_type shr(size_type val)
0098     {
0099         return val >> size_type(1);
0100     }
0101 
0102     //! Shift left by one
0103     static CELER_CONSTEXPR_FUNCTION size_type shl(size_type val)
0104     {
0105         return val << size_type(1);
0106     }
0107 
0108   private:
0109     //// DATA ////
0110 
0111     size_type data_{0};  //!< Stack data
0112     size_type size_{0};  //!< Stack depth
0113 };
0114 
0115 //---------------------------------------------------------------------------//
0116 /*!
0117  * Whether the stack has any pushed values.
0118  */
0119 CELER_FUNCTION bool LogicStack::empty() const
0120 {
0121     return size_ == size_type(0);
0122 }
0123 
0124 //---------------------------------------------------------------------------//
0125 /*!
0126  * Access the top value of the stack.
0127  */
0128 CELER_FUNCTION auto LogicStack::top() const -> value_type
0129 {
0130     CELER_EXPECT(!empty());
0131     return LogicStack::lsb(data_);
0132 }
0133 
0134 //---------------------------------------------------------------------------//
0135 /*!
0136  * Access a single bit (zero is deepest level of stack).
0137  */
0138 CELER_FUNCTION auto LogicStack::operator[](size_type index) const -> value_type
0139 {
0140     CELER_EXPECT(index < size());
0141     size_type shift = size() - index - size_type(1);
0142     return LogicStack::lsb(data_ >> shift);
0143 }
0144 
0145 //---------------------------------------------------------------------------//
0146 /*!
0147  * Push a boolean onto the stack.
0148  */
0149 CELER_FUNCTION void LogicStack::push(value_type v)
0150 {
0151     CELER_EXPECT(size() != max_stack_depth());
0152     // Shift stack left and add least significant bit
0153     data_ = LogicStack::shl(data_) | LogicStack::lsb(v);
0154     // Size for DBC
0155     ++size_;
0156 }
0157 
0158 //---------------------------------------------------------------------------//
0159 /*!
0160  * Pop a value off the stack.
0161  */
0162 CELER_FUNCTION auto LogicStack::pop() -> value_type
0163 {
0164     CELER_EXPECT(!empty());
0165     // Extract least significant bit
0166     value_type result = LogicStack::lsb(data_);
0167     // Shift right
0168     data_ = LogicStack::shr(data_);
0169     // Update size
0170     --size_;
0171     return result;
0172 }
0173 
0174 //---------------------------------------------------------------------------//
0175 /*!
0176  * Negate the value on the top of the stack.
0177  */
0178 CELER_FUNCTION void LogicStack::apply_not()
0179 {
0180     CELER_EXPECT(!empty());
0181     data_ ^= size_type(1);
0182 }
0183 
0184 //---------------------------------------------------------------------------//
0185 /*!
0186  * Apply boolean 'and' to the top of the stack.
0187  */
0188 CELER_FUNCTION void LogicStack::apply_and()
0189 {
0190     CELER_EXPECT(size() >= size_type(2));
0191     size_type temp = LogicStack::lsb(data_);
0192     data_ = LogicStack::shr(data_) & (temp | ~size_type(1));
0193     --size_;
0194 }
0195 
0196 //---------------------------------------------------------------------------//
0197 /*!
0198  * Apply boolean 'or' to the top of the stack.
0199  */
0200 CELER_FUNCTION void LogicStack::apply_or()
0201 {
0202     CELER_EXPECT(size() >= size_type(2));
0203     data_ = LogicStack::shr(data_) | LogicStack::lsb(data_);
0204     --size_;
0205 }
0206 
0207 //---------------------------------------------------------------------------//
0208 }  // namespace detail
0209 }  // namespace celeritas