From 3434a695d231848e33be1d60fe0fa7584df59241 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Wed, 3 Apr 2019 00:03:37 +0200 Subject: [PATCH] Implemented a bunch of the foundation needed for function pointers - incl. the parser and handling of pointers in zeropage variables. --- src/main/fragment/pprz1=pprc1.asm | 4 + .../java/dk/camelot64/kickc/parser/KickC.g4 | 2 +- .../kickc/parser/KickCBaseListener.java | 2 +- .../kickc/parser/KickCBaseVisitor.java | 2 +- .../dk/camelot64/kickc/parser/KickCLexer.java | 2 +- .../camelot64/kickc/parser/KickCListener.java | 2 +- .../camelot64/kickc/parser/KickCParser.java | 305 ++++----- .../camelot64/kickc/parser/KickCVisitor.java | 2 +- .../Pass0GenerateStatementSequence.java | 10 +- .../dk/camelot64/kickc/test/TestPrograms.java | 15 + src/test/kc/function-pointer-noarg-2.kc | 25 + src/test/kc/function-pointer-noarg-3.kc | 35 + src/test/kc/function-pointer-noarg.kc | 24 + src/test/ref/function-pointer-noarg-2.asm | 15 + src/test/ref/function-pointer-noarg-2.cfg | 27 + src/test/ref/function-pointer-noarg-2.log | 437 +++++++++++++ src/test/ref/function-pointer-noarg-2.sym | 17 + src/test/ref/function-pointer-noarg-3.asm | 41 ++ src/test/ref/function-pointer-noarg-3.cfg | 45 ++ src/test/ref/function-pointer-noarg-3.log | 619 ++++++++++++++++++ src/test/ref/function-pointer-noarg-3.sym | 28 + src/test/ref/function-pointer-noarg.asm | 26 + src/test/ref/function-pointer-noarg.cfg | 30 + src/test/ref/function-pointer-noarg.log | 427 ++++++++++++ src/test/ref/function-pointer-noarg.sym | 17 + 25 files changed, 2001 insertions(+), 158 deletions(-) create mode 100644 src/main/fragment/pprz1=pprc1.asm create mode 100644 src/test/kc/function-pointer-noarg-2.kc create mode 100644 src/test/kc/function-pointer-noarg-3.kc create mode 100644 src/test/kc/function-pointer-noarg.kc create mode 100644 src/test/ref/function-pointer-noarg-2.asm create mode 100644 src/test/ref/function-pointer-noarg-2.cfg create mode 100644 src/test/ref/function-pointer-noarg-2.log create mode 100644 src/test/ref/function-pointer-noarg-2.sym create mode 100644 src/test/ref/function-pointer-noarg-3.asm create mode 100644 src/test/ref/function-pointer-noarg-3.cfg create mode 100644 src/test/ref/function-pointer-noarg-3.log create mode 100644 src/test/ref/function-pointer-noarg-3.sym create mode 100644 src/test/ref/function-pointer-noarg.asm create mode 100644 src/test/ref/function-pointer-noarg.cfg create mode 100644 src/test/ref/function-pointer-noarg.log create mode 100644 src/test/ref/function-pointer-noarg.sym diff --git a/src/main/fragment/pprz1=pprc1.asm b/src/main/fragment/pprz1=pprc1.asm new file mode 100644 index 000000000..4c71afd48 --- /dev/null +++ b/src/main/fragment/pprz1=pprc1.asm @@ -0,0 +1,4 @@ +lda #<{c1} +sta {z1} +lda #>{c1} +sta {z1}+1 \ No newline at end of file diff --git a/src/main/java/dk/camelot64/kickc/parser/KickC.g4 b/src/main/java/dk/camelot64/kickc/parser/KickC.g4 index 025c3ddac..9f7974a7c 100644 --- a/src/main/java/dk/camelot64/kickc/parser/KickC.g4 +++ b/src/main/java/dk/camelot64/kickc/parser/KickC.g4 @@ -90,7 +90,7 @@ typeDecl expr : '(' expr ')' #exprPar - | NAME '(' parameterList? ')' #exprCall + | expr '(' parameterList? ')' #exprCall | expr '[' expr ']' #exprArray | '(' typeDecl ')' expr #exprCast | ('--' | '++' ) expr #exprPreMod diff --git a/src/main/java/dk/camelot64/kickc/parser/KickCBaseListener.java b/src/main/java/dk/camelot64/kickc/parser/KickCBaseListener.java index cc5574640..bb165e4fa 100644 --- a/src/main/java/dk/camelot64/kickc/parser/KickCBaseListener.java +++ b/src/main/java/dk/camelot64/kickc/parser/KickCBaseListener.java @@ -1,4 +1,4 @@ -// Generated from C:/c64/kickc/src/main/java/dk/camelot64/kickc/parser\KickC.g4 by ANTLR 4.7 +// Generated from /Users/jespergravgaard/c64/kickc/src/main/java/dk/camelot64/kickc/parser/KickC.g4 by ANTLR 4.7 package dk.camelot64.kickc.parser; import org.antlr.v4.runtime.ParserRuleContext; diff --git a/src/main/java/dk/camelot64/kickc/parser/KickCBaseVisitor.java b/src/main/java/dk/camelot64/kickc/parser/KickCBaseVisitor.java index a164882e8..7a01e1a92 100644 --- a/src/main/java/dk/camelot64/kickc/parser/KickCBaseVisitor.java +++ b/src/main/java/dk/camelot64/kickc/parser/KickCBaseVisitor.java @@ -1,4 +1,4 @@ -// Generated from C:/c64/kickc/src/main/java/dk/camelot64/kickc/parser\KickC.g4 by ANTLR 4.7 +// Generated from /Users/jespergravgaard/c64/kickc/src/main/java/dk/camelot64/kickc/parser/KickC.g4 by ANTLR 4.7 package dk.camelot64.kickc.parser; import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; diff --git a/src/main/java/dk/camelot64/kickc/parser/KickCLexer.java b/src/main/java/dk/camelot64/kickc/parser/KickCLexer.java index 2386edf1a..3baa197e2 100644 --- a/src/main/java/dk/camelot64/kickc/parser/KickCLexer.java +++ b/src/main/java/dk/camelot64/kickc/parser/KickCLexer.java @@ -1,4 +1,4 @@ -// Generated from C:/c64/kickc/src/main/java/dk/camelot64/kickc/parser\KickC.g4 by ANTLR 4.7 +// Generated from /Users/jespergravgaard/c64/kickc/src/main/java/dk/camelot64/kickc/parser/KickC.g4 by ANTLR 4.7 package dk.camelot64.kickc.parser; import org.antlr.v4.runtime.Lexer; import org.antlr.v4.runtime.CharStream; diff --git a/src/main/java/dk/camelot64/kickc/parser/KickCListener.java b/src/main/java/dk/camelot64/kickc/parser/KickCListener.java index d4cccfeaf..59c83252f 100644 --- a/src/main/java/dk/camelot64/kickc/parser/KickCListener.java +++ b/src/main/java/dk/camelot64/kickc/parser/KickCListener.java @@ -1,4 +1,4 @@ -// Generated from C:/c64/kickc/src/main/java/dk/camelot64/kickc/parser\KickC.g4 by ANTLR 4.7 +// Generated from /Users/jespergravgaard/c64/kickc/src/main/java/dk/camelot64/kickc/parser/KickC.g4 by ANTLR 4.7 package dk.camelot64.kickc.parser; import org.antlr.v4.runtime.tree.ParseTreeListener; diff --git a/src/main/java/dk/camelot64/kickc/parser/KickCParser.java b/src/main/java/dk/camelot64/kickc/parser/KickCParser.java index 3712cf3d7..bdc5245fb 100644 --- a/src/main/java/dk/camelot64/kickc/parser/KickCParser.java +++ b/src/main/java/dk/camelot64/kickc/parser/KickCParser.java @@ -1,4 +1,4 @@ -// Generated from C:/c64/kickc/src/main/java/dk/camelot64/kickc/parser\KickC.g4 by ANTLR 4.7 +// Generated from /Users/jespergravgaard/c64/kickc/src/main/java/dk/camelot64/kickc/parser/KickC.g4 by ANTLR 4.7 package dk.camelot64.kickc.parser; import org.antlr.v4.runtime.atn.*; import org.antlr.v4.runtime.dfa.DFA; @@ -2309,7 +2309,9 @@ public class KickCParser extends Parser { } } public static class ExprCallContext extends ExprContext { - public TerminalNode NAME() { return getToken(KickCParser.NAME, 0); } + public ExprContext expr() { + return getRuleContext(ExprContext.class,0); + } public ParameterListContext parameterList() { return getRuleContext(ParameterListContext.class,0); } @@ -2481,9 +2483,9 @@ public class KickCParser extends Parser { int _alt; enterOuterAlt(_localctx, 1); { - setState(352); + setState(346); _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,36,_ctx) ) { + switch ( getInterpreter().adaptivePredict(_input,35,_ctx) ) { case 1: { _localctx = new ExprParContext(_localctx); @@ -2500,48 +2502,25 @@ public class KickCParser extends Parser { break; case 2: { - _localctx = new ExprCallContext(_localctx); + _localctx = new ExprCastContext(_localctx); _ctx = _localctx; _prevctx = _localctx; setState(317); - match(NAME); - setState(318); match(T__3); - setState(320); - _errHandler.sync(this); - _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__3) | (1L << T__5) | (1L << T__27) | (1L << T__30) | (1L << T__31) | (1L << T__32) | (1L << T__33) | (1L << T__34) | (1L << T__35) | (1L << T__36) | (1L << T__41) | (1L << T__42))) != 0) || ((((_la - 75)) & ~0x3f) == 0 && ((1L << (_la - 75)) & ((1L << (STRING - 75)) | (1L << (CHAR - 75)) | (1L << (BOOLEAN - 75)) | (1L << (NUMBER - 75)) | (1L << (NAME - 75)))) != 0)) { - { - setState(319); - parameterList(); - } - } - - setState(322); + setState(318); + typeDecl(0); + setState(319); match(T__4); + setState(320); + expr(23); } break; case 3: - { - _localctx = new ExprCastContext(_localctx); - _ctx = _localctx; - _prevctx = _localctx; - setState(323); - match(T__3); - setState(324); - typeDecl(0); - setState(325); - match(T__4); - setState(326); - expr(23); - } - break; - case 4: { _localctx = new ExprPreModContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(328); + setState(322); _la = _input.LA(1); if ( !(_la==T__30 || _la==T__31) ) { _errHandler.recoverInline(this); @@ -2551,27 +2530,27 @@ public class KickCParser extends Parser { _errHandler.reportMatch(this); consume(); } - setState(329); + setState(323); expr(22); } break; - case 5: + case 4: { _localctx = new ExprPtrContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(330); + setState(324); match(T__27); - setState(331); + setState(325); expr(20); } break; - case 6: + case 5: { _localctx = new ExprUnaryContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(332); + setState(326); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__32) | (1L << T__33) | (1L << T__34) | (1L << T__35) | (1L << T__36))) != 0)) ) { _errHandler.recoverInline(this); @@ -2581,16 +2560,16 @@ public class KickCParser extends Parser { _errHandler.reportMatch(this); consume(); } - setState(333); + setState(327); expr(19); } break; - case 7: + case 6: { _localctx = new ExprUnaryContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(334); + setState(328); _la = _input.LA(1); if ( !(_la==T__41 || _la==T__42) ) { _errHandler.recoverInline(this); @@ -2600,81 +2579,81 @@ public class KickCParser extends Parser { _errHandler.reportMatch(this); consume(); } - setState(335); + setState(329); expr(15); } break; - case 8: + case 7: { _localctx = new InitListContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(336); + setState(330); match(T__5); - setState(337); + setState(331); expr(0); - setState(342); + setState(336); _errHandler.sync(this); _la = _input.LA(1); while (_la==T__7) { { { - setState(338); + setState(332); match(T__7); - setState(339); + setState(333); expr(0); } } - setState(344); + setState(338); _errHandler.sync(this); _la = _input.LA(1); } - setState(345); + setState(339); match(T__6); } break; - case 9: + case 8: { _localctx = new ExprIdContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(347); + setState(341); match(NAME); } break; - case 10: + case 9: { _localctx = new ExprNumberContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(348); + setState(342); match(NUMBER); } break; - case 11: + case 10: { _localctx = new ExprStringContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(349); + setState(343); match(STRING); } break; - case 12: + case 11: { _localctx = new ExprCharContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(350); + setState(344); match(CHAR); } break; - case 13: + case 12: { _localctx = new ExprBoolContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(351); + setState(345); match(BOOLEAN); } break; @@ -2695,9 +2674,9 @@ public class KickCParser extends Parser { { _localctx = new ExprBinaryContext(new ExprContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expr); - setState(354); + setState(348); if (!(precpred(_ctx, 18))) throw new FailedPredicateException(this, "precpred(_ctx, 18)"); - setState(355); + setState(349); _la = _input.LA(1); if ( !(_la==T__37 || _la==T__38) ) { _errHandler.recoverInline(this); @@ -2707,7 +2686,7 @@ public class KickCParser extends Parser { _errHandler.reportMatch(this); consume(); } - setState(356); + setState(350); expr(19); } break; @@ -2715,9 +2694,9 @@ public class KickCParser extends Parser { { _localctx = new ExprBinaryContext(new ExprContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expr); - setState(357); + setState(351); if (!(precpred(_ctx, 17))) throw new FailedPredicateException(this, "precpred(_ctx, 17)"); - setState(358); + setState(352); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__27) | (1L << T__39) | (1L << T__40))) != 0)) ) { _errHandler.recoverInline(this); @@ -2727,7 +2706,7 @@ public class KickCParser extends Parser { _errHandler.reportMatch(this); consume(); } - setState(359); + setState(353); expr(18); } break; @@ -2735,9 +2714,9 @@ public class KickCParser extends Parser { { _localctx = new ExprBinaryContext(new ExprContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expr); - setState(360); + setState(354); if (!(precpred(_ctx, 16))) throw new FailedPredicateException(this, "precpred(_ctx, 16)"); - setState(361); + setState(355); _la = _input.LA(1); if ( !(_la==T__32 || _la==T__33) ) { _errHandler.recoverInline(this); @@ -2747,7 +2726,7 @@ public class KickCParser extends Parser { _errHandler.reportMatch(this); consume(); } - setState(362); + setState(356); expr(17); } break; @@ -2755,9 +2734,9 @@ public class KickCParser extends Parser { { _localctx = new ExprBinaryContext(new ExprContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expr); - setState(363); + setState(357); if (!(precpred(_ctx, 14))) throw new FailedPredicateException(this, "precpred(_ctx, 14)"); - setState(364); + setState(358); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__41) | (1L << T__42) | (1L << T__43) | (1L << T__44) | (1L << T__45) | (1L << T__46))) != 0)) ) { _errHandler.recoverInline(this); @@ -2767,7 +2746,7 @@ public class KickCParser extends Parser { _errHandler.reportMatch(this); consume(); } - setState(365); + setState(359); expr(15); } break; @@ -2775,13 +2754,13 @@ public class KickCParser extends Parser { { _localctx = new ExprBinaryContext(new ExprContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expr); - setState(366); + setState(360); if (!(precpred(_ctx, 13))) throw new FailedPredicateException(this, "precpred(_ctx, 13)"); { - setState(367); + setState(361); match(T__35); } - setState(368); + setState(362); expr(14); } break; @@ -2789,13 +2768,13 @@ public class KickCParser extends Parser { { _localctx = new ExprBinaryContext(new ExprContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expr); - setState(369); + setState(363); if (!(precpred(_ctx, 12))) throw new FailedPredicateException(this, "precpred(_ctx, 12)"); { - setState(370); + setState(364); match(T__47); } - setState(371); + setState(365); expr(13); } break; @@ -2803,13 +2782,13 @@ public class KickCParser extends Parser { { _localctx = new ExprBinaryContext(new ExprContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expr); - setState(372); + setState(366); if (!(precpred(_ctx, 11))) throw new FailedPredicateException(this, "precpred(_ctx, 11)"); { - setState(373); + setState(367); match(T__48); } - setState(374); + setState(368); expr(12); } break; @@ -2817,13 +2796,13 @@ public class KickCParser extends Parser { { _localctx = new ExprBinaryContext(new ExprContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expr); - setState(375); + setState(369); if (!(precpred(_ctx, 10))) throw new FailedPredicateException(this, "precpred(_ctx, 10)"); { - setState(376); + setState(370); match(T__49); } - setState(377); + setState(371); expr(11); } break; @@ -2831,13 +2810,13 @@ public class KickCParser extends Parser { { _localctx = new ExprBinaryContext(new ExprContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expr); - setState(378); + setState(372); if (!(precpred(_ctx, 9))) throw new FailedPredicateException(this, "precpred(_ctx, 9)"); { - setState(379); + setState(373); match(T__50); } - setState(380); + setState(374); expr(10); } break; @@ -2845,11 +2824,11 @@ public class KickCParser extends Parser { { _localctx = new ExprAssignmentContext(new ExprContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expr); - setState(381); + setState(375); if (!(precpred(_ctx, 8))) throw new FailedPredicateException(this, "precpred(_ctx, 8)"); - setState(382); + setState(376); match(T__1); - setState(383); + setState(377); expr(8); } break; @@ -2857,9 +2836,9 @@ public class KickCParser extends Parser { { _localctx = new ExprAssignmentCompoundContext(new ExprContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expr); - setState(384); + setState(378); if (!(precpred(_ctx, 7))) throw new FailedPredicateException(this, "precpred(_ctx, 7)"); - setState(385); + setState(379); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__51) | (1L << T__52) | (1L << T__53) | (1L << T__54) | (1L << T__55) | (1L << T__56) | (1L << T__57) | (1L << T__58) | (1L << T__59) | (1L << T__60))) != 0)) ) { _errHandler.recoverInline(this); @@ -2869,11 +2848,33 @@ public class KickCParser extends Parser { _errHandler.reportMatch(this); consume(); } - setState(386); + setState(380); expr(7); } break; case 12: + { + _localctx = new ExprCallContext(new ExprContext(_parentctx, _parentState)); + pushNewRecursionContext(_localctx, _startState, RULE_expr); + setState(381); + if (!(precpred(_ctx, 25))) throw new FailedPredicateException(this, "precpred(_ctx, 25)"); + setState(382); + match(T__3); + setState(384); + _errHandler.sync(this); + _la = _input.LA(1); + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__3) | (1L << T__5) | (1L << T__27) | (1L << T__30) | (1L << T__31) | (1L << T__32) | (1L << T__33) | (1L << T__34) | (1L << T__35) | (1L << T__36) | (1L << T__41) | (1L << T__42))) != 0) || ((((_la - 75)) & ~0x3f) == 0 && ((1L << (_la - 75)) & ((1L << (STRING - 75)) | (1L << (CHAR - 75)) | (1L << (BOOLEAN - 75)) | (1L << (NUMBER - 75)) | (1L << (NAME - 75)))) != 0)) { + { + setState(383); + parameterList(); + } + } + + setState(386); + match(T__4); + } + break; + case 13: { _localctx = new ExprArrayContext(new ExprContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expr); @@ -2887,7 +2888,7 @@ public class KickCParser extends Parser { match(T__29); } break; - case 13: + case 14: { _localctx = new ExprPostModContext(new ExprContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expr); @@ -4342,21 +4343,23 @@ public class KickCParser extends Parser { case 13: return precpred(_ctx, 7); case 14: - return precpred(_ctx, 24); + return precpred(_ctx, 25); case 15: + return precpred(_ctx, 24); + case 16: return precpred(_ctx, 21); } return true; } private boolean asmExpr_sempred(AsmExprContext _localctx, int predIndex) { switch (predIndex) { - case 16: - return precpred(_ctx, 10); case 17: - return precpred(_ctx, 9); + return precpred(_ctx, 10); case 18: - return precpred(_ctx, 7); + return precpred(_ctx, 9); case 19: + return precpred(_ctx, 7); + case 20: return precpred(_ctx, 6); } return true; @@ -4388,12 +4391,12 @@ public class KickCParser extends Parser { "\20\3\20\3\20\3\20\3\20\3\20\3\20\5\20\u011f\n\20\3\21\3\21\3\21\3\21"+ "\3\21\3\21\3\21\3\21\5\21\u0129\n\21\3\21\3\21\3\21\3\21\3\21\5\21\u0130"+ "\n\21\3\21\3\21\3\21\3\21\7\21\u0136\n\21\f\21\16\21\u0139\13\21\3\22"+ - "\3\22\3\22\3\22\3\22\3\22\3\22\3\22\5\22\u0143\n\22\3\22\3\22\3\22\3\22"+ "\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22"+ - "\7\22\u0157\n\22\f\22\16\22\u015a\13\22\3\22\3\22\3\22\3\22\3\22\3\22"+ - "\3\22\5\22\u0163\n\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22"+ + "\3\22\3\22\3\22\3\22\3\22\3\22\3\22\7\22\u0151\n\22\f\22\16\22\u0154\13"+ + "\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\5\22\u015d\n\22\3\22\3\22\3\22"+ "\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22"+ "\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22"+ + "\3\22\3\22\3\22\3\22\3\22\5\22\u0183\n\22\3\22\3\22\3\22\3\22\3\22\3\22"+ "\3\22\3\22\7\22\u018d\n\22\f\22\16\22\u0190\13\22\3\23\3\23\3\23\7\23"+ "\u0195\n\23\f\23\16\23\u0198\13\23\3\24\3\24\5\24\u019c\n\24\3\24\3\24"+ "\3\25\3\25\3\25\3\25\7\25\u01a4\n\25\f\25\16\25\u01a7\13\25\3\25\3\25"+ @@ -4411,7 +4414,7 @@ public class KickCParser extends Parser { "?\4\2#$,-\4\2\36\36**\2\u0268\2:\3\2\2\2\4>\3\2\2\2\6D\3\2\2\2\bG\3\2"+ "\2\2\nK\3\2\2\2\fR\3\2\2\2\16W\3\2\2\2\20k\3\2\2\2\22\u0081\3\2\2\2\24"+ "\u008c\3\2\2\2\26\u00aa\3\2\2\2\30\u00ad\3\2\2\2\32\u00fe\3\2\2\2\34\u0103"+ - "\3\2\2\2\36\u011e\3\2\2\2 \u0128\3\2\2\2\"\u0162\3\2\2\2$\u0191\3\2\2"+ + "\3\2\2\2\36\u011e\3\2\2\2 \u0128\3\2\2\2\"\u015c\3\2\2\2$\u0191\3\2\2"+ "\2&\u0199\3\2\2\2(\u019f\3\2\2\2*\u01b9\3\2\2\2,\u01be\3\2\2\2.\u01c4"+ "\3\2\2\2\60\u01cd\3\2\2\2\62\u01cf\3\2\2\2\64\u01d3\3\2\2\2\66\u01f3\3"+ "\2\2\28\u0203\3\2\2\2:;\5\6\4\2;<\5\n\6\2<=\7\2\2\3=\3\3\2\2\2>?\5,\27"+ @@ -4492,51 +4495,51 @@ public class KickCParser extends Parser { "\u012c\3\2\2\2\u0135\u0132\3\2\2\2\u0136\u0139\3\2\2\2\u0137\u0135\3\2"+ "\2\2\u0137\u0138\3\2\2\2\u0138!\3\2\2\2\u0139\u0137\3\2\2\2\u013a\u013b"+ "\b\22\1\2\u013b\u013c\7\6\2\2\u013c\u013d\5\"\22\2\u013d\u013e\7\7\2\2"+ - "\u013e\u0163\3\2\2\2\u013f\u0140\7Y\2\2\u0140\u0142\7\6\2\2\u0141\u0143"+ - "\5$\23\2\u0142\u0141\3\2\2\2\u0142\u0143\3\2\2\2\u0143\u0144\3\2\2\2\u0144"+ - "\u0163\7\7\2\2\u0145\u0146\7\6\2\2\u0146\u0147\5 \21\2\u0147\u0148\7\7"+ - "\2\2\u0148\u0149\5\"\22\31\u0149\u0163\3\2\2\2\u014a\u014b\t\2\2\2\u014b"+ - "\u0163\5\"\22\30\u014c\u014d\7\36\2\2\u014d\u0163\5\"\22\26\u014e\u014f"+ - "\t\3\2\2\u014f\u0163\5\"\22\25\u0150\u0151\t\4\2\2\u0151\u0163\5\"\22"+ - "\21\u0152\u0153\7\b\2\2\u0153\u0158\5\"\22\2\u0154\u0155\7\n\2\2\u0155"+ - "\u0157\5\"\22\2\u0156\u0154\3\2\2\2\u0157\u015a\3\2\2\2\u0158\u0156\3"+ - "\2\2\2\u0158\u0159\3\2\2\2\u0159\u015b\3\2\2\2\u015a\u0158\3\2\2\2\u015b"+ - "\u015c\7\t\2\2\u015c\u0163\3\2\2\2\u015d\u0163\7Y\2\2\u015e\u0163\7P\2"+ - "\2\u015f\u0163\7M\2\2\u0160\u0163\7N\2\2\u0161\u0163\7O\2\2\u0162\u013a"+ - "\3\2\2\2\u0162\u013f\3\2\2\2\u0162\u0145\3\2\2\2\u0162\u014a\3\2\2\2\u0162"+ - "\u014c\3\2\2\2\u0162\u014e\3\2\2\2\u0162\u0150\3\2\2\2\u0162\u0152\3\2"+ - "\2\2\u0162\u015d\3\2\2\2\u0162\u015e\3\2\2\2\u0162\u015f\3\2\2\2\u0162"+ - "\u0160\3\2\2\2\u0162\u0161\3\2\2\2\u0163\u018e\3\2\2\2\u0164\u0165\f\24"+ - "\2\2\u0165\u0166\t\5\2\2\u0166\u018d\5\"\22\25\u0167\u0168\f\23\2\2\u0168"+ - "\u0169\t\6\2\2\u0169\u018d\5\"\22\24\u016a\u016b\f\22\2\2\u016b\u016c"+ - "\t\7\2\2\u016c\u018d\5\"\22\23\u016d\u016e\f\20\2\2\u016e\u016f\t\b\2"+ - "\2\u016f\u018d\5\"\22\21\u0170\u0171\f\17\2\2\u0171\u0172\7&\2\2\u0172"+ - "\u018d\5\"\22\20\u0173\u0174\f\16\2\2\u0174\u0175\7\62\2\2\u0175\u018d"+ - "\5\"\22\17\u0176\u0177\f\r\2\2\u0177\u0178\7\63\2\2\u0178\u018d\5\"\22"+ - "\16\u0179\u017a\f\f\2\2\u017a\u017b\7\64\2\2\u017b\u018d\5\"\22\r\u017c"+ - "\u017d\f\13\2\2\u017d\u017e\7\65\2\2\u017e\u018d\5\"\22\f\u017f\u0180"+ - "\f\n\2\2\u0180\u0181\7\4\2\2\u0181\u018d\5\"\22\n\u0182\u0183\f\t\2\2"+ - "\u0183\u0184\t\t\2\2\u0184\u018d\5\"\22\t\u0185\u0186\f\32\2\2\u0186\u0187"+ - "\7\37\2\2\u0187\u0188\5\"\22\2\u0188\u0189\7 \2\2\u0189\u018d\3\2\2\2"+ - "\u018a\u018b\f\27\2\2\u018b\u018d\t\2\2\2\u018c\u0164\3\2\2\2\u018c\u0167"+ - "\3\2\2\2\u018c\u016a\3\2\2\2\u018c\u016d\3\2\2\2\u018c\u0170\3\2\2\2\u018c"+ - "\u0173\3\2\2\2\u018c\u0176\3\2\2\2\u018c\u0179\3\2\2\2\u018c\u017c\3\2"+ - "\2\2\u018c\u017f\3\2\2\2\u018c\u0182\3\2\2\2\u018c\u0185\3\2\2\2\u018c"+ - "\u018a\3\2\2\2\u018d\u0190\3\2\2\2\u018e\u018c\3\2\2\2\u018e\u018f\3\2"+ - "\2\2\u018f#\3\2\2\2\u0190\u018e\3\2\2\2\u0191\u0196\5\"\22\2\u0192\u0193"+ - "\7\n\2\2\u0193\u0195\5\"\22\2\u0194\u0192\3\2\2\2\u0195\u0198\3\2\2\2"+ - "\u0196\u0194\3\2\2\2\u0196\u0197\3\2\2\2\u0197%\3\2\2\2\u0198\u0196\3"+ - "\2\2\2\u0199\u019b\7@\2\2\u019a\u019c\5(\25\2\u019b\u019a\3\2\2\2\u019b"+ - "\u019c\3\2\2\2\u019c\u019d\3\2\2\2\u019d\u019e\7K\2\2\u019e\'\3\2\2\2"+ - "\u019f\u01a0\7\6\2\2\u01a0\u01a5\5*\26\2\u01a1\u01a2\7\n\2\2\u01a2\u01a4"+ - "\5*\26\2\u01a3\u01a1\3\2\2\2\u01a4\u01a7\3\2\2\2\u01a5\u01a3\3\2\2\2\u01a5"+ - "\u01a6\3\2\2\2\u01a6\u01a8\3\2\2\2\u01a7\u01a5\3\2\2\2\u01a8\u01a9\7\7"+ - "\2\2\u01a9)\3\2\2\2\u01aa\u01ab\7A\2\2\u01ab\u01ba\7M\2\2\u01ac\u01ad"+ - "\7B\2\2\u01ad\u01ba\7Y\2\2\u01ae\u01af\7C\2\2\u01af\u01ba\7M\2\2\u01b0"+ - "\u01b1\7D\2\2\u01b1\u01ba\5\"\22\2\u01b2\u01b3\7E\2\2\u01b3\u01ba\5\""+ - "\22\2\u01b4\u01b7\7F\2\2\u01b5\u01b8\7\17\2\2\u01b6\u01b8\5\"\22\2\u01b7"+ - "\u01b5\3\2\2\2\u01b7\u01b6\3\2\2\2\u01b8\u01ba\3\2\2\2\u01b9\u01aa\3\2"+ - "\2\2\u01b9\u01ac\3\2\2\2\u01b9\u01ae\3\2\2\2\u01b9\u01b0\3\2\2\2\u01b9"+ + "\u013e\u015d\3\2\2\2\u013f\u0140\7\6\2\2\u0140\u0141\5 \21\2\u0141\u0142"+ + "\7\7\2\2\u0142\u0143\5\"\22\31\u0143\u015d\3\2\2\2\u0144\u0145\t\2\2\2"+ + "\u0145\u015d\5\"\22\30\u0146\u0147\7\36\2\2\u0147\u015d\5\"\22\26\u0148"+ + "\u0149\t\3\2\2\u0149\u015d\5\"\22\25\u014a\u014b\t\4\2\2\u014b\u015d\5"+ + "\"\22\21\u014c\u014d\7\b\2\2\u014d\u0152\5\"\22\2\u014e\u014f\7\n\2\2"+ + "\u014f\u0151\5\"\22\2\u0150\u014e\3\2\2\2\u0151\u0154\3\2\2\2\u0152\u0150"+ + "\3\2\2\2\u0152\u0153\3\2\2\2\u0153\u0155\3\2\2\2\u0154\u0152\3\2\2\2\u0155"+ + "\u0156\7\t\2\2\u0156\u015d\3\2\2\2\u0157\u015d\7Y\2\2\u0158\u015d\7P\2"+ + "\2\u0159\u015d\7M\2\2\u015a\u015d\7N\2\2\u015b\u015d\7O\2\2\u015c\u013a"+ + "\3\2\2\2\u015c\u013f\3\2\2\2\u015c\u0144\3\2\2\2\u015c\u0146\3\2\2\2\u015c"+ + "\u0148\3\2\2\2\u015c\u014a\3\2\2\2\u015c\u014c\3\2\2\2\u015c\u0157\3\2"+ + "\2\2\u015c\u0158\3\2\2\2\u015c\u0159\3\2\2\2\u015c\u015a\3\2\2\2\u015c"+ + "\u015b\3\2\2\2\u015d\u018e\3\2\2\2\u015e\u015f\f\24\2\2\u015f\u0160\t"+ + "\5\2\2\u0160\u018d\5\"\22\25\u0161\u0162\f\23\2\2\u0162\u0163\t\6\2\2"+ + "\u0163\u018d\5\"\22\24\u0164\u0165\f\22\2\2\u0165\u0166\t\7\2\2\u0166"+ + "\u018d\5\"\22\23\u0167\u0168\f\20\2\2\u0168\u0169\t\b\2\2\u0169\u018d"+ + "\5\"\22\21\u016a\u016b\f\17\2\2\u016b\u016c\7&\2\2\u016c\u018d\5\"\22"+ + "\20\u016d\u016e\f\16\2\2\u016e\u016f\7\62\2\2\u016f\u018d\5\"\22\17\u0170"+ + "\u0171\f\r\2\2\u0171\u0172\7\63\2\2\u0172\u018d\5\"\22\16\u0173\u0174"+ + "\f\f\2\2\u0174\u0175\7\64\2\2\u0175\u018d\5\"\22\r\u0176\u0177\f\13\2"+ + "\2\u0177\u0178\7\65\2\2\u0178\u018d\5\"\22\f\u0179\u017a\f\n\2\2\u017a"+ + "\u017b\7\4\2\2\u017b\u018d\5\"\22\n\u017c\u017d\f\t\2\2\u017d\u017e\t"+ + "\t\2\2\u017e\u018d\5\"\22\t\u017f\u0180\f\33\2\2\u0180\u0182\7\6\2\2\u0181"+ + "\u0183\5$\23\2\u0182\u0181\3\2\2\2\u0182\u0183\3\2\2\2\u0183\u0184\3\2"+ + "\2\2\u0184\u018d\7\7\2\2\u0185\u0186\f\32\2\2\u0186\u0187\7\37\2\2\u0187"+ + "\u0188\5\"\22\2\u0188\u0189\7 \2\2\u0189\u018d\3\2\2\2\u018a\u018b\f\27"+ + "\2\2\u018b\u018d\t\2\2\2\u018c\u015e\3\2\2\2\u018c\u0161\3\2\2\2\u018c"+ + "\u0164\3\2\2\2\u018c\u0167\3\2\2\2\u018c\u016a\3\2\2\2\u018c\u016d\3\2"+ + "\2\2\u018c\u0170\3\2\2\2\u018c\u0173\3\2\2\2\u018c\u0176\3\2\2\2\u018c"+ + "\u0179\3\2\2\2\u018c\u017c\3\2\2\2\u018c\u017f\3\2\2\2\u018c\u0185\3\2"+ + "\2\2\u018c\u018a\3\2\2\2\u018d\u0190\3\2\2\2\u018e\u018c\3\2\2\2\u018e"+ + "\u018f\3\2\2\2\u018f#\3\2\2\2\u0190\u018e\3\2\2\2\u0191\u0196\5\"\22\2"+ + "\u0192\u0193\7\n\2\2\u0193\u0195\5\"\22\2\u0194\u0192\3\2\2\2\u0195\u0198"+ + "\3\2\2\2\u0196\u0194\3\2\2\2\u0196\u0197\3\2\2\2\u0197%\3\2\2\2\u0198"+ + "\u0196\3\2\2\2\u0199\u019b\7@\2\2\u019a\u019c\5(\25\2\u019b\u019a\3\2"+ + "\2\2\u019b\u019c\3\2\2\2\u019c\u019d\3\2\2\2\u019d\u019e\7K\2\2\u019e"+ + "\'\3\2\2\2\u019f\u01a0\7\6\2\2\u01a0\u01a5\5*\26\2\u01a1\u01a2\7\n\2\2"+ + "\u01a2\u01a4\5*\26\2\u01a3\u01a1\3\2\2\2\u01a4\u01a7\3\2\2\2\u01a5\u01a3"+ + "\3\2\2\2\u01a5\u01a6\3\2\2\2\u01a6\u01a8\3\2\2\2\u01a7\u01a5\3\2\2\2\u01a8"+ + "\u01a9\7\7\2\2\u01a9)\3\2\2\2\u01aa\u01ab\7A\2\2\u01ab\u01ba\7M\2\2\u01ac"+ + "\u01ad\7B\2\2\u01ad\u01ba\7Y\2\2\u01ae\u01af\7C\2\2\u01af\u01ba\7M\2\2"+ + "\u01b0\u01b1\7D\2\2\u01b1\u01ba\5\"\22\2\u01b2\u01b3\7E\2\2\u01b3\u01ba"+ + "\5\"\22\2\u01b4\u01b7\7F\2\2\u01b5\u01b8\7\17\2\2\u01b6\u01b8\5\"\22\2"+ + "\u01b7\u01b5\3\2\2\2\u01b7\u01b6\3\2\2\2\u01b8\u01ba\3\2\2\2\u01b9\u01aa"+ + "\3\2\2\2\u01b9\u01ac\3\2\2\2\u01b9\u01ae\3\2\2\2\u01b9\u01b0\3\2\2\2\u01b9"+ "\u01b2\3\2\2\2\u01b9\u01b4\3\2\2\2\u01ba+\3\2\2\2\u01bb\u01bd\5.\30\2"+ "\u01bc\u01bb\3\2\2\2\u01bd\u01c0\3\2\2\2\u01be\u01bc\3\2\2\2\u01be\u01bf"+ "\3\2\2\2\u01bf-\3\2\2\2\u01c0\u01be\3\2\2\2\u01c1\u01c5\5\60\31\2\u01c2"+ @@ -4571,7 +4574,7 @@ public class KickCParser extends Parser { "\2\2\u0213\u0211\3\2\2\2\u0213\u0214\3\2\2\2\u02149\3\2\2\2\u0215\u0213"+ "\3\2\2\28DMRW^dkrx}\u0086\u008c\u0093\u00a8\u00aa\u00af\u00b4\u00c1\u00c6"+ "\u00d2\u00e0\u00e6\u00ee\u00f7\u00fe\u0103\u0107\u010c\u0112\u011e\u0128"+ - "\u012f\u0135\u0137\u0142\u0158\u0162\u018c\u018e\u0196\u019b\u01a5\u01b7"+ + "\u012f\u0135\u0137\u0152\u015c\u0182\u018c\u018e\u0196\u019b\u01a5\u01b7"+ "\u01b9\u01be\u01c4\u01ca\u01cd\u01d1\u01d9\u01f3\u0203\u0211\u0213"; public static final ATN _ATN = new ATNDeserializer().deserialize(_serializedATN.toCharArray()); diff --git a/src/main/java/dk/camelot64/kickc/parser/KickCVisitor.java b/src/main/java/dk/camelot64/kickc/parser/KickCVisitor.java index 0faa5687f..df7bbd73a 100644 --- a/src/main/java/dk/camelot64/kickc/parser/KickCVisitor.java +++ b/src/main/java/dk/camelot64/kickc/parser/KickCVisitor.java @@ -1,4 +1,4 @@ -// Generated from C:/c64/kickc/src/main/java/dk/camelot64/kickc/parser\KickC.g4 by ANTLR 4.7 +// Generated from /Users/jespergravgaard/c64/kickc/src/main/java/dk/camelot64/kickc/parser/KickC.g4 by ANTLR 4.7 package dk.camelot64.kickc.parser; import org.antlr.v4.runtime.tree.ParseTreeVisitor; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index 5597dd7d5..09c1368fa 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -1119,6 +1119,14 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { @Override public Object visitExprCall(KickCParser.ExprCallContext ctx) { + + String procedureName; + if(ctx.expr() instanceof KickCParser.ExprIdContext) { + procedureName = ctx.expr().getText(); + } else { + throw new CompileError("Function pointer calls not supported.", new StatementSource(ctx)); + } + List parameters; KickCParser.ParameterListContext parameterList = ctx.parameterList(); if(parameterList != null) { @@ -1128,7 +1136,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { } VariableIntermediate tmpVar = getCurrentScope().addVariableIntermediate(); VariableRef tmpVarRef = tmpVar.getRef(); - sequence.addStatement(new StatementCall(tmpVarRef, ctx.NAME().getText(), parameters, new StatementSource(ctx), ensureUnusedComments(getCommentsSymbol(ctx)))); + sequence.addStatement(new StatementCall(tmpVarRef, procedureName, parameters, new StatementSource(ctx), ensureUnusedComments(getCommentsSymbol(ctx)))); return tmpVarRef; } diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 04dc2d3b0..6f9e7accc 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -32,6 +32,21 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testFunctionPointerNoarg3() throws IOException, URISyntaxException { + compileAndCompare("function-pointer-noarg-3"); + } + + @Test + public void testFunctionPointerNoarg2() throws IOException, URISyntaxException { + compileAndCompare("function-pointer-noarg-2"); + } + + @Test + public void testFunctionPointerNoarg() throws IOException, URISyntaxException { + compileAndCompare("function-pointer-noarg"); + } + @Test public void testLoopBreakContinue() throws IOException, URISyntaxException { compileAndCompare("loop-break-continue"); diff --git a/src/test/kc/function-pointer-noarg-2.kc b/src/test/kc/function-pointer-noarg-2.kc new file mode 100644 index 000000000..0a69dce77 --- /dev/null +++ b/src/test/kc/function-pointer-noarg-2.kc @@ -0,0 +1,25 @@ +// Tests creating and assigning pointers to non-args no-return functions + +void main() { + const byte* SCREEN = $400; + + void()* f; + + for ( byte i: 0..100) { + if((i&1)==0) { + f = &fn1; + } else { + f = &fn2; + } + } +} + +void fn1() { + const byte* BORDERCOL = $d020; + (*BORDERCOL)++; +} + +void fn2() { + const byte* BGCOL = $d021; + (*BGCOL)++; +} \ No newline at end of file diff --git a/src/test/kc/function-pointer-noarg-3.kc b/src/test/kc/function-pointer-noarg-3.kc new file mode 100644 index 000000000..6c2a9a1f9 --- /dev/null +++ b/src/test/kc/function-pointer-noarg-3.kc @@ -0,0 +1,35 @@ +// Tests creating and assigning pointers to non-args no-return functions - plus inline kickasm-based calling + +void main() { + const byte* SCREEN = $400; + + void()* f; + + byte i = 0; + while(true) { + ++i; + if((i&1)==0) { + f = &fn1; + } else { + f = &fn2; + } + kickasm(uses f) {{ + jsr ff + }} + } +} + +kickasm {{ +ff: + jmp (main.f) +}} + +void fn1() { + const byte* BORDERCOL = $d020; + (*BORDERCOL)++; +} + +void fn2() { + const byte* BGCOL = $d021; + (*BGCOL)++; +} \ No newline at end of file diff --git a/src/test/kc/function-pointer-noarg.kc b/src/test/kc/function-pointer-noarg.kc new file mode 100644 index 000000000..5f7452543 --- /dev/null +++ b/src/test/kc/function-pointer-noarg.kc @@ -0,0 +1,24 @@ +// Tests creating pointers to non-args no-return functions + +void main() { + const byte* SCREEN = $400; + + void()* f; + f = &fn1; + SCREEN[0] = <(word)f; + SCREEN[1] = >(word)f; + f = &fn2; + SCREEN[2] = <(word)f; + SCREEN[3] = >(word)f; + +} + +void fn1() { + const byte* BORDERCOL = $d020; + (*BORDERCOL)++; +} + +void fn2() { + const byte* BGCOL = $d021; + (*BGCOL)++; +} \ No newline at end of file diff --git a/src/test/ref/function-pointer-noarg-2.asm b/src/test/ref/function-pointer-noarg-2.asm new file mode 100644 index 000000000..1283305a9 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-2.asm @@ -0,0 +1,15 @@ +// Tests creating and assigning pointers to non-args no-return functions +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +main: { + ldx #0 + b1: + txa + and #1 + cmp #0 + inx + cpx #$65 + bne b1 + rts +} diff --git a/src/test/ref/function-pointer-noarg-2.cfg b/src/test/ref/function-pointer-noarg-2.cfg new file mode 100644 index 000000000..76ffff285 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-2.cfg @@ -0,0 +1,27 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + to:main::@1 +main::@1: scope:[main] from main main::@2 + [5] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@2/(byte) main::i#1 ) + [6] (byte~) main::$0 ← (byte) main::i#2 & (byte/signed byte/word/signed word/dword/signed dword) 1 + [7] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@2 + to:main::@3 +main::@3: scope:[main] from main::@1 + [8] phi() + to:main::@2 +main::@2: scope:[main] from main::@1 main::@3 + [9] (byte) main::i#1 ← ++ (byte) main::i#2 + [10] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $65) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@2 + [11] return + to:@return diff --git a/src/test/ref/function-pointer-noarg-2.log b/src/test/ref/function-pointer-noarg-2.log new file mode 100644 index 000000000..5c41877d8 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-2.log @@ -0,0 +1,437 @@ +Resolved forward reference fn1 to (void()) fn1() +Resolved forward reference fn2 to (void()) fn2() + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@3 +main: scope:[main] from @3 + (byte*) main::SCREEN#0 ← ((byte*)) (word/signed word/dword/signed dword) $400 + (void()*) main::f#0 ← (void()*) 0 + (byte) main::i#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0 + to:main::@1 +main::@1: scope:[main] from main main::@3 + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@3/(byte) main::i#1 ) + (byte~) main::$0 ← (byte) main::i#2 & (byte/signed byte/word/signed word/dword/signed dword) 1 + (bool~) main::$1 ← (byte~) main::$0 == (byte/signed byte/word/signed word/dword/signed dword) 0 + if((bool~) main::$1) goto main::@2 + to:main::@4 +main::@2: scope:[main] from main::@1 + (byte) main::i#4 ← phi( main::@1/(byte) main::i#2 ) + (void()*~) main::$3 ← & (void()) fn1() + (void()*) main::f#1 ← (void()*~) main::$3 + to:main::@3 +main::@4: scope:[main] from main::@1 + (byte) main::i#5 ← phi( main::@1/(byte) main::i#2 ) + (void()*~) main::$2 ← & (void()) fn2() + (void()*) main::f#2 ← (void()*~) main::$2 + to:main::@3 +main::@3: scope:[main] from main::@2 main::@4 + (byte) main::i#3 ← phi( main::@2/(byte) main::i#4 main::@4/(byte) main::i#5 ) + (byte) main::i#1 ← (byte) main::i#3 + rangenext(0,$64) + (bool~) main::$4 ← (byte) main::i#1 != rangelast(0,$64) + if((bool~) main::$4) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@3 + return + to:@return +fn1: scope:[fn1] from + (byte*) fn1::BORDERCOL#0 ← ((byte*)) (word/dword/signed dword) $d020 + *((byte*) fn1::BORDERCOL#0) ← ++ *((byte*) fn1::BORDERCOL#0) + to:fn1::@return +fn1::@return: scope:[fn1] from fn1 + return + to:@return +fn2: scope:[fn2] from + (byte*) fn2::BGCOL#0 ← ((byte*)) (word/dword/signed dword) $d021 + *((byte*) fn2::BGCOL#0) ← ++ *((byte*) fn2::BGCOL#0) + to:fn2::@return +fn2::@return: scope:[fn2] from fn2 + return + to:@return +@3: scope:[] from @begin + call main + to:@4 +@4: scope:[] from @3 + to:@end +@end: scope:[] from @4 + +SYMBOL TABLE SSA +(label) @3 +(label) @4 +(label) @begin +(label) @end +(void()) fn1() +(label) fn1::@return +(byte*) fn1::BORDERCOL +(byte*) fn1::BORDERCOL#0 +(void()) fn2() +(label) fn2::@return +(byte*) fn2::BGCOL +(byte*) fn2::BGCOL#0 +(void()) main() +(byte~) main::$0 +(bool~) main::$1 +(void()*~) main::$2 +(void()*~) main::$3 +(bool~) main::$4 +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@return +(byte*) main::SCREEN +(byte*) main::SCREEN#0 +(void()*) main::f +(void()*) main::f#0 +(void()*) main::f#1 +(void()*) main::f#2 +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 +(byte) main::i#4 +(byte) main::i#5 + +Culled Empty Block (label) @4 +Successful SSA optimization Pass2CullEmptyBlocks +Alias (byte) main::i#2 = (byte) main::i#4 (byte) main::i#5 +Alias (void()*) main::f#1 = (void()*~) main::$3 +Alias (void()*) main::f#2 = (void()*~) main::$2 +Successful SSA optimization Pass2AliasElimination +Alias (byte) main::i#2 = (byte) main::i#3 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$1 [6] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@2 +Simple Condition (bool~) main::$4 [16] if((byte) main::i#1!=rangelast(0,$64)) goto main::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte*) main::SCREEN#0 = ((byte*))$400 +Constant (const void()*) main::f#0 = 0 +Constant (const byte) main::i#0 = 0 +Constant (const void()*) main::f#1 = &fn1 +Constant (const void()*) main::f#2 = &fn2 +Constant (const byte*) fn1::BORDERCOL#0 = ((byte*))$d020 +Constant (const byte*) fn2::BGCOL#0 = ((byte*))$d021 +Successful SSA optimization Pass2ConstantIdentification +Successful SSA optimization PassNEliminateUnusedVars +Removing unused procedure fn1 +Removing unused procedure block fn1 +Removing unused procedure block fn1::@return +Removing unused procedure fn2 +Removing unused procedure block fn2 +Removing unused procedure block fn2::@return +Successful SSA optimization Pass2EliminateUnusedBlocks +Resolved ranged next value main::i#1 ← ++ main::i#2 to ++ +Resolved ranged comparison value if(main::i#1!=rangelast(0,$64)) goto main::@1 to (byte/signed byte/word/signed word/dword/signed dword) $65 +Culled Empty Block (label) main::@2 +Successful SSA optimization Pass2CullEmptyBlocks +Inlining constant with var siblings (const byte) main::i#0 +Constant inlined main::i#0 = (byte/signed byte/word/signed word/dword/signed dword) 0 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting main::@7(between main::@3 and main::@1) +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @3 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@4 +CALL GRAPH +Calls in [] to main:2 + +Created 1 initial phi equivalence classes +Coalesced [12] main::i#6 ← main::i#1 +Coalesced down to 1 phi equivalence classes +Culled Empty Block (label) main::@7 +Renumbering block @3 to @1 +Renumbering block main::@3 to main::@2 +Renumbering block main::@4 to main::@3 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@3 + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + to:main::@1 +main::@1: scope:[main] from main main::@2 + [5] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@2/(byte) main::i#1 ) + [6] (byte~) main::$0 ← (byte) main::i#2 & (byte/signed byte/word/signed word/dword/signed dword) 1 + [7] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@2 + to:main::@3 +main::@3: scope:[main] from main::@1 + [8] phi() + to:main::@2 +main::@2: scope:[main] from main::@1 main::@3 + [9] (byte) main::i#1 ← ++ (byte) main::i#2 + [10] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $65) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@2 + [11] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(void()) main() +(byte~) main::$0 22.0 +(byte*) main::SCREEN +(void()*) main::f +(byte) main::i +(byte) main::i#1 16.5 +(byte) main::i#2 8.25 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +Added variable main::$0 to zero page equivalence class [ main::$0 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ main::$0 ] +Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Allocated zp ZP_BYTE:3 [ main::$0 ] + +INITIAL ASM +//SEG0 File Comments +// Tests creating and assigning pointers to non-args no-return functions +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels +//SEG3 @begin +bbegin: +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG5 @1 +b1: +//SEG6 [2] call main +//SEG7 [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main +//SEG8 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG9 @end +bend: +//SEG10 main +main: { + .label _0 = 3 + .label i = 2 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta i + jmp b1 + //SEG13 [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + //SEG14 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp b1 + //SEG15 main::@1 + b1: + //SEG16 [6] (byte~) main::$0 ← (byte) main::i#2 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuz1=vbuz2_band_vbuc1 + lda #1 + and i + sta _0 + //SEG17 [7] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@2 -- vbuz1_eq_0_then_la1 + lda _0 + cmp #0 + beq b2 + //SEG18 [8] phi from main::@1 to main::@3 [phi:main::@1->main::@3] + b3_from_b1: + jmp b3 + //SEG19 main::@3 + b3: + jmp b2 + //SEG20 main::@2 + b2: + //SEG21 [9] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc i + //SEG22 [10] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $65) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #$65 + cmp i + bne b1_from_b2 + jmp breturn + //SEG23 main::@return + breturn: + //SEG24 [11] return + rts +} + +REGISTER UPLIFT POTENTIAL REGISTERS +Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:3 [ main::$0 ] : zp ZP_BYTE:3 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 24.75: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] 22: zp ZP_BYTE:3 [ main::$0 ] +Uplift Scope [] + +Uplifting [main] best 338 combination reg byte x [ main::i#2 main::i#1 ] reg byte a [ main::$0 ] +Uplifting [] best 338 combination + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 File Comments +// Tests creating and assigning pointers to non-args no-return functions +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels +//SEG3 @begin +bbegin: +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG5 @1 +b1: +//SEG6 [2] call main +//SEG7 [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main +//SEG8 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG9 @end +bend: +//SEG10 main +main: { + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp b1 + //SEG13 [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + //SEG14 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp b1 + //SEG15 main::@1 + b1: + //SEG16 [6] (byte~) main::$0 ← (byte) main::i#2 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuaa=vbuxx_band_vbuc1 + txa + and #1 + //SEG17 [7] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@2 -- vbuaa_eq_0_then_la1 + cmp #0 + beq b2 + //SEG18 [8] phi from main::@1 to main::@3 [phi:main::@1->main::@3] + b3_from_b1: + jmp b3 + //SEG19 main::@3 + b3: + jmp b2 + //SEG20 main::@2 + b2: + //SEG21 [9] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + //SEG22 [10] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $65) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #$65 + bne b1_from_b2 + jmp breturn + //SEG23 main::@return + breturn: + //SEG24 [11] return + rts +} + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp b3 +Removing instruction jmp b2 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label b1_from_b2 with b1 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b1_from_b2: +Removing instruction b3_from_b1: +Removing instruction b3: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction jmp b1 +Removing instruction beq b2 +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction bbegin: +Removing instruction b2: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(void()) main() +(byte~) main::$0 reg byte a 22.0 +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte*) main::SCREEN +(void()*) main::f +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#2 reg byte x 8.25 + +reg byte x [ main::i#2 main::i#1 ] +reg byte a [ main::$0 ] + + +FINAL ASSEMBLER +Score: 151 + +//SEG0 File Comments +// Tests creating and assigning pointers to non-args no-return functions +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG2 Global Constants & labels +//SEG3 @begin +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +//SEG5 @1 +//SEG6 [2] call main +//SEG7 [4] phi from @1 to main [phi:@1->main] +//SEG8 [3] phi from @1 to @end [phi:@1->@end] +//SEG9 @end +//SEG10 main +main: { + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + //SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + //SEG13 [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + //SEG14 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + //SEG15 main::@1 + b1: + //SEG16 [6] (byte~) main::$0 ← (byte) main::i#2 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuaa=vbuxx_band_vbuc1 + txa + and #1 + //SEG17 [7] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@2 -- vbuaa_eq_0_then_la1 + cmp #0 + //SEG18 [8] phi from main::@1 to main::@3 [phi:main::@1->main::@3] + //SEG19 main::@3 + //SEG20 main::@2 + //SEG21 [9] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + //SEG22 [10] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $65) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #$65 + bne b1 + //SEG23 main::@return + //SEG24 [11] return + rts +} + diff --git a/src/test/ref/function-pointer-noarg-2.sym b/src/test/ref/function-pointer-noarg-2.sym new file mode 100644 index 000000000..578bd5a5e --- /dev/null +++ b/src/test/ref/function-pointer-noarg-2.sym @@ -0,0 +1,17 @@ +(label) @1 +(label) @begin +(label) @end +(void()) main() +(byte~) main::$0 reg byte a 22.0 +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte*) main::SCREEN +(void()*) main::f +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#2 reg byte x 8.25 + +reg byte x [ main::i#2 main::i#1 ] +reg byte a [ main::$0 ] diff --git a/src/test/ref/function-pointer-noarg-3.asm b/src/test/ref/function-pointer-noarg-3.asm new file mode 100644 index 000000000..a5efe51f9 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-3.asm @@ -0,0 +1,41 @@ +// Tests creating and assigning pointers to non-args no-return functions - plus inline kickasm-based calling +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + ff: + jmp (main.f) + +main: { + .label f = 2 + ldx #0 + b2: + inx + txa + and #1 + cmp #0 + beq b1 + lda #fn2 + sta f+1 + jmp b3 + b1: + lda #fn1 + sta f+1 + b3: + jsr ff + + jmp b2 +} +fn2: { + .label BGCOL = $d021 + inc BGCOL + rts +} +fn1: { + .label BORDERCOL = $d020 + inc BORDERCOL + rts +} diff --git a/src/test/ref/function-pointer-noarg-3.cfg b/src/test/ref/function-pointer-noarg-3.cfg new file mode 100644 index 000000000..b57f852d4 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-3.cfg @@ -0,0 +1,45 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + kickasm() {{ ff: + jmp (main.f) + }} + to:@2 +@2: scope:[] from @1 + [2] phi() + [3] call main + to:@end +@end: scope:[] from @2 + [4] phi() +main: scope:[main] from @2 + [5] phi() + to:main::@1 +main::@1: scope:[main] from main main::@3 + [6] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@3/(byte) main::i#1 ) + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] (byte) main::i#1 ← ++ (byte) main::i#2 + [8] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 + [9] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@3 + to:main::@4 +main::@4: scope:[main] from main::@2 + [10] phi() + to:main::@3 +main::@3: scope:[main] from main::@2 main::@4 + [11] (void()*) main::f#3 ← phi( main::@2/&(void()) fn1() main::@4/&(void()) fn2() ) + kickasm( uses main::f#3) {{ jsr ff + }} + to:main::@1 +fn2: scope:[fn2] from + [13] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) + to:fn2::@return +fn2::@return: scope:[fn2] from fn2 + [14] return + to:@return +fn1: scope:[fn1] from + [15] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) + to:fn1::@return +fn1::@return: scope:[fn1] from fn1 + [16] return + to:@return diff --git a/src/test/ref/function-pointer-noarg-3.log b/src/test/ref/function-pointer-noarg-3.log new file mode 100644 index 000000000..fdf73efa3 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-3.log @@ -0,0 +1,619 @@ +Resolved forward reference fn1 to (void()) fn1() +Resolved forward reference fn2 to (void()) fn2() + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@1 +main: scope:[main] from @3 + (byte*) main::SCREEN#0 ← ((byte*)) (word/signed word/dword/signed dword) $400 + (void()*) main::f#0 ← (void()*) 0 + (byte) main::i#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0 + to:main::@1 +main::@1: scope:[main] from main main::@5 + (byte) main::i#3 ← phi( main/(byte) main::i#0 main::@5/(byte) main::i#4 ) + if(true) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + (byte) main::i#2 ← phi( main::@1/(byte) main::i#3 ) + (byte) main::i#1 ← ++ (byte) main::i#2 + (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 + (bool~) main::$1 ← (byte~) main::$0 == (byte/signed byte/word/signed word/dword/signed dword) 0 + if((bool~) main::$1) goto main::@4 + to:main::@8 +main::@4: scope:[main] from main::@2 + (byte) main::i#5 ← phi( main::@2/(byte) main::i#1 ) + (void()*~) main::$3 ← & (void()) fn1() + (void()*) main::f#1 ← (void()*~) main::$3 + to:main::@5 +main::@8: scope:[main] from main::@2 + (byte) main::i#6 ← phi( main::@2/(byte) main::i#1 ) + (void()*~) main::$2 ← & (void()) fn2() + (void()*) main::f#2 ← (void()*~) main::$2 + to:main::@5 +main::@5: scope:[main] from main::@4 main::@8 + (byte) main::i#4 ← phi( main::@4/(byte) main::i#5 main::@8/(byte) main::i#6 ) + (void()*) main::f#3 ← phi( main::@4/(void()*) main::f#1 main::@8/(void()*) main::f#2 ) + kickasm( uses main::f#3) {{ jsr ff + }} + to:main::@1 +main::@return: scope:[main] from main::@1 + return + to:@return +@1: scope:[] from @begin + kickasm() {{ ff: + jmp (main.f) + }} + to:@3 +fn1: scope:[fn1] from + (byte*) fn1::BORDERCOL#0 ← ((byte*)) (word/dword/signed dword) $d020 + *((byte*) fn1::BORDERCOL#0) ← ++ *((byte*) fn1::BORDERCOL#0) + to:fn1::@return +fn1::@return: scope:[fn1] from fn1 + return + to:@return +fn2: scope:[fn2] from + (byte*) fn2::BGCOL#0 ← ((byte*)) (word/dword/signed dword) $d021 + *((byte*) fn2::BGCOL#0) ← ++ *((byte*) fn2::BGCOL#0) + to:fn2::@return +fn2::@return: scope:[fn2] from fn2 + return + to:@return +@3: scope:[] from @1 + call main + to:@4 +@4: scope:[] from @3 + to:@end +@end: scope:[] from @4 + +SYMBOL TABLE SSA +(label) @1 +(label) @3 +(label) @4 +(label) @begin +(label) @end +(void()) fn1() +(label) fn1::@return +(byte*) fn1::BORDERCOL +(byte*) fn1::BORDERCOL#0 +(void()) fn2() +(label) fn2::@return +(byte*) fn2::BGCOL +(byte*) fn2::BGCOL#0 +(void()) main() +(byte~) main::$0 +(bool~) main::$1 +(void()*~) main::$2 +(void()*~) main::$3 +(label) main::@1 +(label) main::@2 +(label) main::@4 +(label) main::@5 +(label) main::@8 +(label) main::@return +(byte*) main::SCREEN +(byte*) main::SCREEN#0 +(void()*) main::f +(void()*) main::f#0 +(void()*) main::f#1 +(void()*) main::f#2 +(void()*) main::f#3 +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 +(byte) main::i#4 +(byte) main::i#5 +(byte) main::i#6 + +Culled Empty Block (label) @4 +Successful SSA optimization Pass2CullEmptyBlocks +Alias (byte) main::i#2 = (byte) main::i#3 +Alias (byte) main::i#1 = (byte) main::i#5 (byte) main::i#6 +Alias (void()*) main::f#1 = (void()*~) main::$3 +Alias (void()*) main::f#2 = (void()*~) main::$2 +Successful SSA optimization Pass2AliasElimination +Alias (byte) main::i#1 = (byte) main::i#4 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$1 [9] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@4 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte*) main::SCREEN#0 = ((byte*))$400 +Constant (const void()*) main::f#0 = 0 +Constant (const byte) main::i#0 = 0 +Constant (const void()*) main::f#1 = &fn1 +Constant (const void()*) main::f#2 = &fn2 +Constant (const byte*) fn1::BORDERCOL#0 = ((byte*))$d020 +Constant (const byte*) fn2::BGCOL#0 = ((byte*))$d021 +Successful SSA optimization Pass2ConstantIdentification +if() condition always true - replacing block destination [1] if(true) goto main::@2 +Successful SSA optimization Pass2ConstantIfs +Successful SSA optimization PassNEliminateUnusedVars +Removing unused block main::@return +Successful SSA optimization Pass2EliminateUnusedBlocks +Culled Empty Block (label) main::@4 +Successful SSA optimization Pass2CullEmptyBlocks +Inlining constant with var siblings (const byte) main::i#0 +Inlining constant with var siblings (const void()*) main::f#1 +Inlining constant with var siblings (const void()*) main::f#2 +Constant inlined main::i#0 = (byte/signed byte/word/signed word/dword/signed dword) 0 +Constant inlined main::f#2 = &(void()) fn2() +Constant inlined main::f#1 = &(void()) fn1() +Successful SSA optimization Pass2ConstantInlining +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @3 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@8 +CALL GRAPH +Calls in [] to main:3 + +Created 2 initial phi equivalence classes +Coalesced [13] main::i#7 ← main::i#1 +Coalesced down to 2 phi equivalence classes +Renumbering block @3 to @2 +Renumbering block main::@5 to main::@3 +Renumbering block main::@8 to main::@4 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@4 + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + kickasm() {{ ff: + jmp (main.f) + }} + to:@2 +@2: scope:[] from @1 + [2] phi() + [3] call main + to:@end +@end: scope:[] from @2 + [4] phi() +main: scope:[main] from @2 + [5] phi() + to:main::@1 +main::@1: scope:[main] from main main::@3 + [6] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@3/(byte) main::i#1 ) + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] (byte) main::i#1 ← ++ (byte) main::i#2 + [8] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 + [9] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@3 + to:main::@4 +main::@4: scope:[main] from main::@2 + [10] phi() + to:main::@3 +main::@3: scope:[main] from main::@2 main::@4 + [11] (void()*) main::f#3 ← phi( main::@2/&(void()) fn1() main::@4/&(void()) fn2() ) + kickasm( uses main::f#3) {{ jsr ff + }} + to:main::@1 +fn2: scope:[fn2] from + [13] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) + to:fn2::@return +fn2::@return: scope:[fn2] from fn2 + [14] return + to:@return +fn1: scope:[fn1] from + [15] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) + to:fn1::@return +fn1::@return: scope:[fn1] from fn1 + [16] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(void()) fn1() +(byte*) fn1::BORDERCOL +(void()) fn2() +(byte*) fn2::BGCOL +(void()) main() +(byte~) main::$0 22.0 +(byte*) main::SCREEN +(void()*) main::f +(void()*) main::f#3 +(byte) main::i +(byte) main::i#1 5.5 +(byte) main::i#2 22.0 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +[ main::f#3 ] +Added variable main::$0 to zero page equivalence class [ main::$0 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ main::f#3 ] +[ main::$0 ] +Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Allocated zp ZP_WORD:3 [ main::f#3 ] +Allocated zp ZP_BYTE:5 [ main::$0 ] + +INITIAL ASM +//SEG0 File Comments +// Tests creating and assigning pointers to non-args no-return functions - plus inline kickasm-based calling +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels +//SEG3 @begin +bbegin: + jmp b1 +//SEG4 @1 +b1: +//SEG5 kickasm() {{ ff: jmp (main.f) }} + ff: + jmp (main.f) + +//SEG6 [2] phi from @1 to @2 [phi:@1->@2] +b2_from_b1: + jmp b2 +//SEG7 @2 +b2: +//SEG8 [3] call main +//SEG9 [5] phi from @2 to main [phi:@2->main] +main_from_b2: + jsr main +//SEG10 [4] phi from @2 to @end [phi:@2->@end] +bend_from_b2: + jmp bend +//SEG11 @end +bend: +//SEG12 main +main: { + .label _0 = 5 + .label i = 2 + .label f = 3 + //SEG13 [6] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG14 [6] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta i + jmp b1 + //SEG15 main::@1 + b1: + jmp b2 + //SEG16 main::@2 + b2: + //SEG17 [7] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc i + //SEG18 [8] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuz1=vbuz2_band_vbuc1 + lda #1 + and i + sta _0 + //SEG19 [9] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@3 -- vbuz1_eq_0_then_la1 + lda _0 + cmp #0 + beq b3_from_b2 + //SEG20 [10] phi from main::@2 to main::@4 [phi:main::@2->main::@4] + b4_from_b2: + jmp b4 + //SEG21 main::@4 + b4: + //SEG22 [11] phi from main::@4 to main::@3 [phi:main::@4->main::@3] + b3_from_b4: + //SEG23 [11] phi (void()*) main::f#3 = &(void()) fn2() [phi:main::@4->main::@3#0] -- pprz1=pprc1 + lda #fn2 + sta f+1 + jmp b3 + //SEG24 [11] phi from main::@2 to main::@3 [phi:main::@2->main::@3] + b3_from_b2: + //SEG25 [11] phi (void()*) main::f#3 = &(void()) fn1() [phi:main::@2->main::@3#0] -- pprz1=pprc1 + lda #fn1 + sta f+1 + jmp b3 + //SEG26 main::@3 + b3: + //SEG27 kickasm( uses main::f#3) {{ jsr ff }} + jsr ff + + //SEG28 [6] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + b1_from_b3: + //SEG29 [6] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy + jmp b1 +} +//SEG30 fn2 +fn2: { + .label BGCOL = $d021 + //SEG31 [13] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BGCOL + jmp breturn + //SEG32 fn2::@return + breturn: + //SEG33 [14] return + rts +} +//SEG34 fn1 +fn1: { + .label BORDERCOL = $d020 + //SEG35 [15] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BORDERCOL + jmp breturn + //SEG36 fn1::@return + breturn: + //SEG37 [16] return + rts +} + +REGISTER UPLIFT POTENTIAL REGISTERS +Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_WORD:3 [ main::f#3 ] : zp ZP_WORD:3 , +Potential registers zp ZP_BYTE:5 [ main::$0 ] : zp ZP_BYTE:5 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 27.5: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] 22: zp ZP_BYTE:5 [ main::$0 ] 0: zp ZP_WORD:3 [ main::f#3 ] +Uplift Scope [fn1] +Uplift Scope [fn2] +Uplift Scope [] + +Uplifting [main] best 3393 combination reg byte x [ main::i#2 main::i#1 ] reg byte a [ main::$0 ] zp ZP_WORD:3 [ main::f#3 ] +Uplifting [fn1] best 3393 combination +Uplifting [fn2] best 3393 combination +Uplifting [] best 3393 combination +Allocated (was zp ZP_WORD:3) zp ZP_WORD:2 [ main::f#3 ] + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 File Comments +// Tests creating and assigning pointers to non-args no-return functions - plus inline kickasm-based calling +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels +//SEG3 @begin +bbegin: + jmp b1 +//SEG4 @1 +b1: +//SEG5 kickasm() {{ ff: jmp (main.f) }} + ff: + jmp (main.f) + +//SEG6 [2] phi from @1 to @2 [phi:@1->@2] +b2_from_b1: + jmp b2 +//SEG7 @2 +b2: +//SEG8 [3] call main +//SEG9 [5] phi from @2 to main [phi:@2->main] +main_from_b2: + jsr main +//SEG10 [4] phi from @2 to @end [phi:@2->@end] +bend_from_b2: + jmp bend +//SEG11 @end +bend: +//SEG12 main +main: { + .label f = 2 + //SEG13 [6] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG14 [6] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp b1 + //SEG15 main::@1 + b1: + jmp b2 + //SEG16 main::@2 + b2: + //SEG17 [7] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + //SEG18 [8] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuaa=vbuxx_band_vbuc1 + txa + and #1 + //SEG19 [9] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@3 -- vbuaa_eq_0_then_la1 + cmp #0 + beq b3_from_b2 + //SEG20 [10] phi from main::@2 to main::@4 [phi:main::@2->main::@4] + b4_from_b2: + jmp b4 + //SEG21 main::@4 + b4: + //SEG22 [11] phi from main::@4 to main::@3 [phi:main::@4->main::@3] + b3_from_b4: + //SEG23 [11] phi (void()*) main::f#3 = &(void()) fn2() [phi:main::@4->main::@3#0] -- pprz1=pprc1 + lda #fn2 + sta f+1 + jmp b3 + //SEG24 [11] phi from main::@2 to main::@3 [phi:main::@2->main::@3] + b3_from_b2: + //SEG25 [11] phi (void()*) main::f#3 = &(void()) fn1() [phi:main::@2->main::@3#0] -- pprz1=pprc1 + lda #fn1 + sta f+1 + jmp b3 + //SEG26 main::@3 + b3: + //SEG27 kickasm( uses main::f#3) {{ jsr ff }} + jsr ff + + //SEG28 [6] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + b1_from_b3: + //SEG29 [6] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy + jmp b1 +} +//SEG30 fn2 +fn2: { + .label BGCOL = $d021 + //SEG31 [13] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BGCOL + jmp breturn + //SEG32 fn2::@return + breturn: + //SEG33 [14] return + rts +} +//SEG34 fn1 +fn1: { + .label BORDERCOL = $d020 + //SEG35 [15] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BORDERCOL + jmp breturn + //SEG36 fn1::@return + breturn: + //SEG37 [16] return + rts +} + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp b2 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp b2 +Removing instruction jmp b4 +Removing instruction jmp b3 +Removing instruction jmp breturn +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label b1 with b2 +Removing instruction b1: +Removing instruction b2_from_b1: +Removing instruction main_from_b2: +Removing instruction bend_from_b2: +Removing instruction b1: +Removing instruction b4_from_b2: +Removing instruction b3_from_b4: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction b2: +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction b4: +Removing instruction b1_from_b3: +Removing instruction breturn: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Relabelling long label b3_from_b2 to b1 +Succesful ASM optimization Pass5RelabelLongLabels +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @2 +(label) @begin +(label) @end +(void()) fn1() +(label) fn1::@return +(byte*) fn1::BORDERCOL +(const byte*) fn1::BORDERCOL#0 BORDERCOL = ((byte*))(word/dword/signed dword) $d020 +(void()) fn2() +(label) fn2::@return +(byte*) fn2::BGCOL +(const byte*) fn2::BGCOL#0 BGCOL = ((byte*))(word/dword/signed dword) $d021 +(void()) main() +(byte~) main::$0 reg byte a 22.0 +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(byte*) main::SCREEN +(void()*) main::f +(void()*) main::f#3 f zp ZP_WORD:2 +(byte) main::i +(byte) main::i#1 reg byte x 5.5 +(byte) main::i#2 reg byte x 22.0 + +reg byte x [ main::i#2 main::i#1 ] +zp ZP_WORD:2 [ main::f#3 ] +reg byte a [ main::$0 ] + + +FINAL ASSEMBLER +Score: 3225 + +//SEG0 File Comments +// Tests creating and assigning pointers to non-args no-return functions - plus inline kickasm-based calling +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG2 Global Constants & labels +//SEG3 @begin +//SEG4 @1 +//SEG5 kickasm() {{ ff: jmp (main.f) }} + ff: + jmp (main.f) + +//SEG6 [2] phi from @1 to @2 [phi:@1->@2] +//SEG7 @2 +//SEG8 [3] call main +//SEG9 [5] phi from @2 to main [phi:@2->main] +//SEG10 [4] phi from @2 to @end [phi:@2->@end] +//SEG11 @end +//SEG12 main +main: { + .label f = 2 + //SEG13 [6] phi from main to main::@1 [phi:main->main::@1] + //SEG14 [6] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + //SEG15 main::@1 + //SEG16 main::@2 + b2: + //SEG17 [7] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + //SEG18 [8] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuaa=vbuxx_band_vbuc1 + txa + and #1 + //SEG19 [9] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@3 -- vbuaa_eq_0_then_la1 + cmp #0 + beq b1 + //SEG20 [10] phi from main::@2 to main::@4 [phi:main::@2->main::@4] + //SEG21 main::@4 + //SEG22 [11] phi from main::@4 to main::@3 [phi:main::@4->main::@3] + //SEG23 [11] phi (void()*) main::f#3 = &(void()) fn2() [phi:main::@4->main::@3#0] -- pprz1=pprc1 + lda #fn2 + sta f+1 + jmp b3 + //SEG24 [11] phi from main::@2 to main::@3 [phi:main::@2->main::@3] + b1: + //SEG25 [11] phi (void()*) main::f#3 = &(void()) fn1() [phi:main::@2->main::@3#0] -- pprz1=pprc1 + lda #fn1 + sta f+1 + //SEG26 main::@3 + b3: + //SEG27 kickasm( uses main::f#3) {{ jsr ff }} + jsr ff + + //SEG28 [6] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + //SEG29 [6] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy + jmp b2 +} +//SEG30 fn2 +fn2: { + .label BGCOL = $d021 + //SEG31 [13] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BGCOL + //SEG32 fn2::@return + //SEG33 [14] return + rts +} +//SEG34 fn1 +fn1: { + .label BORDERCOL = $d020 + //SEG35 [15] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BORDERCOL + //SEG36 fn1::@return + //SEG37 [16] return + rts +} + diff --git a/src/test/ref/function-pointer-noarg-3.sym b/src/test/ref/function-pointer-noarg-3.sym new file mode 100644 index 000000000..3fbb74ea1 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-3.sym @@ -0,0 +1,28 @@ +(label) @1 +(label) @2 +(label) @begin +(label) @end +(void()) fn1() +(label) fn1::@return +(byte*) fn1::BORDERCOL +(const byte*) fn1::BORDERCOL#0 BORDERCOL = ((byte*))(word/dword/signed dword) $d020 +(void()) fn2() +(label) fn2::@return +(byte*) fn2::BGCOL +(const byte*) fn2::BGCOL#0 BGCOL = ((byte*))(word/dword/signed dword) $d021 +(void()) main() +(byte~) main::$0 reg byte a 22.0 +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(byte*) main::SCREEN +(void()*) main::f +(void()*) main::f#3 f zp ZP_WORD:2 +(byte) main::i +(byte) main::i#1 reg byte x 5.5 +(byte) main::i#2 reg byte x 22.0 + +reg byte x [ main::i#2 main::i#1 ] +zp ZP_WORD:2 [ main::f#3 ] +reg byte a [ main::$0 ] diff --git a/src/test/ref/function-pointer-noarg.asm b/src/test/ref/function-pointer-noarg.asm new file mode 100644 index 000000000..061e7170e --- /dev/null +++ b/src/test/ref/function-pointer-noarg.asm @@ -0,0 +1,26 @@ +// Tests creating pointers to non-args no-return functions +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +main: { + .label SCREEN = $400 + lda #fn1 + sta SCREEN+1 + lda #fn2 + sta SCREEN+3 + rts +} +fn2: { + .label BGCOL = $d021 + inc BGCOL + rts +} +fn1: { + .label BORDERCOL = $d020 + inc BORDERCOL + rts +} diff --git a/src/test/ref/function-pointer-noarg.cfg b/src/test/ref/function-pointer-noarg.cfg new file mode 100644 index 000000000..e39c54a88 --- /dev/null +++ b/src/test/ref/function-pointer-noarg.cfg @@ -0,0 +1,30 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] *((const byte*) main::SCREEN#0) ← <((word))&(void()) fn1() + [5] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 1) ← >((word))&(void()) fn1() + [6] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 2) ← <((word))&(void()) fn2() + [7] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 3) ← >((word))&(void()) fn2() + to:main::@return +main::@return: scope:[main] from main + [8] return + to:@return +fn2: scope:[fn2] from + [9] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) + to:fn2::@return +fn2::@return: scope:[fn2] from fn2 + [10] return + to:@return +fn1: scope:[fn1] from + [11] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) + to:fn1::@return +fn1::@return: scope:[fn1] from fn1 + [12] return + to:@return diff --git a/src/test/ref/function-pointer-noarg.log b/src/test/ref/function-pointer-noarg.log new file mode 100644 index 000000000..68383764e --- /dev/null +++ b/src/test/ref/function-pointer-noarg.log @@ -0,0 +1,427 @@ +Resolved forward reference fn1 to (void()) fn1() +Resolved forward reference fn2 to (void()) fn2() + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@3 +main: scope:[main] from @3 + (byte*) main::SCREEN#0 ← ((byte*)) (word/signed word/dword/signed dword) $400 + (void()*) main::f#0 ← (void()*) 0 + (void()*~) main::$0 ← & (void()) fn1() + (void()*) main::f#1 ← (void()*~) main::$0 + (word~) main::$1 ← ((word)) (void()*) main::f#1 + (byte~) main::$2 ← < (word~) main::$1 + *((byte*) main::SCREEN#0 + (byte/signed byte/word/signed word/dword/signed dword) 0) ← (byte~) main::$2 + (word~) main::$3 ← ((word)) (void()*) main::f#1 + (byte~) main::$4 ← > (word~) main::$3 + *((byte*) main::SCREEN#0 + (byte/signed byte/word/signed word/dword/signed dword) 1) ← (byte~) main::$4 + (void()*~) main::$5 ← & (void()) fn2() + (void()*) main::f#2 ← (void()*~) main::$5 + (word~) main::$6 ← ((word)) (void()*) main::f#2 + (byte~) main::$7 ← < (word~) main::$6 + *((byte*) main::SCREEN#0 + (byte/signed byte/word/signed word/dword/signed dword) 2) ← (byte~) main::$7 + (word~) main::$8 ← ((word)) (void()*) main::f#2 + (byte~) main::$9 ← > (word~) main::$8 + *((byte*) main::SCREEN#0 + (byte/signed byte/word/signed word/dword/signed dword) 3) ← (byte~) main::$9 + to:main::@return +main::@return: scope:[main] from main + return + to:@return +fn1: scope:[fn1] from + (byte*) fn1::BORDERCOL#0 ← ((byte*)) (word/dword/signed dword) $d020 + *((byte*) fn1::BORDERCOL#0) ← ++ *((byte*) fn1::BORDERCOL#0) + to:fn1::@return +fn1::@return: scope:[fn1] from fn1 + return + to:@return +fn2: scope:[fn2] from + (byte*) fn2::BGCOL#0 ← ((byte*)) (word/dword/signed dword) $d021 + *((byte*) fn2::BGCOL#0) ← ++ *((byte*) fn2::BGCOL#0) + to:fn2::@return +fn2::@return: scope:[fn2] from fn2 + return + to:@return +@3: scope:[] from @begin + call main + to:@4 +@4: scope:[] from @3 + to:@end +@end: scope:[] from @4 + +SYMBOL TABLE SSA +(label) @3 +(label) @4 +(label) @begin +(label) @end +(void()) fn1() +(label) fn1::@return +(byte*) fn1::BORDERCOL +(byte*) fn1::BORDERCOL#0 +(void()) fn2() +(label) fn2::@return +(byte*) fn2::BGCOL +(byte*) fn2::BGCOL#0 +(void()) main() +(void()*~) main::$0 +(word~) main::$1 +(byte~) main::$2 +(word~) main::$3 +(byte~) main::$4 +(void()*~) main::$5 +(word~) main::$6 +(byte~) main::$7 +(word~) main::$8 +(byte~) main::$9 +(label) main::@return +(byte*) main::SCREEN +(byte*) main::SCREEN#0 +(void()*) main::f +(void()*) main::f#0 +(void()*) main::f#1 +(void()*) main::f#2 + +Culled Empty Block (label) @4 +Successful SSA optimization Pass2CullEmptyBlocks +Alias (void()*) main::f#1 = (void()*~) main::$0 +Alias (void()*) main::f#2 = (void()*~) main::$5 +Successful SSA optimization Pass2AliasElimination +Constant (const byte*) main::SCREEN#0 = ((byte*))$400 +Constant (const void()*) main::f#0 = 0 +Constant (const void()*) main::f#1 = &fn1 +Constant (const void()*) main::f#2 = &fn2 +Constant (const byte*) fn1::BORDERCOL#0 = ((byte*))$d020 +Constant (const byte*) fn2::BGCOL#0 = ((byte*))$d021 +Successful SSA optimization Pass2ConstantIdentification +Constant (const word) main::$1 = ((word))main::f#1 +Constant (const word) main::$3 = ((word))main::f#1 +Constant (const word) main::$6 = ((word))main::f#2 +Constant (const word) main::$8 = ((word))main::f#2 +Successful SSA optimization Pass2ConstantIdentification +Constant (const byte) main::$2 = main::$3 +Constant (const byte) main::$7 = main::$8 +Successful SSA optimization Pass2ConstantIdentification +Consolidated array index constant in *(main::SCREEN#0+0) +Consolidated array index constant in *(main::SCREEN#0+1) +Consolidated array index constant in *(main::SCREEN#0+2) +Consolidated array index constant in *(main::SCREEN#0+3) +Successful SSA optimization Pass2ConstantAdditionElimination +Successful SSA optimization PassNEliminateUnusedVars +Inlining constant with different constant siblings (const void()*) main::f#1 +Inlining constant with different constant siblings (const void()*) main::f#2 +Constant inlined main::$1 = ((word))&(void()) fn1() +Constant inlined main::$2 = <((word))&(void()) fn1() +Constant inlined main::$6 = ((word))&(void()) fn2() +Constant inlined main::f#2 = &(void()) fn2() +Constant inlined main::$3 = ((word))&(void()) fn1() +Constant inlined main::$4 = >((word))&(void()) fn1() +Constant inlined main::$9 = >((word))&(void()) fn2() +Constant inlined main::$7 = <((word))&(void()) fn2() +Constant inlined main::f#1 = &(void()) fn1() +Constant inlined main::$8 = ((word))&(void()) fn2() +Successful SSA optimization Pass2ConstantInlining +Simplifying constant plus zero main::SCREEN#0+0 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @3 +Adding NOP phi() at start of @end +CALL GRAPH +Calls in [] to main:2 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Renumbering block @3 to @1 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] *((const byte*) main::SCREEN#0) ← <((word))&(void()) fn1() + [5] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 1) ← >((word))&(void()) fn1() + [6] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 2) ← <((word))&(void()) fn2() + [7] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 3) ← >((word))&(void()) fn2() + to:main::@return +main::@return: scope:[main] from main + [8] return + to:@return +fn2: scope:[fn2] from + [9] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) + to:fn2::@return +fn2::@return: scope:[fn2] from fn2 + [10] return + to:@return +fn1: scope:[fn1] from + [11] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) + to:fn1::@return +fn1::@return: scope:[fn1] from fn1 + [12] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(void()) fn1() +(byte*) fn1::BORDERCOL +(void()) fn2() +(byte*) fn2::BGCOL +(void()) main() +(byte*) main::SCREEN +(void()*) main::f + +Initial phi equivalence classes +Complete equivalence classes + +INITIAL ASM +//SEG0 File Comments +// Tests creating pointers to non-args no-return functions +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels +//SEG3 @begin +bbegin: +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG5 @1 +b1: +//SEG6 [2] call main + jsr main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG8 @end +bend: +//SEG9 main +main: { + .label SCREEN = $400 + //SEG10 [4] *((const byte*) main::SCREEN#0) ← <((word))&(void()) fn1() -- _deref_pbuc1=vbuc2 + lda #((word))&(void()) fn1() -- _deref_pbuc1=vbuc2 + lda #>fn1 + sta SCREEN+1 + //SEG12 [6] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 2) ← <((word))&(void()) fn2() -- _deref_pbuc1=vbuc2 + lda #((word))&(void()) fn2() -- _deref_pbuc1=vbuc2 + lda #>fn2 + sta SCREEN+3 + jmp breturn + //SEG14 main::@return + breturn: + //SEG15 [8] return + rts +} +//SEG16 fn2 +fn2: { + .label BGCOL = $d021 + //SEG17 [9] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BGCOL + jmp breturn + //SEG18 fn2::@return + breturn: + //SEG19 [10] return + rts +} +//SEG20 fn1 +fn1: { + .label BORDERCOL = $d020 + //SEG21 [11] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BORDERCOL + jmp breturn + //SEG22 fn1::@return + breturn: + //SEG23 [12] return + rts +} + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] *((const byte*) main::SCREEN#0) ← <((word))&(void()) fn1() [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [5] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 1) ← >((word))&(void()) fn1() [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [6] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 2) ← <((word))&(void()) fn2() [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [7] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 3) ← >((word))&(void()) fn2() [ ] ( main:2 [ ] ) always clobbers reg byte a + +REGISTER UPLIFT SCOPES +Uplift Scope [main] +Uplift Scope [fn1] +Uplift Scope [fn2] +Uplift Scope [] + +Uplifting [main] best 75 combination +Uplifting [fn1] best 75 combination +Uplifting [fn2] best 75 combination +Uplifting [] best 75 combination + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 File Comments +// Tests creating pointers to non-args no-return functions +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels +//SEG3 @begin +bbegin: +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG5 @1 +b1: +//SEG6 [2] call main + jsr main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG8 @end +bend: +//SEG9 main +main: { + .label SCREEN = $400 + //SEG10 [4] *((const byte*) main::SCREEN#0) ← <((word))&(void()) fn1() -- _deref_pbuc1=vbuc2 + lda #((word))&(void()) fn1() -- _deref_pbuc1=vbuc2 + lda #>fn1 + sta SCREEN+1 + //SEG12 [6] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 2) ← <((word))&(void()) fn2() -- _deref_pbuc1=vbuc2 + lda #((word))&(void()) fn2() -- _deref_pbuc1=vbuc2 + lda #>fn2 + sta SCREEN+3 + jmp breturn + //SEG14 main::@return + breturn: + //SEG15 [8] return + rts +} +//SEG16 fn2 +fn2: { + .label BGCOL = $d021 + //SEG17 [9] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BGCOL + jmp breturn + //SEG18 fn2::@return + breturn: + //SEG19 [10] return + rts +} +//SEG20 fn1 +fn1: { + .label BORDERCOL = $d020 + //SEG21 [11] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BORDERCOL + jmp breturn + //SEG22 fn1::@return + breturn: + //SEG23 [12] return + rts +} + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp breturn +Removing instruction jmp breturn +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction bend_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction breturn: +Removing instruction breturn: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(void()) fn1() +(label) fn1::@return +(byte*) fn1::BORDERCOL +(const byte*) fn1::BORDERCOL#0 BORDERCOL = ((byte*))(word/dword/signed dword) $d020 +(void()) fn2() +(label) fn2::@return +(byte*) fn2::BGCOL +(const byte*) fn2::BGCOL#0 BGCOL = ((byte*))(word/dword/signed dword) $d021 +(void()) main() +(label) main::@return +(byte*) main::SCREEN +(const byte*) main::SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400 +(void()*) main::f + + + +FINAL ASSEMBLER +Score: 54 + +//SEG0 File Comments +// Tests creating pointers to non-args no-return functions +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG2 Global Constants & labels +//SEG3 @begin +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +//SEG5 @1 +//SEG6 [2] call main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +//SEG8 @end +//SEG9 main +main: { + .label SCREEN = $400 + //SEG10 [4] *((const byte*) main::SCREEN#0) ← <((word))&(void()) fn1() -- _deref_pbuc1=vbuc2 + lda #((word))&(void()) fn1() -- _deref_pbuc1=vbuc2 + lda #>fn1 + sta SCREEN+1 + //SEG12 [6] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 2) ← <((word))&(void()) fn2() -- _deref_pbuc1=vbuc2 + lda #((word))&(void()) fn2() -- _deref_pbuc1=vbuc2 + lda #>fn2 + sta SCREEN+3 + //SEG14 main::@return + //SEG15 [8] return + rts +} +//SEG16 fn2 +fn2: { + .label BGCOL = $d021 + //SEG17 [9] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BGCOL + //SEG18 fn2::@return + //SEG19 [10] return + rts +} +//SEG20 fn1 +fn1: { + .label BORDERCOL = $d020 + //SEG21 [11] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BORDERCOL + //SEG22 fn1::@return + //SEG23 [12] return + rts +} + diff --git a/src/test/ref/function-pointer-noarg.sym b/src/test/ref/function-pointer-noarg.sym new file mode 100644 index 000000000..2f402517b --- /dev/null +++ b/src/test/ref/function-pointer-noarg.sym @@ -0,0 +1,17 @@ +(label) @1 +(label) @begin +(label) @end +(void()) fn1() +(label) fn1::@return +(byte*) fn1::BORDERCOL +(const byte*) fn1::BORDERCOL#0 BORDERCOL = ((byte*))(word/dword/signed dword) $d020 +(void()) fn2() +(label) fn2::@return +(byte*) fn2::BGCOL +(const byte*) fn2::BGCOL#0 BGCOL = ((byte*))(word/dword/signed dword) $d021 +(void()) main() +(label) main::@return +(byte*) main::SCREEN +(const byte*) main::SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400 +(void()*) main::f +