Path: blob/master/thirdparty/basis_universal/encoder/cppspmd_flow.h
9902 views
// Do not include this header directly.1// Control flow functionality in common between all the headers.2//3// Copyright 2020-2024 Binomial LLC4//5// Licensed under the Apache License, Version 2.0 (the "License");6// you may not use this file except in compliance with the License.7// You may obtain a copy of the License at8//9// http://www.apache.org/licenses/LICENSE-2.010//11// Unless required by applicable law or agreed to in writing, software12// distributed under the License is distributed on an "AS IS" BASIS,13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.14// See the License for the specific language governing permissions and15// limitations under the License.1617#ifdef _DEBUG18CPPSPMD_FORCE_INLINE void spmd_kernel::check_masks()19{20assert(!any(andnot(m_kernel_exec, m_exec)));21}22#endif2324CPPSPMD_FORCE_INLINE void spmd_kernel::spmd_break()25{26#ifdef _DEBUG27assert(m_in_loop);28#endif2930m_exec = exec_mask::all_off();31}3233CPPSPMD_FORCE_INLINE void spmd_kernel::spmd_continue()34{35#ifdef _DEBUG36assert(m_in_loop);37#endif3839// Kill any active lanes, and remember which lanes were active so we can re-enable them at the end of the loop body.40m_continue_mask = m_continue_mask | m_exec;41m_exec = exec_mask::all_off();42}4344CPPSPMD_FORCE_INLINE void spmd_kernel::spmd_return()45{46// Permenantly kill all active lanes47m_kernel_exec = andnot(m_exec, m_kernel_exec);48m_exec = exec_mask::all_off();49}5051template<typename UnmaskedBody>52CPPSPMD_FORCE_INLINE void spmd_kernel::spmd_unmasked(const UnmaskedBody& unmaskedBody)53{54exec_mask orig_exec = m_exec, orig_kernel_exec = m_kernel_exec;5556m_kernel_exec = exec_mask::all_on();57m_exec = exec_mask::all_on();5859unmaskedBody();6061m_kernel_exec = m_kernel_exec & orig_kernel_exec;62m_exec = m_exec & orig_exec;6364check_masks();65}6667struct scoped_unmasked_restorer68{69spmd_kernel *m_pKernel;70exec_mask m_orig_exec, m_orig_kernel_exec;7172CPPSPMD_FORCE_INLINE scoped_unmasked_restorer(spmd_kernel *pKernel) :73m_pKernel(pKernel),74m_orig_exec(pKernel->m_exec),75m_orig_kernel_exec(pKernel->m_kernel_exec)76{77pKernel->m_kernel_exec = exec_mask::all_on();78pKernel->m_exec = exec_mask::all_on();79}8081CPPSPMD_FORCE_INLINE ~scoped_unmasked_restorer()82{83m_pKernel->m_kernel_exec = m_pKernel->m_kernel_exec & m_orig_kernel_exec;84m_pKernel->m_exec = m_pKernel->m_exec & m_orig_exec;85m_pKernel->check_masks();86}87};8889#define SPMD_UNMASKED_BEGIN { scoped_unmasked_restorer _unmasked_restorer(this);90#define SPMD_UNMASKED_END }9192#if 093template<typename SPMDKernel, typename... Args>94CPPSPMD_FORCE_INLINE decltype(auto) spmd_kernel::spmd_call(Args&&... args)95{96SPMDKernel kernel;97kernel.init(m_exec);98return kernel._call(std::forward<Args>(args)...);99}100#else101template<typename SPMDKernel, typename... Args>102CPPSPMD_FORCE_INLINE void spmd_kernel::spmd_call(Args&&... args)103{104SPMDKernel kernel;105kernel.init(m_exec);106kernel._call(std::forward<Args>(args)...);107}108#endif109110CPPSPMD_FORCE_INLINE void spmd_kernel::spmd_if_break(const vbool& cond)111{112#ifdef _DEBUG113assert(m_in_loop);114#endif115116exec_mask cond_exec(cond);117118m_exec = andnot(m_exec & cond_exec, m_exec);119120check_masks();121}122123// No SPMD breaks, continues, etc. allowed124template<typename IfBody>125CPPSPMD_FORCE_INLINE void spmd_kernel::spmd_sif(const vbool& cond, const IfBody& ifBody)126{127exec_mask im = m_exec & exec_mask(cond);128129if (any(im))130{131const exec_mask orig_exec = m_exec;132m_exec = im;133ifBody();134m_exec = orig_exec;135}136}137138// No SPMD breaks, continues, etc. allowed139template<typename IfBody, typename ElseBody>140CPPSPMD_FORCE_INLINE void spmd_kernel::spmd_sifelse(const vbool& cond, const IfBody& ifBody, const ElseBody &elseBody)141{142const exec_mask orig_exec = m_exec;143144exec_mask im = m_exec & exec_mask(cond);145146if (any(im))147{148m_exec = im;149ifBody();150}151152exec_mask em = orig_exec & exec_mask(!cond);153154if (any(em))155{156m_exec = em;157elseBody();158}159160m_exec = orig_exec;161}162163template<typename IfBody>164CPPSPMD_FORCE_INLINE void spmd_kernel::spmd_if(const vbool& cond, const IfBody& ifBody)165{166exec_mask cond_exec(cond);167168exec_mask pre_if_exec = cond_exec & m_exec;169170if (any(pre_if_exec))171{172exec_mask unexecuted_lanes = andnot(cond_exec, m_exec);173m_exec = pre_if_exec;174175ifBody();176177// Propagate any lanes that got disabled inside the if body into the exec mask outside the if body, but turn on any lanes that didn't execute inside the if body.178m_exec = m_exec | unexecuted_lanes;179180check_masks();181}182}183184template<typename IfBody, typename ElseBody>185CPPSPMD_FORCE_INLINE void spmd_kernel::spmd_ifelse(const vbool& cond, const IfBody& ifBody, const ElseBody& elseBody)186{187bool all_flag = false;188189exec_mask cond_exec(cond);190191{192exec_mask pre_if_exec = cond_exec & m_exec;193194int mask = pre_if_exec.get_movemask();195if (mask != 0)196{197all_flag = ((uint32_t)mask == m_exec.get_movemask());198199exec_mask unexecuted_lanes = andnot(cond_exec, m_exec);200m_exec = pre_if_exec;201202ifBody();203204// Propagate any lanes that got disabled inside the if body into the exec mask outside the if body, but turn on any lanes that didn't execute inside the if body.205m_exec = m_exec | unexecuted_lanes;206207check_masks();208}209}210211if (!all_flag)212{213exec_mask pre_if_exec = andnot(cond_exec, m_exec);214215if (any(pre_if_exec))216{217exec_mask unexecuted_lanes = cond_exec & m_exec;218m_exec = pre_if_exec;219220ifBody();221222// Propagate any lanes that got disabled inside the if body into the exec mask outside the if body, but turn on any lanes that didn't execute inside the if body.223m_exec = m_exec | unexecuted_lanes;224225check_masks();226}227}228}229230struct scoped_exec_restorer231{232exec_mask *m_pMask;233exec_mask m_prev_mask;234CPPSPMD_FORCE_INLINE scoped_exec_restorer(exec_mask *pExec_mask) : m_pMask(pExec_mask), m_prev_mask(*pExec_mask) { }235CPPSPMD_FORCE_INLINE ~scoped_exec_restorer() { *m_pMask = m_prev_mask; }236};237238// Cannot use SPMD break, continue, or return inside "simple" if/else239#define SPMD_SIF(cond) exec_mask CPPSPMD_GLUER2(_exec_temp, __LINE__)(m_exec & exec_mask(vbool(cond))); if (any(CPPSPMD_GLUER2(_exec_temp, __LINE__))) \240{ CPPSPMD::scoped_exec_restorer CPPSPMD_GLUER2(_exec_restore_, __LINE__)(&m_exec); m_exec = CPPSPMD_GLUER2(_exec_temp, __LINE__);241242#define SPMD_SELSE(cond) } exec_mask CPPSPMD_GLUER2(_exec_temp, __LINE__)(m_exec & exec_mask(!vbool(cond))); if (any(CPPSPMD_GLUER2(_exec_temp, __LINE__))) \243{ CPPSPMD::scoped_exec_restorer CPPSPMD_GLUER2(_exec_restore_, __LINE__)(&m_exec); m_exec = CPPSPMD_GLUER2(_exec_temp, __LINE__);244245#define SPMD_SENDIF }246247// Same as SPMD_SIF, except doesn't use a scoped object248#define SPMD_SIF2(cond) exec_mask CPPSPMD_GLUER2(_exec_temp, __LINE__)(m_exec & exec_mask(vbool(cond))); if (any(CPPSPMD_GLUER2(_exec_temp, __LINE__))) \249{ exec_mask _orig_exec = m_exec; m_exec = CPPSPMD_GLUER2(_exec_temp, __LINE__);250251#define SPMD_SELSE2(cond) m_exec = _orig_exec; } exec_mask CPPSPMD_GLUER2(_exec_temp, __LINE__)(m_exec & exec_mask(!vbool(cond))); if (any(CPPSPMD_GLUER2(_exec_temp, __LINE__))) \252{ exec_mask _orig_exec = m_exec; m_exec = CPPSPMD_GLUER2(_exec_temp, __LINE__);253254#define SPMD_SEND_IF2 m_exec = _orig_exec; }255256// Same as SPMD_SIF(), except the if/else blocks are always executed257#define SPMD_SAIF(cond) exec_mask CPPSPMD_GLUER2(_exec_temp, __LINE__)(m_exec & exec_mask(vbool(cond))); { CPPSPMD::scoped_exec_restorer CPPSPMD_GLUER2(_exec_restore_, __LINE__)(&m_exec); \258m_exec = CPPSPMD_GLUER2(_exec_temp, __LINE__);259260#define SPMD_SAELSE(cond) } exec_mask CPPSPMD_GLUER2(_exec_temp, __LINE__)(m_exec & exec_mask(!vbool(cond))); { CPPSPMD::scoped_exec_restorer CPPSPMD_GLUER2(_exec_restore_, __LINE__)(&m_exec); \261m_exec = CPPSPMD_GLUER2(_exec_temp, __LINE__);262263#define SPMD_SAENDIF }264265// Cannot use SPMD break, continue, or return inside sselect266#define SPMD_SSELECT(var) do { vint_t _select_var = var; scoped_exec_restorer _orig_exec(&m_exec); exec_mask _select_executed(exec_mask::all_off());267#define SPMD_SCASE(value) exec_mask CPPSPMD_GLUER2(_exec_temp, __LINE__)(_orig_exec.m_prev_mask & exec_mask(vbool(_select_var == (value)))); if (any(CPPSPMD_GLUER2(_exec_temp, __LINE__))) \268{ m_exec = CPPSPMD_GLUER2(_exec_temp, __LINE__); _select_executed = _select_executed | m_exec;269270//#define SPMD_SCASE_END if (_select_executed.get_movemask() == _orig_exec.m_prev_mask.get_movemask()) break; }271#define SPMD_SCASE_END if (!any(_select_executed ^ _orig_exec.m_prev_mask)) break; }272#define SPMD_SDEFAULT exec_mask _all_other_lanes(andnot(_select_executed, _orig_exec.m_prev_mask)); if (any(_all_other_lanes)) { m_exec = _all_other_lanes;273#define SPMD_SDEFAULT_END }274#define SPMD_SSELECT_END } while(0);275276// Same as SPMD_SSELECT, except all cases are executed.277// Cannot use SPMD break, continue, or return inside sselect278#define SPMD_SASELECT(var) do { vint_t _select_var = var; scoped_exec_restorer _orig_exec(&m_exec); exec_mask _select_executed(exec_mask::all_off());279280#define SPMD_SACASE(value) exec_mask CPPSPMD_GLUER2(_exec_temp, __LINE__)(_orig_exec.m_prev_mask & exec_mask(vbool(_select_var == (value)))); { m_exec = CPPSPMD_GLUER2(_exec_temp, __LINE__); \281_select_executed = _select_executed | m_exec;282283#define SPMD_SACASE_END }284#define SPMD_SADEFAULT exec_mask _all_other_lanes(andnot(_select_executed, _orig_exec.m_prev_mask)); { m_exec = _all_other_lanes;285#define SPMD_SADEFAULT_END }286#define SPMD_SASELECT_END } while(0);287288struct scoped_exec_restorer2289{290spmd_kernel *m_pKernel;291exec_mask m_unexecuted_lanes;292293CPPSPMD_FORCE_INLINE scoped_exec_restorer2(spmd_kernel *pKernel, const vbool &cond) :294m_pKernel(pKernel)295{296exec_mask cond_exec(cond);297m_unexecuted_lanes = andnot(cond_exec, pKernel->m_exec);298pKernel->m_exec = cond_exec & pKernel->m_exec;299}300301CPPSPMD_FORCE_INLINE ~scoped_exec_restorer2()302{303m_pKernel->m_exec = m_pKernel->m_exec | m_unexecuted_lanes;304m_pKernel->check_masks();305}306};307308#define SPMD_IF(cond) { CPPSPMD::scoped_exec_restorer2 CPPSPMD_GLUER2(_exec_restore2_, __LINE__)(this, vbool(cond)); if (any(m_exec)) {309#define SPMD_ELSE(cond) } } { CPPSPMD::scoped_exec_restorer2 CPPSPMD_GLUER2(_exec_restore2_, __LINE__)(this, !vbool(cond)); if (any(m_exec)) {310#define SPMD_END_IF } }311312// Same as SPMD_IF, except the conditional block is always executed.313#define SPMD_AIF(cond) { CPPSPMD::scoped_exec_restorer2 CPPSPMD_GLUER2(_exec_restore2_, __LINE__)(this, vbool(cond)); {314#define SPMD_AELSE(cond) } } { CPPSPMD::scoped_exec_restorer2 CPPSPMD_GLUER2(_exec_restore2_, __LINE__)(this, !vbool(cond)); {315#define SPMD_AEND_IF } }316317class scoped_exec_saver318{319exec_mask m_exec, m_kernel_exec, m_continue_mask;320spmd_kernel *m_pKernel;321#ifdef _DEBUG322bool m_in_loop;323#endif324325public:326inline scoped_exec_saver(spmd_kernel *pKernel) :327m_exec(pKernel->m_exec), m_kernel_exec(pKernel->m_kernel_exec), m_continue_mask(pKernel->m_continue_mask),328m_pKernel(pKernel)329{330#ifdef _DEBUG331m_in_loop = pKernel->m_in_loop;332#endif333}334335inline ~scoped_exec_saver()336{337m_pKernel->m_exec = m_exec;338m_pKernel->m_continue_mask = m_continue_mask;339m_pKernel->m_kernel_exec = m_kernel_exec;340#ifdef _DEBUG341m_pKernel->m_in_loop = m_in_loop;342m_pKernel->check_masks();343#endif344}345};346347#define SPMD_BEGIN_CALL scoped_exec_saver CPPSPMD_GLUER2(_begin_call_scoped_exec_saver, __LINE__)(this); m_continue_mask = exec_mask::all_off();348#define SPMD_BEGIN_CALL_ALL_LANES scoped_exec_saver CPPSPMD_GLUER2(_begin_call_scoped_exec_saver, __LINE__)(this); m_exec = exec_mask::all_on(); m_continue_mask = exec_mask::all_off();349350template<typename ForeachBody>351CPPSPMD_FORCE_INLINE void spmd_kernel::spmd_foreach(int begin, int end, const ForeachBody& foreachBody)352{353if (begin == end)354return;355356if (!any(m_exec))357return;358359// We don't support iterating backwards.360if (begin > end)361std::swap(begin, end);362363exec_mask prev_continue_mask = m_continue_mask, prev_exec = m_exec;364365int total_full = (end - begin) / PROGRAM_COUNT;366int total_partial = (end - begin) % PROGRAM_COUNT;367368lint_t loop_index = begin + program_index;369370const int total_loops = total_full + (total_partial ? 1 : 0);371372m_continue_mask = exec_mask::all_off();373374for (int i = 0; i < total_loops; i++)375{376int n = PROGRAM_COUNT;377if ((i == (total_loops - 1)) && (total_partial))378{379exec_mask partial_mask = exec_mask(vint_t(total_partial) > vint_t(program_index));380m_exec = m_exec & partial_mask;381n = total_partial;382}383384foreachBody(loop_index, n);385386m_exec = m_exec | m_continue_mask;387if (!any(m_exec))388break;389390m_continue_mask = exec_mask::all_off();391check_masks();392393store_all(loop_index, loop_index + PROGRAM_COUNT);394}395396m_exec = prev_exec & m_kernel_exec;397m_continue_mask = prev_continue_mask;398check_masks();399}400401template<typename WhileCondBody, typename WhileBody>402CPPSPMD_FORCE_INLINE void spmd_kernel::spmd_while(const WhileCondBody& whileCondBody, const WhileBody& whileBody)403{404exec_mask orig_exec = m_exec;405406exec_mask orig_continue_mask = m_continue_mask;407m_continue_mask = exec_mask::all_off();408409#ifdef _DEBUG410const bool prev_in_loop = m_in_loop;411m_in_loop = true;412#endif413414while(true)415{416exec_mask cond_exec = exec_mask(whileCondBody());417m_exec = m_exec & cond_exec;418419if (!any(m_exec))420break;421422whileBody();423424m_exec = m_exec | m_continue_mask;425m_continue_mask = exec_mask::all_off();426check_masks();427}428429#ifdef _DEBUG430m_in_loop = prev_in_loop;431#endif432433m_exec = orig_exec & m_kernel_exec;434m_continue_mask = orig_continue_mask;435check_masks();436}437438struct scoped_while_restorer439{440spmd_kernel *m_pKernel;441exec_mask m_orig_exec, m_orig_continue_mask;442#ifdef _DEBUG443bool m_prev_in_loop;444#endif445446CPPSPMD_FORCE_INLINE scoped_while_restorer(spmd_kernel *pKernel) :447m_pKernel(pKernel),448m_orig_exec(pKernel->m_exec),449m_orig_continue_mask(pKernel->m_continue_mask)450{451pKernel->m_continue_mask.all_off();452453#ifdef _DEBUG454m_prev_in_loop = pKernel->m_in_loop;455pKernel->m_in_loop = true;456#endif457}458459CPPSPMD_FORCE_INLINE ~scoped_while_restorer()460{461m_pKernel->m_exec = m_orig_exec & m_pKernel->m_kernel_exec;462m_pKernel->m_continue_mask = m_orig_continue_mask;463#ifdef _DEBUG464m_pKernel->m_in_loop = m_prev_in_loop;465m_pKernel->check_masks();466#endif467}468};469470#undef SPMD_WHILE471#undef SPMD_WEND472#define SPMD_WHILE(cond) { scoped_while_restorer CPPSPMD_GLUER2(_while_restore_, __LINE__)(this); while(true) { exec_mask CPPSPMD_GLUER2(cond_exec, __LINE__) = exec_mask(vbool(cond)); \473m_exec = m_exec & CPPSPMD_GLUER2(cond_exec, __LINE__); if (!any(m_exec)) break;474475#define SPMD_WEND m_exec = m_exec | m_continue_mask; m_continue_mask = exec_mask::all_off(); check_masks(); } }476477// Nesting is not supported (although it will compile, but the results won't make much sense).478#define SPMD_FOREACH(loop_var, bi, ei) if (((bi) != (ei)) && (any(m_exec))) { \479scoped_while_restorer CPPSPMD_GLUER2(_while_restore_, __LINE__)(this); \480uint32_t b = (uint32_t)(bi), e = (uint32_t)(ei); if ((b) > (e)) { std::swap(b, e); } const uint32_t total_full = ((e) - (b)) >> PROGRAM_COUNT_SHIFT, total_partial = ((e) - (b)) & (PROGRAM_COUNT - 1); \481lint_t loop_var = program_index + (int)b; const uint32_t total_loops = total_full + (total_partial ? 1U : 0U); \482for (uint32_t CPPSPMD_GLUER2(_foreach_counter, __LINE__) = 0; CPPSPMD_GLUER2(_foreach_counter, __LINE__) < total_loops; ++CPPSPMD_GLUER2(_foreach_counter, __LINE__)) { \483if ((CPPSPMD_GLUER2(_foreach_counter, __LINE__) == (total_loops - 1)) && (total_partial)) { exec_mask partial_mask = exec_mask(vint_t((int)total_partial) > vint_t(program_index)); m_exec = m_exec & partial_mask; }484485#define SPMD_FOREACH_END(loop_var) m_exec = m_exec | m_continue_mask; if (!any(m_exec)) break; m_continue_mask = exec_mask::all_off(); check_masks(); store_all(loop_var, loop_var + PROGRAM_COUNT); } }486487// Okay to use spmd_continue or spmd_return, but not spmd_break488#define SPMD_FOREACH_ACTIVE(index_var) int64_t index_var; { uint64_t _movemask = m_exec.get_movemask(); if (_movemask) { scoped_while_restorer CPPSPMD_GLUER2(_while_restore_, __LINE__)(this); \489for (uint32_t _i = 0; _i < PROGRAM_COUNT; ++_i) { \490if (_movemask & (1U << _i)) { \491m_exec.enable_lane(_i); m_exec = m_exec & m_kernel_exec; \492(index_var) = _i; \493494#define SPMD_FOREACH_ACTIVE_END } } } }495496// Okay to use spmd_continue, but not spmd_break/spmd_continue497#define SPMD_FOREACH_UNIQUE_INT(index_var, var) { scoped_while_restorer CPPSPMD_GLUER2(_while_restore_, __LINE__)(this); \498CPPSPMD_DECL(int_t, _vals[PROGRAM_COUNT]); store_linear_all(_vals, var); std::sort(_vals, _vals + PROGRAM_COUNT); \499const int _n = (int)(std::unique(_vals, _vals + PROGRAM_COUNT) - _vals); \500for (int _i = 0; _i < _n; ++_i) { int index_var = _vals[_i]; vbool cond = (vint_t(var) == vint_t(index_var)); m_exec = exec_mask(cond);501502#define SPMD_FOREACH_UNIQUE_INT_END } }503504struct scoped_simple_while_restorer505{506spmd_kernel* m_pKernel;507exec_mask m_orig_exec;508#ifdef _DEBUG509bool m_prev_in_loop;510#endif511512CPPSPMD_FORCE_INLINE scoped_simple_while_restorer(spmd_kernel* pKernel) :513m_pKernel(pKernel),514m_orig_exec(pKernel->m_exec)515{516517#ifdef _DEBUG518m_prev_in_loop = pKernel->m_in_loop;519pKernel->m_in_loop = true;520#endif521}522523CPPSPMD_FORCE_INLINE ~scoped_simple_while_restorer()524{525m_pKernel->m_exec = m_orig_exec;526#ifdef _DEBUG527m_pKernel->m_in_loop = m_prev_in_loop;528m_pKernel->check_masks();529#endif530}531};532533// Cannot use SPMD break, continue, or return inside simple while534535#define SPMD_SWHILE(cond) { scoped_simple_while_restorer CPPSPMD_GLUER2(_while_restore_, __LINE__)(this); \536while(true) { \537exec_mask CPPSPMD_GLUER2(cond_exec, __LINE__) = exec_mask(vbool(cond)); m_exec = m_exec & CPPSPMD_GLUER2(cond_exec, __LINE__); if (!any(m_exec)) break;538#define SPMD_SWEND } }539540// Cannot use SPMD break, continue, or return inside simple do541#define SPMD_SDO { scoped_simple_while_restorer CPPSPMD_GLUER2(_while_restore_, __LINE__)(this); while(true) {542#define SPMD_SEND_DO(cond) exec_mask CPPSPMD_GLUER2(cond_exec, __LINE__) = exec_mask(vbool(cond)); m_exec = m_exec & CPPSPMD_GLUER2(cond_exec, __LINE__); if (!any(m_exec)) break; } }543544#undef SPMD_FOR545#undef SPMD_END_FOR546#define SPMD_FOR(for_init, for_cond) { for_init; scoped_while_restorer CPPSPMD_GLUER2(_while_restore_, __LINE__)(this); while(true) { exec_mask CPPSPMD_GLUER2(cond_exec, __LINE__) = exec_mask(vbool(for_cond)); \547m_exec = m_exec & CPPSPMD_GLUER2(cond_exec, __LINE__); if (!any(m_exec)) break;548#define SPMD_END_FOR(for_inc) m_exec = m_exec | m_continue_mask; m_continue_mask = exec_mask::all_off(); check_masks(); for_inc; } }549550template<typename ForInitBody, typename ForCondBody, typename ForIncrBody, typename ForBody>551CPPSPMD_FORCE_INLINE void spmd_kernel::spmd_for(const ForInitBody& forInitBody, const ForCondBody& forCondBody, const ForIncrBody& forIncrBody, const ForBody& forBody)552{553exec_mask orig_exec = m_exec;554555forInitBody();556557exec_mask orig_continue_mask = m_continue_mask;558m_continue_mask = exec_mask::all_off();559560#ifdef _DEBUG561const bool prev_in_loop = m_in_loop;562m_in_loop = true;563#endif564565while(true)566{567exec_mask cond_exec = exec_mask(forCondBody());568m_exec = m_exec & cond_exec;569570if (!any(m_exec))571break;572573forBody();574575m_exec = m_exec | m_continue_mask;576m_continue_mask = exec_mask::all_off();577check_masks();578579forIncrBody();580}581582m_exec = orig_exec & m_kernel_exec;583m_continue_mask = orig_continue_mask;584585#ifdef _DEBUG586m_in_loop = prev_in_loop;587check_masks();588#endif589}590591592