• R/O
  • SSH
  • HTTPS

eircompile: Commit


Commit MetaInfo

Revision63 (tree)
Time2021-12-21 20:31:25
Authorquiret

Log Message

- fixed a rare bug introduced in the recently added denial-inheritance-guaranteee optimization which happened in interplay with the undeniable-change optimization due to mismatching restrictions on calculation path behaviour
- added a unit test to reflect the bugfix

Change Summary

Incremental Difference

--- eircompile/include/eircompile/lexing_env.h (revision 62)
+++ eircompile/include/eircompile/lexing_env.h (revision 63)
@@ -2794,6 +2794,7 @@
27942794 this->pcalc = std::move( right.pcalc );
27952795 this->is_inline_call = right.is_inline_call;
27962796 this->is_sso = right.is_sso;
2797+ this->sso_is_just_undeniable_change = right.sso_is_just_undeniable_change;
27972798 this->is_attrib_continuation = right.is_attrib_continuation;
27982799 this->requires_init = right.requires_init;
27992800 this->was_called_by_extend = right.was_called_by_extend;
@@ -2809,6 +2810,7 @@
28092810 right.pcalc = std::monostate();
28102811 right.is_inline_call = false;
28112812 right.is_sso = false;
2813+ right.sso_is_just_undeniable_change = false;
28122814 right.is_attrib_continuation = false;
28132815 right.requires_init = false;
28142816 right.was_called_by_extend = false;
@@ -2857,6 +2859,7 @@
28572859 this->pcalc = std::move( right.pcalc );
28582860 this->is_inline_call = right.is_inline_call;
28592861 this->is_sso = right.is_sso;
2862+ this->sso_is_just_undeniable_change = right.sso_is_just_undeniable_change;
28602863 this->is_attrib_continuation = right.is_attrib_continuation;
28612864 this->requires_init = right.requires_init;
28622865 this->was_called_by_extend = right.was_called_by_extend;
@@ -2872,6 +2875,7 @@
28722875 right.pcalc = std::monostate();
28732876 right.is_inline_call = false;
28742877 right.is_sso = false;
2878+ right.sso_is_just_undeniable_change = false;
28752879 right.is_attrib_continuation = false;
28762880 right.requires_init = false;
28772881 right.was_called_by_extend = false;
@@ -2922,6 +2926,7 @@
29222926
29232927 // We no longer have a value that stems from below, so remember that.
29242928 this->is_sso = false;
2929+ this->sso_is_just_undeniable_change = false;
29252930
29262931 return true;
29272932 }
@@ -3133,6 +3138,7 @@
31333138 std::variant <std::monostate, cNodeType*, node_selector*> pcalc;
31343139 bool is_inline_call = false;
31353140 bool is_sso = false; // is this entry using single-step-optimization?
3141+ bool sso_is_just_undeniable_change = false; // is this SSO on the grounds of undeniable-change?
31363142 bool is_attrib_continuation = false;
31373143 bool requires_init = false; // late-init of constr_node for calculatory paths (optimization).
31383144 bool was_called_by_extend = false;
@@ -3460,10 +3466,36 @@
34603466 if ( entry.requires_init )
34613467 {
34623468 entry.is_inline_call = true; // We are supposed to take-over this node as replacement on return.
3463- entry.is_sso = ( current_step->IsUndeniableChange() || prevStep->DoesInheritSubStepDenial( prevEntry->process_idx ) );
34643469
3465- if ( entry.is_sso == false && sel_man != nullptr )
3470+ // Decide about SSO.
3471+ bool is_sso = false;
3472+
3473+ if ( prevEntry->is_inline_call && prevEntry->is_sso && prevEntry->sso_is_just_undeniable_change )
34663474 {
3475+ bool is_undeniable_change = current_step->IsUndeniableChange();
3476+
3477+ if ( is_undeniable_change )
3478+ {
3479+ is_sso = true;
3480+ entry.sso_is_just_undeniable_change = true;
3481+ }
3482+ }
3483+ else
3484+ {
3485+ if ( prevStep->DoesInheritSubStepDenial( prevEntry->process_idx ) )
3486+ {
3487+ is_sso = true;
3488+ }
3489+ else if ( current_step->IsUndeniableChange() )
3490+ {
3491+ is_sso = true;
3492+ entry.sso_is_just_undeniable_change = true;
3493+ }
3494+ }
3495+ entry.is_sso = is_sso;
3496+
3497+ if ( is_sso == false && sel_man != nullptr )
3498+ {
34673499 requires_selector_memory = true;
34683500 }
34693501 }
--- eircompile/include/eircompile/lexing_selectors.h (revision 62)
+++ eircompile/include/eircompile/lexing_selectors.h (revision 63)
@@ -12,14 +12,10 @@
1212 LexerStructHasAssignAttribute <structType, charType> &&
1313 std::is_invocable_r <binNodeType*, decltype(&structType::CreateOperation), structType&, cNodeType* /* left */, cNodeType* /* right */>::value;
1414
15-template <typename structType, typename charType, typename cNodeType, typename binNodeType = cNodeType>
16-concept BinaryMutableOperatorDispatcher =
17- BinaryOperatorDispatcher <structType, charType, cNodeType, binNodeType> &&
18- std::is_invocable_r <binNodeType*, decltype(&binNodeType::OpDeepClone), const binNodeType&>::value &&
19- std::is_invocable_r <void, decltype(&binNodeType::SetLeft), binNodeType&, cNodeType*>::value &&
20- std::is_invocable_r <void, decltype(&binNodeType::SetRight), binNodeType&, cNodeType*>::value &&
21- std::is_invocable_r <cNodeType*, decltype(&binNodeType::GetLeft), const binNodeType&>::value &&
22- std::is_invocable_r <cNodeType*, decltype(&binNodeType::GetRight), const binNodeType&>::value;
15+template <typename structType, typename charType, typename cNodeType>
16+concept OperatorDispatcherHasNodeDisambiguator =
17+ std::is_invocable_r <bool, decltype(structType::IsOperationNode), const eir::FixedString <charType>& /* attrib */>::value &&
18+ LexerStructHasAssignNode <structType, charType, cNodeType>;
2319
2420 // Build a left-associative tree with special nodes.
2521 template <
@@ -107,6 +103,14 @@
107103
108104 inline bool AssignNode( const eir::FixedString <char>& attrib, nodeType *node )
109105 {
106+ if constexpr ( OperatorDispatcherHasNodeDisambiguator <binOpDisp, charType, nodeType> )
107+ {
108+ if ( binOpDisp::IsOperationNode( attrib ) == false )
109+ {
110+ return binOpDisp.AssignNode( attrib, node );
111+ }
112+ }
113+
110114 nodeType *root = this->root;
111115
112116 if ( root == nullptr )
@@ -225,6 +229,14 @@
225229
226230 inline bool AssignNode( const eir::FixedString <charType>& attrib, nodeType *node )
227231 {
232+ if constexpr ( OperatorDispatcherHasNodeDisambiguator <binOpDisp, charType, nodeType> )
233+ {
234+ if ( binOpDisp::IsOperationNode( attrib ) == false )
235+ {
236+ return binOpDisp.AssignNode( attrib, node );
237+ }
238+ }
239+
228240 if ( this->nodes_in_order.GetCount() > 0 )
229241 {
230242 // There has to have been an operand so add it to the operands-list.
@@ -379,6 +391,14 @@
379391
380392 inline bool AssignNode( const eir::FixedString <charType>& attrib, cNodeType *node )
381393 {
394+ if constexpr ( OperatorDispatcherHasNodeDisambiguator <unaOpDisp, charType, cNodeType> )
395+ {
396+ if ( unaOpDisp::IsOperationNode( attrib ) == false )
397+ {
398+ return unaOpDisp.AssignNode( attrib, node );
399+ }
400+ }
401+
382402 if ( cNodeType *prev_root = this->root )
383403 {
384404 if constexpr ( LexerStructMutable <cNodeType, charType, cNodeType> )
--- testcompiler/src/cos_tests.cpp (revision 62)
+++ testcompiler/src/cos_tests.cpp (revision 63)
@@ -233,6 +233,44 @@
233233 using UnaryOperation::UnaryOperation;
234234 };
235235
236+struct CastOperation : public Operation
237+{
238+ inline CastOperation( COSNode *totype = nullptr, COSNode *castobj = nullptr ) noexcept : totype( totype ), castobj( castobj )
239+ {
240+ return;
241+ }
242+ inline CastOperation( const CastOperation& ) = default;
243+ inline CastOperation( CastOperation&& right ) noexcept
244+ {
245+ this->totype = right.totype;
246+ this->castobj = right.castobj;
247+
248+ right.totype = nullptr;
249+ right.castobj = nullptr;
250+ }
251+
252+ COSNode *totype;
253+ COSNode *castobj;
254+};
255+
256+struct TemplateInstantiationOperation : public Operation
257+{
258+ inline TemplateInstantiationOperation( COSNode *templ = nullptr, depVector <COSNode*> tparams = {} ) noexcept : templ( templ ), tparams( std::move( tparams ) )
259+ {
260+ return;
261+ }
262+ inline TemplateInstantiationOperation( const TemplateInstantiationOperation& ) = default;
263+ inline TemplateInstantiationOperation( TemplateInstantiationOperation&& right ) noexcept : tparams( std::move( right.tparams ) )
264+ {
265+ this->templ = right.templ;
266+
267+ right.templ = nullptr;
268+ }
269+
270+ COSNode *templ;
271+ depVector <COSNode*> tparams;
272+};
273+
236274 struct IfStatement : public COSNode
237275 {
238276 inline IfStatement( COSNode *condition = nullptr, COSNode *true_branch = nullptr, COSNode *false_branch = nullptr ) noexcept : condition( condition ), true_branch( true_branch ), false_branch( false_branch )
@@ -1330,8 +1368,16 @@
13301368 };
13311369 operation.GetLastStep()->setSelector <left_associative_selector <char, COSNode, key_dispatcher, decltype(lexer)::RuntimeEnv>> ( &lexer.GetRuntimeEnvironment() );
13321370 }
1333- assert( lexer.CompileInto( compiler, operation, "[opitem:operation, spaces optype:'.' spaces]" ) != nullptr );
1371+ assert( lexer.CompileInto( compiler, operation, "[istempl_inst:\"template\" spaces op:operation spaces '<' spaces [<0> procgate type, spaces ',' spaces] spaces '>' | op:operation, spaces '.' spaces]" ) != nullptr );
13341372 {
1373+ struct dot_with_templ_dispatcher
1374+ {
1375+
1376+ };
1377+ }
1378+#if 0
1379+ assert( lexer.CompileInto( compiler, operation, "[opitem:operation, spaces '.' spaces]" ) != nullptr );
1380+ {
13351381 struct dot_dispatcher
13361382 {
13371383 inline void AssignAttribute( const eir::FixedString <char>& attrib, const eir::FixedString <char>& value )
@@ -1346,6 +1392,7 @@
13461392 };
13471393 operation.GetLastStep()->setSelector <left_associative_selector <char, COSNode, dot_dispatcher, decltype(lexer)::RuntimeEnv>> ( &lexer.GetRuntimeEnvironment() );
13481394 }
1395+#endif //0
13491396
13501397 auto tailop = lexer.MakeStep <decltype(lexer)::RuntimeEnv::StepAlternatives> ();
13511398
--- testcompiler/src/lexingenv_tests.cpp (revision 62)
+++ testcompiler/src/lexingenv_tests.cpp (revision 63)
@@ -683,7 +683,7 @@
683683 }
684684 printf( "ok.\n" );
685685
686- printf( "testing ProductionMachine denial-inheritance-guarantee optimization..." );
686+ printf( "testing ProductionMachine denial-inheritance-guarantee optimization (basic sequence enclosure)..." );
687687 {
688688 // It should be a natural idea that enclosing inline sequences should contribute to the
689689 // same working-copy object like the sequence which encloses them, especially if they are
@@ -738,6 +738,128 @@
738738 }
739739 printf( "ok.\n" );
740740
741+ printf( "testing ProductionMachine denial-inheritance-guarantee conflict with undeniable-change (repeat-any-step)..." );
742+ {
743+ // There is a conflict between the general undeniable-change optimization while in interplay with the
744+ // denial-inheritance-guarantee optimization. For example we have a step-repeat hosting a single-step
745+ // sequence which hosts a two step sequence. The repeat-step will grant SSO to the single-step sequence
746+ // on the grounds of undeniable-change. In the buggy implementation the single-step sequence will grant
747+ // SSO to the two-step sequence on the grounds of denial-inheritance-guarantee (DIG). This is a bug because
748+ // the repeat-step would never grant any sub-step SSO based on DIG (sub-steps of repeat are free to make
749+ // their own object copies and grant SSO based on such copy for any reason).
750+
751+ struct node
752+ {
753+ virtual ~node( void )
754+ {
755+ return;
756+ }
757+
758+ inline void Delete( void ) noexcept
759+ {
760+ delete this;
761+ }
762+
763+ virtual node* Clone( void ) = 0;
764+
765+ virtual void AssignAttribute( const eir::FixedString <char>& attrib, const eir::FixedString <char>& value ) = 0;
766+ };
767+
768+ struct counter_node : public node
769+ {
770+ node* Clone( void ) override
771+ {
772+ return new counter_node( *this );
773+ }
774+
775+ void AssignAttribute( const eir::FixedString <char>& attrib, const eir::FixedString <char>& value ) override
776+ {
777+ if ( value == "A" )
778+ {
779+ this->num_a++;
780+ }
781+ else if ( value == "B" )
782+ {
783+ this->num_b++;
784+ }
785+ }
786+
787+ size_t num_a = 0;
788+ size_t num_b = 0;
789+ };
790+
791+ LexingEnvironment <char, node, EirHeapLinkAllocator> env( eir::constr_with_alloc::DEFAULT, &globalHeapAlloc );
792+
793+ decltype(env)::StepToken tok_a( &env );
794+ tok_a.token_str = "A";
795+ tok_a.attribute_key = "tok";
796+
797+ decltype(env)::StepToken tok_b( &env );
798+ tok_b.token_str = "B";
799+ tok_b.attribute_key = "tok";
800+
801+ decltype(env)::StepToken tok_a_unreg( &env );
802+ tok_a_unreg.token_str = "A";
803+
804+ decltype(env)::StepToken tok_b_unreg( &env );
805+ tok_b_unreg.token_str = "B";
806+
807+ decltype(env)::SerializedStep seq_ab( &env );
808+ seq_ab.SetConnections( { &tok_a, &tok_b } );
809+
810+ decltype(env)::SerializedStep seqpipe( &env );
811+ seqpipe.AddConnection( &seq_ab );
812+
813+ decltype(env)::StepRepeatAny repeat( &env );
814+ repeat.SetNode( &seqpipe );
815+ repeat.setConstructor(
816+ []
817+ {
818+ return new counter_node();
819+ }
820+ );
821+
822+ decltype(env)::StepAlternatives alt_ab_unreg( &env );
823+ alt_ab_unreg.SetConnections( { &tok_a_unreg, &tok_b_unreg } );
824+
825+ decltype(env)::StepRepeatAny repeat_cleanup( &env );
826+ repeat_cleanup.SetNode( &alt_ab_unreg );
827+
828+ decltype(env)::SerializedStep producible( &env );
829+ producible.SetConnections( { &repeat, &repeat_cleanup } );
830+ producible.take_first_succ_node = true;
831+
832+ decltype(env)::ProductionMachine machine( &env );
833+
834+ machine.Execute( "ABABAB", &producible );
835+
836+ assert( machine.successful == true );
837+ {
838+ counter_node *cn = dynamic_cast <counter_node*> ( machine.DetachNode() );
839+
840+ assert( cn != nullptr );
841+ assert( cn->num_a == 3 );
842+ assert( cn->num_b == 3 );
843+
844+ cn->Delete();
845+ }
846+
847+ // This test produces an invalid count of 3 As and 2 Bs if the optimization implementation is buggy.
848+ machine.Execute( "ABABA", &producible );
849+
850+ assert( machine.successful == true );
851+ {
852+ counter_node *cn = dynamic_cast <counter_node*> ( machine.DetachNode() );
853+
854+ assert( cn != nullptr );
855+ assert( cn->num_a == 2 ); // it should only count pairs, not individuals.
856+ assert( cn->num_b == 2 );
857+
858+ cn->Delete();
859+ }
860+ }
861+ printf( "ok.\n" );
862+
741863 printf( "testing ProductionMachine StepExtend..." );
742864 {
743865 TestAllocEnv env( eir::constr_with_alloc::DEFAULT, &globalHeapAlloc );
Show on old repository browser