Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /include/orange/univ/detail/LogicStack.hh was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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