mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-02-10 10:31:27 +00:00
Implemented proper handling of referenced to constants inside inline ASM. Closes #146
This commit is contained in:
parent
d293c47141
commit
4eed4f7659
@ -190,7 +190,7 @@ public class ControlFlowGraphCopyVisitor extends ControlFlowGraphBaseVisitor<Obj
|
||||
|
||||
@Override
|
||||
public Object visitAsm(StatementAsm orig) {
|
||||
return new StatementAsm(orig.getAsmLines(), orig.getSource(), orig.getComments());
|
||||
return new StatementAsm(orig.getAsmLines(), orig.getReferenced(), orig.getSource(), orig.getComments());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -202,6 +202,27 @@ public abstract class ProgramValue {
|
||||
|
||||
}
|
||||
|
||||
/** A variable/constant referenced inside inline ASM. */
|
||||
public static class AsmReferenced extends ProgramValue {
|
||||
private StatementAsm statementAsm;
|
||||
private String label;
|
||||
|
||||
public AsmReferenced(StatementAsm statementAsm, String label) {
|
||||
this.statementAsm = statementAsm;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RValue get() {
|
||||
return statementAsm.getReferenced().get(label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(RValue value) {
|
||||
statementAsm.getReferenced().put(label, (SymbolVariableRef) value);
|
||||
}
|
||||
}
|
||||
|
||||
/** Location inside inline kickasm code. */
|
||||
public static class KickAsmLocation extends ProgramValue {
|
||||
|
||||
@ -615,4 +636,5 @@ public abstract class ProgramValue {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -9,10 +9,14 @@ import dk.camelot64.kickc.model.symbols.ProgramScope;
|
||||
import dk.camelot64.kickc.model.symbols.SymbolVariable;
|
||||
import dk.camelot64.kickc.model.types.SymbolTypeArray;
|
||||
import dk.camelot64.kickc.model.values.*;
|
||||
import dk.camelot64.kickc.parser.KickCBaseVisitor;
|
||||
import dk.camelot64.kickc.parser.KickCParser;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Capable of iterating the different structures of a Program (graph, block, statement, symboltable, symbol).
|
||||
@ -134,7 +138,11 @@ public class ProgramValueIterator {
|
||||
execute(new ProgramValue.KickAsmCycles(statementKickAsm), handler, statement, statementsIt, block);
|
||||
}
|
||||
} else if(statement instanceof StatementAsm) {
|
||||
|
||||
StatementAsm statementAsm = (StatementAsm) statement;
|
||||
Map<String, SymbolVariableRef> referenced = statementAsm.getReferenced();
|
||||
for(String label : referenced.keySet()) {
|
||||
execute(new ProgramValue.AsmReferenced(statementAsm, label), handler, statement, statementsIt, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,9 +2,11 @@ package dk.camelot64.kickc.model.statements;
|
||||
|
||||
import dk.camelot64.kickc.model.Comment;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.values.SymbolVariableRef;
|
||||
import dk.camelot64.kickc.parser.KickCParser;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** Inline ASM code */
|
||||
public class StatementAsm extends StatementBase {
|
||||
@ -12,9 +14,13 @@ public class StatementAsm extends StatementBase {
|
||||
/** ASM Fragment code. */
|
||||
private KickCParser.AsmLinesContext asmLines;
|
||||
|
||||
public StatementAsm(KickCParser.AsmLinesContext asmLines, StatementSource source, List<Comment> comments) {
|
||||
/** All variables/constants referenced in the inline assembler. */
|
||||
private Map<String, SymbolVariableRef> referenced;
|
||||
|
||||
public StatementAsm(KickCParser.AsmLinesContext asmLines, Map<String, SymbolVariableRef> referenced, StatementSource source, List<Comment> comments) {
|
||||
super(null, source, comments);
|
||||
this.asmLines = asmLines;
|
||||
this.referenced = referenced;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -32,4 +38,11 @@ public class StatementAsm extends StatementBase {
|
||||
return asmLines;
|
||||
}
|
||||
|
||||
public void setReferenced(Map<String, SymbolVariableRef> referenced) {
|
||||
this.referenced = referenced;
|
||||
}
|
||||
|
||||
public Map<String, SymbolVariableRef> getReferenced() {
|
||||
return referenced;
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,7 @@ import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -73,7 +70,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
|
||||
@Override
|
||||
public Void visitFile(KickCParser.FileContext ctx) {
|
||||
if(program.getFileComments()==null) {
|
||||
if(program.getFileComments() == null) {
|
||||
// Only set program file level comments for the first file.
|
||||
program.setFileComments(ensureUnusedComments(getCommentsFile(ctx)));
|
||||
}
|
||||
@ -325,7 +322,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
ConstantInteger zero = new ConstantInteger(0l);
|
||||
Statement stmt = new StatementAssignment(lValue.getRef(), zero, new StatementSource(ctx), ensureUnusedComments(comments));
|
||||
sequence.addStatement(stmt);
|
||||
} else if(type instanceof SymbolTypeArray) {
|
||||
} else if(type instanceof SymbolTypeArray) {
|
||||
// Add an zero-array initializer
|
||||
SymbolTypeArray typeArray = (SymbolTypeArray) type;
|
||||
RValue size = typeArray.getSize();
|
||||
@ -334,14 +331,14 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
}
|
||||
Statement stmt = new StatementAssignment(lValue.getRef(), new ArrayFilled(typeArray.getElementType(), size), new StatementSource(ctx), ensureUnusedComments(comments));
|
||||
sequence.addStatement(stmt);
|
||||
} else if(type instanceof SymbolTypePointer) {
|
||||
} else if(type instanceof SymbolTypePointer) {
|
||||
// Add an zero value initializer
|
||||
SymbolTypePointer typePointer = (SymbolTypePointer) type;
|
||||
ConstantValue zero = new ConstantPointer(0l, typePointer.getElementType());
|
||||
Statement stmt = new StatementAssignment(lValue.getRef(), zero, new StatementSource(ctx), ensureUnusedComments(comments));
|
||||
sequence.addStatement(stmt);
|
||||
} else {
|
||||
throw new CompileError("Default initializer not implemented for type "+type.getTypeName(), new StatementSource(ctx));
|
||||
throw new CompileError("Default initializer not implemented for type " + type.getTypeName(), new StatementSource(ctx));
|
||||
}
|
||||
|
||||
}
|
||||
@ -538,7 +535,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
Label doJumpLabel = getCurrentSymbols().addLabelIntermediate();
|
||||
Label endJumpLabel = getCurrentSymbols().addLabelIntermediate();
|
||||
List<Comment> comments = ensureUnusedComments(getCommentsSymbol(ctx));
|
||||
StatementLabel beginJumpTarget = new StatementLabel(beginJumpLabel.getRef(), new StatementSource(ctx),comments);
|
||||
StatementLabel beginJumpTarget = new StatementLabel(beginJumpLabel.getRef(), new StatementSource(ctx), comments);
|
||||
sequence.addStatement(beginJumpTarget);
|
||||
PrePostModifierHandler.addPreModifiers(this, ctx.expr());
|
||||
RValue rValue = (RValue) this.visit(ctx.expr());
|
||||
@ -694,7 +691,26 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
@Override
|
||||
public Object visitStmtAsm(KickCParser.StmtAsmContext ctx) {
|
||||
List<Comment> comments = ensureUnusedComments(getCommentsSymbol(ctx));
|
||||
sequence.addStatement(new StatementAsm(ctx.asmLines(), new StatementSource(ctx), comments));
|
||||
|
||||
Map<String, SymbolVariableRef> referenced = new LinkedHashMap<>();
|
||||
// Find all referenced symbols in the asm lines
|
||||
KickCBaseVisitor<Void> visitor = new KickCBaseVisitor<Void>() {
|
||||
@Override
|
||||
public Void visitAsmExprLabel(KickCParser.AsmExprLabelContext ctxLabel) {
|
||||
String label = ctxLabel.NAME().toString();
|
||||
Symbol symbol = getCurrentSymbols().getSymbol(ctxLabel.NAME().getText());
|
||||
if(symbol instanceof Variable) {
|
||||
Variable variable = (Variable) symbol;
|
||||
referenced.put(label, variable.getRef());
|
||||
} else {
|
||||
throw new CompileError("Symbol referenced in inline ASM not found " + label, new StatementSource(ctxLabel));
|
||||
}
|
||||
return super.visitAsmExprLabel(ctxLabel);
|
||||
}
|
||||
};
|
||||
visitor.visit(ctx.asmLines());
|
||||
StatementAsm statementAsm = new StatementAsm(ctx.asmLines(), referenced, new StatementSource(ctx), comments);
|
||||
sequence.addStatement(statementAsm);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -775,7 +791,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
public Object visitExprAssignment(KickCParser.ExprAssignmentContext ctx) {
|
||||
Object val = visit(ctx.expr(0));
|
||||
if(!(val instanceof LValue)) {
|
||||
throw new CompileError("Error! Illegal assignment Lvalue " + val.toString(), new StatementSource(ctx));
|
||||
throw new CompileError("Error! Illegal assignment Lvalue " + val.toString(), new StatementSource(ctx));
|
||||
}
|
||||
LValue lValue = (LValue) val;
|
||||
if(lValue instanceof VariableRef && ((VariableRef) lValue).isIntermediate()) {
|
||||
@ -966,7 +982,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
if(hiddenToken.getChannel() == CHANNEL_WHITESPACE) {
|
||||
String text = hiddenToken.getText();
|
||||
long newlineCount = text.chars().filter(ch -> ch == '\n').count();
|
||||
if(newlineCount > 1 && comments.size()>0) {
|
||||
if(newlineCount > 1 && comments.size() > 0) {
|
||||
// Create new comment block
|
||||
commentBlocks.add(comments);
|
||||
comments = new ArrayList<>();
|
||||
@ -978,17 +994,17 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
text = text.substring(2);
|
||||
}
|
||||
if(text.startsWith("/*")) {
|
||||
text = text.substring(2, text.length()-2);
|
||||
text = text.substring(2, text.length() - 2);
|
||||
isBlock = true;
|
||||
}
|
||||
Comment comment = new Comment(text);
|
||||
comment.setBlock(isBlock);
|
||||
comment.setTokenIndex(hiddenToken.getTokenIndex());
|
||||
comments.add( comment);
|
||||
comments.add(comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(comments.size()>0) {
|
||||
if(comments.size() > 0) {
|
||||
commentBlocks.add(comments);
|
||||
}
|
||||
return commentBlocks;
|
||||
@ -1004,7 +1020,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
* @return The comments if they are unused. An empty comment if they had already been used.
|
||||
*/
|
||||
private List<Comment> ensureUnusedComments(List<Comment> candidate) {
|
||||
if(candidate.size()==0) {
|
||||
if(candidate.size() == 0) {
|
||||
return candidate;
|
||||
}
|
||||
int tokenIndex = candidate.get(0).getTokenIndex();
|
||||
@ -1027,7 +1043,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
*/
|
||||
private List<Comment> getCommentsFile(ParserRuleContext ctx) {
|
||||
List<List<Comment>> commentBlocks = getCommentBlocks(ctx);
|
||||
if(commentBlocks.size()==0) {
|
||||
if(commentBlocks.size() == 0) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return commentBlocks.get(0);
|
||||
@ -1042,7 +1058,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
*/
|
||||
private List<Comment> getCommentsSymbol(ParserRuleContext ctx) {
|
||||
List<List<Comment>> commentBlocks = getCommentBlocks(ctx);
|
||||
if(commentBlocks.size()==0) {
|
||||
if(commentBlocks.size() == 0) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return commentBlocks.get(commentBlocks.size() - 1);
|
||||
|
@ -4,14 +4,22 @@ import dk.camelot64.kickc.model.CompileError;
|
||||
import dk.camelot64.kickc.model.ControlFlowBlock;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.statements.Statement;
|
||||
import dk.camelot64.kickc.model.statements.StatementAsm;
|
||||
import dk.camelot64.kickc.model.statements.StatementKickAsm;
|
||||
import dk.camelot64.kickc.model.values.ConstantRef;
|
||||
import dk.camelot64.kickc.model.values.ConstantValue;
|
||||
import dk.camelot64.kickc.model.values.RValue;
|
||||
import dk.camelot64.kickc.model.values.SymbolVariableRef;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Asserts that some RValues have been resolved to Constants.
|
||||
* Checks:
|
||||
* - KickAssembler locations
|
||||
* - KickAssembler bytes
|
||||
* - KickAssembler cycles
|
||||
* - ASM referenced variables
|
||||
*/
|
||||
public class Pass3AssertConstants extends Pass2SsaAssertion {
|
||||
|
||||
@ -36,6 +44,15 @@ public class Pass3AssertConstants extends Pass2SsaAssertion {
|
||||
if(cycles!= null && !(cycles instanceof ConstantValue)) {
|
||||
throw new CompileError("Error! KickAssembler cycles is not constant " + cycles.toString(), statement);
|
||||
}
|
||||
} else if(statement instanceof StatementAsm) {
|
||||
StatementAsm statementAsm = (StatementAsm) statement;
|
||||
Map<String, SymbolVariableRef> referenced = statementAsm.getReferenced();
|
||||
for(String label : referenced.keySet()) {
|
||||
SymbolVariableRef symbolRef = referenced.get(label);
|
||||
if(!(symbolRef instanceof ConstantRef)) {
|
||||
throw new CompileError("Error! Inline ASM reference is not constant " + label, statement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,12 +44,22 @@ public class TestPrograms {
|
||||
AsmFragmentTemplateUsages.logUsages(log, false, false, false, false, false, false);
|
||||
}
|
||||
|
||||
/*
|
||||
@Test
|
||||
public void testInlineAsmRefoutProblem() throws IOException, URISyntaxException {
|
||||
compileAndCompare("inline-asm-refout-problem");
|
||||
public void testInlineAsmRefoutIllegal() throws IOException, URISyntaxException {
|
||||
assertError("inline-asm-refout-illegal", "Inline ASM reference is not constant");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInlineAsmRefoutConst() throws IOException, URISyntaxException {
|
||||
compileAndCompare("inline-asm-refout-const");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInlineAsmRefoutUndef() throws IOException, URISyntaxException {
|
||||
assertError("inline-asm-refout-undef", "Symbol referenced in inline ASM not found");
|
||||
}
|
||||
|
||||
/*
|
||||
@Test
|
||||
public void testConstIfProblem() throws IOException, URISyntaxException {
|
||||
compileAndCompare("const-if-problem");
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Illustrates how inline assembler can reference data from the outside program
|
||||
// Illustrates how inline assembler can reference data from the outside program without the data being optimized away as unused
|
||||
|
||||
const byte* SCREEN = $400;
|
||||
byte[] table = "cml!";
|
14
src/test/kc/inline-asm-refout-illegal.kc
Normal file
14
src/test/kc/inline-asm-refout-illegal.kc
Normal file
@ -0,0 +1,14 @@
|
||||
// Illustrates how inline assembler referencing variables is illegal
|
||||
|
||||
const byte* SCREEN = $400;
|
||||
|
||||
void main() {
|
||||
for( byte i: 0..10) {
|
||||
asm {
|
||||
lda #'a'
|
||||
ldx i
|
||||
sta SCREEN,x
|
||||
}
|
||||
}
|
||||
|
||||
}
|
6
src/test/kc/inline-asm-refout-undef.kc
Normal file
6
src/test/kc/inline-asm-refout-undef.kc
Normal file
@ -0,0 +1,6 @@
|
||||
// Reference to undefined symbol in inline asm
|
||||
void main() {
|
||||
asm {
|
||||
lda qwe
|
||||
}
|
||||
}
|
16
src/test/ref/inline-asm-refout-const.asm
Normal file
16
src/test/ref/inline-asm-refout-const.asm
Normal file
@ -0,0 +1,16 @@
|
||||
// Illustrates how inline assembler can reference data from the outside program without the data being optimized away as unused
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
.label SCREEN = $400
|
||||
main: {
|
||||
ldx #0
|
||||
!:
|
||||
lda table,x
|
||||
sta SCREEN+1,x
|
||||
inx
|
||||
cpx #4
|
||||
bne !-
|
||||
rts
|
||||
}
|
||||
table: .text "cml!"
|
15
src/test/ref/inline-asm-refout-const.cfg
Normal file
15
src/test/ref/inline-asm-refout-const.cfg
Normal file
@ -0,0 +1,15 @@
|
||||
@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
|
||||
asm { ldx#0 !: ldatable,x staSCREEN+1,x inx cpx#4 bne!- }
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
[5] return
|
||||
to:@return
|
233
src/test/ref/inline-asm-refout-const.log
Normal file
233
src/test/ref/inline-asm-refout-const.log
Normal file
@ -0,0 +1,233 @@
|
||||
|
||||
CONTROL FLOW GRAPH SSA
|
||||
@begin: scope:[] from
|
||||
(byte*) SCREEN#0 ← ((byte*)) (word/signed word/dword/signed dword) $400
|
||||
(byte[]) table#0 ← (const string) $0
|
||||
to:@1
|
||||
main: scope:[main] from @1
|
||||
asm { ldx#0 !: ldatable,x staSCREEN+1,x inx cpx#4 bne!- }
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
return
|
||||
to:@return
|
||||
@1: scope:[] from @begin
|
||||
call main
|
||||
to:@2
|
||||
@2: scope:[] from @1
|
||||
to:@end
|
||||
@end: scope:[] from @2
|
||||
|
||||
SYMBOL TABLE SSA
|
||||
(const string) $0 = (string) "cml!"
|
||||
(label) @1
|
||||
(label) @2
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(byte*) SCREEN
|
||||
(byte*) SCREEN#0
|
||||
(void()) main()
|
||||
(label) main::@return
|
||||
(byte[]) table
|
||||
(byte[]) table#0
|
||||
|
||||
Culled Empty Block (label) @2
|
||||
Successful SSA optimization Pass2CullEmptyBlocks
|
||||
Constant (const byte*) SCREEN#0 = ((byte*))$400
|
||||
Constant (const byte[]) table#0 = $0
|
||||
Successful SSA optimization Pass2ConstantIdentification
|
||||
Constant inlined $0 = (const byte[]) table#0
|
||||
Successful SSA optimization Pass2ConstantInlining
|
||||
Adding NOP phi() at start of @begin
|
||||
Adding NOP phi() at start of @1
|
||||
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
|
||||
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
|
||||
asm { ldx#0 !: ldatable,x staSCREEN+1,x inx cpx#4 bne!- }
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
[5] return
|
||||
to:@return
|
||||
|
||||
|
||||
VARIABLE REGISTER WEIGHTS
|
||||
(byte*) SCREEN
|
||||
(void()) main()
|
||||
(byte[]) table
|
||||
|
||||
Initial phi equivalence classes
|
||||
Complete equivalence classes
|
||||
|
||||
INITIAL ASM
|
||||
//SEG0 File Comments
|
||||
// Illustrates how inline assembler can reference data from the outside program without the data being optimized away as unused
|
||||
//SEG1 Basic Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(bbegin)
|
||||
.pc = $80d "Program"
|
||||
//SEG2 Global Constants & labels
|
||||
.label SCREEN = $400
|
||||
//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: {
|
||||
//SEG10 asm { ldx#0 !: ldatable,x staSCREEN+1,x inx cpx#4 bne!- }
|
||||
ldx #0
|
||||
!:
|
||||
lda table,x
|
||||
sta SCREEN+1,x
|
||||
inx
|
||||
cpx #4
|
||||
bne !-
|
||||
jmp breturn
|
||||
//SEG11 main::@return
|
||||
breturn:
|
||||
//SEG12 [5] return
|
||||
rts
|
||||
}
|
||||
table: .text "cml!"
|
||||
|
||||
REGISTER UPLIFT POTENTIAL REGISTERS
|
||||
Statement asm { ldx#0 !: ldatable,x staSCREEN+1,x inx cpx#4 bne!- } always clobbers reg byte a reg byte x
|
||||
|
||||
REGISTER UPLIFT SCOPES
|
||||
Uplift Scope [main]
|
||||
Uplift Scope []
|
||||
|
||||
Uplifting [main] best 39 combination
|
||||
Uplifting [] best 39 combination
|
||||
|
||||
ASSEMBLER BEFORE OPTIMIZATION
|
||||
//SEG0 File Comments
|
||||
// Illustrates how inline assembler can reference data from the outside program without the data being optimized away as unused
|
||||
//SEG1 Basic Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(bbegin)
|
||||
.pc = $80d "Program"
|
||||
//SEG2 Global Constants & labels
|
||||
.label SCREEN = $400
|
||||
//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: {
|
||||
//SEG10 asm { ldx#0 !: ldatable,x staSCREEN+1,x inx cpx#4 bne!- }
|
||||
ldx #0
|
||||
!:
|
||||
lda table,x
|
||||
sta SCREEN+1,x
|
||||
inx
|
||||
cpx #4
|
||||
bne !-
|
||||
jmp breturn
|
||||
//SEG11 main::@return
|
||||
breturn:
|
||||
//SEG12 [5] return
|
||||
rts
|
||||
}
|
||||
table: .text "cml!"
|
||||
|
||||
ASSEMBLER OPTIMIZATIONS
|
||||
Removing instruction jmp b1
|
||||
Removing instruction jmp bend
|
||||
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:
|
||||
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
|
||||
(byte*) SCREEN
|
||||
(const byte*) SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400
|
||||
(void()) main()
|
||||
(label) main::@return
|
||||
(byte[]) table
|
||||
(const byte[]) table#0 table = (string) "cml!"
|
||||
|
||||
|
||||
|
||||
FINAL ASSEMBLER
|
||||
Score: 24
|
||||
|
||||
//SEG0 File Comments
|
||||
// Illustrates how inline assembler can reference data from the outside program without the data being optimized away as unused
|
||||
//SEG1 Basic Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
//SEG2 Global Constants & labels
|
||||
.label SCREEN = $400
|
||||
//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: {
|
||||
//SEG10 asm { ldx#0 !: ldatable,x staSCREEN+1,x inx cpx#4 bne!- }
|
||||
ldx #0
|
||||
!:
|
||||
lda table,x
|
||||
sta SCREEN+1,x
|
||||
inx
|
||||
cpx #4
|
||||
bne !-
|
||||
//SEG11 main::@return
|
||||
//SEG12 [5] return
|
||||
rts
|
||||
}
|
||||
table: .text "cml!"
|
||||
|
10
src/test/ref/inline-asm-refout-const.sym
Normal file
10
src/test/ref/inline-asm-refout-const.sym
Normal file
@ -0,0 +1,10 @@
|
||||
(label) @1
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(byte*) SCREEN
|
||||
(const byte*) SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400
|
||||
(void()) main()
|
||||
(label) main::@return
|
||||
(byte[]) table
|
||||
(const byte[]) table#0 table = (string) "cml!"
|
||||
|
Loading…
x
Reference in New Issue
Block a user