1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-06-03 07:29:37 +00:00

Libraries

This commit is contained in:
Sven Van de Velde 2023-10-27 15:31:51 +02:00
parent 1025aa5b00
commit 1f0bddc768
24 changed files with 380 additions and 133 deletions

View File

@ -80,6 +80,7 @@ FORM_SSA: '__ssa' ;
FORM_MA: '__ma' ;
INTRINSIC: '__intrinsic' ;
CALLINGCONVENTION: '__stackcall' | '__phicall' | '__varcall' | '__intrinsiccall';
LIBRARY: '__library';
IF: 'if' ;
ELSE: 'else' ;
WHILE: 'while' ;

View File

@ -166,6 +166,7 @@ directive
| INTERRUPT ( PAR_BEGIN NAME PAR_END )? #directiveInterrupt
| LOCAL_RESERVE PAR_BEGIN pragmaParam ( COMMA pragmaParam )* PAR_END #directiveReserveZp
| CALLINGCONVENTION #directiveCallingConvention
| LIBRARY PAR_BEGIN pragmaParam PAR_END #directiveLibrary
;
stmtSeq

View File

@ -242,7 +242,7 @@ public class Compiler {
if(getLog().isVerbosePass1CreateSsa()) {
getLog().append("SYMBOLS");
getLog().append(program.getScope().toStringVars(program, false));
getLog().append(program.getScope().toStringVars(program, false, false));
}
new Pass1FixLValuesLoHi(program).execute();
@ -337,7 +337,7 @@ public class Compiler {
getLog().append(program.prettyControlFlowGraph());
getLog().append("SYMBOL TABLE SSA");
getLog().append(program.getScope().toStringVars(program, false));
getLog().append(program.getScope().toStringVars(program, false, false));
program.endPass1();
@ -673,7 +673,7 @@ public class Compiler {
getLog().append("\nVARIABLE REGISTER WEIGHTS");
program.getVariableRegisterWeights();
getLog().append(program.getScope().toStringVars(program, true));
getLog().append(program.getScope().toStringVars(program, true, false));
new Pass4LiveRangeEquivalenceClassesFinalize(program).allocate();
new Pass4RegistersFinalize(program).allocate(true, false);
@ -784,12 +784,15 @@ public class Compiler {
}
getLog().append("\nFINAL SYMBOL TABLE");
getLog().append(program.getScope().toStringVars(program, false));
getLog().append(program.getScope().toStringVars(program, false, false));
getLog().append("\nFINAL ASSEMBLER");
getLog().append("Score: " + Pass4RegisterUpliftCombinations.getAsmScore(program) + "\n");
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(false, true, true, false), program));
getLog().append("\nZERO PAGE TABLE");
getLog().append(program.getScope().toStringVars(program, true, true));
}
}

View File

@ -292,7 +292,7 @@ public class AsmFormat {
if(!symbolScopeRef.equals(codeScopeRef)) {
if(symbolScopeRef.getFullName().length() > 0)
// Reference to symbol in another scope
return asmFix(symbolScopeRef.getFullName() + "." + asmName);
return asmFix2(symbolScopeRef.getFullName() + "." + asmName, symbolScopeRef.getFullName());
else {
// Check if the local code scope has a symbol with the same name
final Scope codeScope = program.getScope().getScope(codeScopeRef);
@ -305,7 +305,7 @@ public class AsmFormat {
}
} else {
// Reference to local symbol
return asmFix(asmName);
return asmFix2(asmName, codeScopeRef.getLocalName());
}
}
@ -337,5 +337,32 @@ public class AsmFormat {
return result.toString();
}
/**
* Fix characters in an ASM parameter/label name. Handles '@:$#'
*
* @param source The source string
* @return The fixed string
*/
public static String asmFix2(String source, String prefix) {
StringBuilder result = new StringBuilder();
char[] sourceChars = source.toCharArray();
for(char sourceChar : sourceChars) {
switch(sourceChar) {
case '@':
result.append("__b");
break;
case ':':
case '#':
result.append('_');
break;
case '$':
result.append( prefix + "__");
break;
default:
result.append(sourceChar);
}
}
return result.toString();
}
}

View File

@ -57,7 +57,7 @@ final public class AsmFragmentInstanceSpecBuilder {
(callingDistance.equals(Bank.CallingDistance.NEAR)?null:toBank.bankArea()));
ScopeRef codeScope = program.getScope().getRef();
bindings.bind("c1", new ConstantInteger(toBank.bankNumber()));
bindings.bind("la1", new LabelRef(toProcedure.getFullName()));
bindings.bind("la1", new LabelRef(((toProcedure.getLibrary() != null)?toProcedure.getLibrary().toLowerCase() + ".":"") + toProcedure.getFullName()));
return new AsmFragmentInstanceSpec(program, signature, bindings, codeScope);
}

View File

@ -2,6 +2,7 @@ package dk.camelot64.kickc.fragment.signature;
import dk.camelot64.kickc.asm.fragment.signature.AsmFragmentSignatureLexer;
import dk.camelot64.kickc.asm.fragment.signature.AsmFragmentSignatureParser;
import dk.camelot64.kickc.model.symbols.Bank;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;

View File

@ -1,9 +1,6 @@
package dk.camelot64.kickc.model;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.symbols.ProgramScope;
import dk.camelot64.kickc.model.symbols.Scope;
import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.symbols.*;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeProcedure;
import dk.camelot64.kickc.model.values.ConstantInteger;

View File

@ -176,5 +176,23 @@ public class Directive {
}
}
/** Indicates the location is in a library. */
public static class Library extends Directive {
public String getLibrary() {
return library;
}
public void setLibrary(String library) {
this.library = library;
}
private String library;
public Library(String library) {
super("__library");
this.library = library;
}
}
}

View File

@ -23,6 +23,10 @@ public class Procedure extends Scope {
private List<String> parameterNames;
/** True if the parameter list ends with a variable length parameter list "..." */
private boolean variableLengthParameterList;
/** True if the procedure is declared extern, which will declare the procedure and it's parameters,
* but a coding block is not defined within the compilation and must be handled at an other place.
*/
private boolean declaredExtern;
/** true if the procedure is declared inline. */
private boolean declaredInline;
/** True if the procedure is declared intrinsic. */
@ -41,6 +45,18 @@ public class Procedure extends Scope {
private boolean isConstructor;
/** The source of the procedure definition. */
private StatementSource definitionSource;
/** The library where the function resides */
private String library;
public String getLibrary() {
return library;
}
public void setLibrary(String library) {
this.library = library;
}
/**
* The bank that the procedure code is placed in.
* Used to decide whether to produce near, close or far call code when generating calls.
@ -109,6 +125,7 @@ public class Procedure extends Scope {
super(name, parentScope, segmentData);
this.procedureType = procedureType;
this.declaredInline = false;
this.declaredExtern = false;
this.bank = Objects.requireNonNull(bank);
this.interruptType = null;
this.comments = new ArrayList<>();
@ -116,6 +133,7 @@ public class Procedure extends Scope {
this.callingConvention = callingConvention;
this.constructorRefs = new ArrayList<>();
this.isConstructor = false;
this.library = null;
}
public StatementSource getDefinitionSource() {
@ -153,6 +171,13 @@ public class Procedure extends Scope {
public boolean isVariableLengthParameterList() {
return variableLengthParameterList;
}
public boolean isDeclaredExtern() {
return declaredExtern;
}
public void setDeclaredExtern(boolean declaredExtern) {
this.declaredExtern = declaredExtern;
}
public boolean isDeclaredIntrinsic() {
return declaredIntrinsic;
@ -206,7 +231,7 @@ public class Procedure extends Scope {
StringBuilder res = new StringBuilder();
res.append(toString(program));
res.append("\n");
res.append(super.toStringVars(program, onlyVars));
res.append(super.toStringVars(program, onlyVars, false));
return res.toString();
}

View File

@ -3,6 +3,7 @@ package dk.camelot64.kickc.model.symbols;
import dk.camelot64.kickc.model.LiveRangeEquivalenceClass;
import dk.camelot64.kickc.model.LiveRangeEquivalenceClassSet;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.Registers;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeProgram;
import dk.camelot64.kickc.model.values.*;
@ -112,16 +113,25 @@ public class ProgramScope extends Scope {
}
@Override
public String toStringVars(Program program, boolean onlyVars) {
public String toStringVars(Program program, boolean onlyVars, boolean onlyZP) {
LiveRangeEquivalenceClassSet liveRangeEquivalenceClassSet = program.getLiveRangeEquivalenceClassSet();
StringBuilder out = new StringBuilder();
out.append(super.toStringVars(program, onlyVars));
out.append(super.toStringVars(program, onlyVars, onlyZP));
if(liveRangeEquivalenceClassSet != null) {
out.append("\n");
Integer maxZP = 0;
for(LiveRangeEquivalenceClass liveRangeEquivalenceClass : liveRangeEquivalenceClassSet.getEquivalenceClasses()) {
out.append(liveRangeEquivalenceClass.toString());
out.append("\n");
Registers.Register register = liveRangeEquivalenceClass.getRegister();
if(onlyZP && register != null && Registers.RegisterType.ZP_MEM == register.getType()) {
out.append(liveRangeEquivalenceClass.toString());
out.append("\n");
Integer ZP = ((Registers.RegisterZpMem) register).getZp();
if (ZP > maxZP && !program.getReservedZps().contains(ZP)) {
maxZP = ZP;
}
}
}
out.append("\nMaximum zeropage register consumed: " + maxZP + String.format(" (0x%x)", maxZP));
}
return out.toString();
}

View File

@ -356,7 +356,7 @@ public abstract class Scope implements Symbol {
return (Scope) symbol;
}
public String toStringVars(Program program, boolean onlyVars) {
public String toStringVars(Program program, boolean onlyVars, boolean onlyZP) {
VariableRegisterWeights registerWeights = program.getOrNullVariableRegisterWeights();
StringBuilder res = new StringBuilder();
Set<String> names = symbols.keySet();
@ -369,10 +369,11 @@ public abstract class Scope implements Symbol {
if(symbol instanceof StructDefinition)
continue;
if(!onlyVars || symbol instanceof Procedure || symbol instanceof BlockScope || symbol instanceof ProgramScope)
res.append(((Scope) symbol).toStringVars(program, onlyVars));
res.append(((Scope) symbol).toStringVars(program, onlyVars, false));
} else if(symbol instanceof Variable) {
Variable symVar = (Variable) symbol;
if(!onlyVars || symVar.isVariable()) {
if(!(onlyZP && symVar.getRegister() instanceof Registers.RegisterZpMem && symVar.getRegister().getType() == Registers.RegisterType.ZP_MEM)) continue; // Show only ZP vars
// Output if not instructed to only output variables - or if it is a variable
if(symVar.isKindLoadStore()) res.append("__loadstore ");
if(symVar.isKindConstant()) res.append("__constant ");

View File

@ -320,17 +320,14 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
program.addReservedZps(reservedZps);
}
case CParser.PRAGMA_CONSTRUCTOR_FOR -> {
this.pragmaConstructorFors.add(ctx);
return null;
this.pragmaConstructorFors.add(ctx);
return null;
}
// Defines that the result is an asm routine or capability library instead of a program.
case CParser.PRAGMA_LIBRARY -> {
this.asmLibrary = pragmaParamName(pragmaParamSingle(ctx));
program.setAsmLibrary(this.asmLibrary);
}
default -> {
program.getLog().append("Warning! Unknown #pragma " + pragmaName);
case CParser.PRAGMA_LIBRARY -> { // Defines that the result is an asm routine or capability library instead of a program.
this.asmLibrary = pragmaParamName(pragmaParamSingle(ctx));
program.setAsmLibrary(this.asmLibrary);
}
default -> program.getLog().append("Warning! Unknown #pragma " + pragmaName);
}
return null;
}
@ -473,6 +470,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
// enter the procedure
scopeStack.push(procedure);
/** Copied to solve Issue #820 - the preparation of the return block */
Variable returnVar = procedure.getLocalVar("return");
// Add the body
@ -537,7 +535,97 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
program.createProcedureCompilation(procedure.getRef());
}
if(defineProcedure) {
if(procedure.isDeclaredExtern() && !defineProcedure) {
/** This is an almost exact copy of a procedure definition.
* When a procedure is defined external, the required control blocks are to be allocated
* for the procedure parameters and return value for the sequent steps in the compiler to work.
* However, during assembler generation, an externally defined procedure is completely ignored from being generated.
* In this way, without too much impact on the compiler design, external functions could be implemented.
*
* The code is copied and reworked from (Search for #820):
* - Issue #820 TAG A - the preparation of the parameters and the entry block.
* - Issue #820 TAG B - the preparation of the return block.
*
*/
// Make sure comments, directives and source are from the definition
addDirectives(procedure, varDecl.getDeclDirectives());
procedure.setComments(ensureUnusedComments(getCommentsSymbol(ctx)));
procedure.setDefinitionSource(statementSource);
// enter the procedure
scopeStack.push(procedure);
// Add parameter variables...
boolean variableLengthParameterList = false;
List<Variable> parameterList = new ArrayList<>();
for (ParameterDecl parameter : varDecl.parameters) {
// Handle variable length parameter lists
if (SymbolType.PARAM_LIST.equals(parameter.type)) {
procedure.setVariableLengthParameterList(true);
variableLengthParameterList = true;
continue;
} else if (variableLengthParameterList)
throw new CompileError("Variable length parameter list is only legal as the last parameter.", statementSource);
// Handle stray void parameters (Any single void parameter was removed by the type parser)
if (SymbolType.VOID.equals(parameter.type))
throw new CompileError("Illegal void parameter.", statementSource);
// Handle parameters without a name in the declaration
if (parameter.name == null)
throw new CompileError("Illegal unnamed parameter.", statementSource);
VariableBuilder varBuilder = new VariableBuilder(parameter.name, getCurrentScope(), true, false, parameter.type, null, currentSegmentData, program.getTargetPlatform().getVariableBuilderConfig());
final Variable paramVar = varBuilder.build();
parameterList.add(paramVar);
}
procedure.setParameters(parameterList);
procedure.setSegmentData(currentSegmentData); // When a procedure is defined, the currentDataSegment is to be set.
procedure.setSegmentCode(currentSegmentCode); // When a procedure is defined, the currentSegmentCode is to be set.
if(procedure.getBank() == null && currentBank != null) {
procedure.setBank(currentBank); // When a procedure is defined, the currentBank is to be set, or far calls won't work.
}
// Add return variable
if(!SymbolType.VOID.equals(procedure.getReturnType())) {
final VariableBuilder builder = new VariableBuilder("return", procedure, false, false, procedure.getReturnType(), varDecl.getDeclDirectives(), currentSegmentData, program.getTargetPlatform().getVariableBuilderConfig());
builder.build();
}
Variable returnVar = procedure.getLocalVar("return");
// Add the body
addStatement(new StatementProcedureBegin(procedure.getRef(), statementSource, Comment.NO_COMMENTS));
Label procExit = procedure.addLabel(SymbolRef.PROCEXIT_BLOCK_NAME);
// Variable tmpVar = addIntermediateVar();
// RValue rValue = tmpVar.getRef();
// returnVar = procedure.getLocalVariable("return");
//
// addStatement(new StatementAssignment((LValue) returnVar.getRef(), rValue, false, statementSource, null));
// Label returnLabel = procedure.getLocalLabel(SymbolRef.PROCEXIT_BLOCK_NAME);
// addStatement(new StatementJump(returnLabel.getRef(), new StatementSource(ctx), ensureUnusedComments(getCommentsSymbol(ctx))));
addStatement(new StatementLabel(procExit.getRef(), statementSource, Comment.NO_COMMENTS));
if(Procedure.CallingConvention.PHI_CALL.equals(procedure.getCallingConvention()) && returnVar != null && returnVar.isKindPhiMaster()) {
addStatement(new StatementAssignment(returnVar.getVariableRef(), returnVar.getRef(), false, statementSource, Comment.NO_COMMENTS));
}
SymbolVariableRef returnVarRef = null;
if(returnVar != null) {
returnVarRef = returnVar.getRef();
returnVar.setDeclarationOnly(false); // The procedure is defined as extern and this property is interited by the variable.
// program.getScope().add(tmpVar);
//tmpVar.setDeclarationOnly(false); // Same for the temporary return value.
}
addStatement(new StatementReturn(returnVarRef, statementSource, Comment.NO_COMMENTS));
addStatement(new StatementProcedureEnd(procedure.getRef(), statementSource, Comment.NO_COMMENTS));
scopeStack.pop();
}
/** Copied to solve Issue #820 TAG A */
if(defineProcedure && !procedure.isDeclaredExtern()) {
// Make sure comments, directives and source are from the definition
addDirectives(procedure, varDecl.getDeclDirectives());
procedure.setComments(ensureUnusedComments(getCommentsSymbol(ctx)));
@ -1198,6 +1286,8 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
} else if(directive instanceof Directive.Bank directiveBank) {
Bank bank = new Bank(directiveBank.getBankArea(), directiveBank.getBankNumber());
procedure.setBank(bank);
} else if(directive instanceof Directive.Library) {
procedure.setLibrary(((Directive.Library) directive).getLibrary());
} else if(directive instanceof Directive.CallingConvention) {
procedure.setCallingConvention(((Directive.CallingConvention) directive).callingConvention);
} else if(directive instanceof Directive.Interrupt) {
@ -1206,6 +1296,8 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
procedure.setReservedZps(((Directive.ReserveZp) directive).reservedZp);
} else if(directive instanceof Directive.Intrinsic) {
procedure.setDeclaredIntrinsic(true);
} else if(directive instanceof Directive.Extern) {
procedure.setDeclaredExtern(true);
//} else {
// throw new CompileError("Unsupported function directive " + directive.getName(), source);
}
@ -1264,6 +1356,11 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
return new Directive.Interrupt(interruptType);
}
public Object visitDirectiveLibrary(KickCParser.DirectiveLibraryContext ctx) {
String library = ctx.pragmaParam().getText().toLowerCase(Locale.ENGLISH);
return new Directive.Library(library);
}
@Override
public Directive visitDirectiveCallingConvention(KickCParser.DirectiveCallingConventionContext ctx) {
Procedure.CallingConvention callingConvention = Procedure.CallingConvention.getCallingConvension(ctx.getText());

View File

@ -62,10 +62,14 @@ public class Pass1AssertReturn extends Pass1Base {
}
}
Graph.Block successor = graph.getBlock(block.getDefaultSuccessor());
if(successor == null || successor.getLabel().getLocalName().equals(SymbolRef.PROCEXIT_BLOCK_NAME)) {
throw new CompileError("Error! Method must end with a return statement. " + block.getScope().toString(getProgram()));
} else {
assertReturn(graph, successor, visited);
Procedure procedure = getProgram().getProcedure(successor);
// External procedures have a dummy return statement, it is for a placeholder only and it is not defined properly.
if(!procedure.isDeclaredExtern()) {
if (successor == null || successor.getLabel().getLocalName().equals(SymbolRef.PROCEXIT_BLOCK_NAME)) {
throw new CompileError("Error! Method must end with a return statement. " + block.getScope().toString(getProgram()));
} else {
assertReturn(graph, successor, visited);
}
}
}

View File

@ -70,6 +70,8 @@ public class Pass1AssertUsedVars extends Pass1Base {
for(Statement statement : block.getStatements()) {
// PHI block has already been examined
if(statement instanceof StatementPhiBlock) continue;
// External procedures have no defined variables, not even the return statement. The blocks are dummy blocks.
if(this.getProgram().getProcedure(block).isDeclaredExtern()) continue;
Collection<VariableRef> used = referenceInfos.getUsedVars(statement);
for(VariableRef usedRef : used) {
if(!defined.contains(usedRef)) {

View File

@ -133,20 +133,21 @@ public class Pass1CallStack extends Pass2SsaOptimization {
final StatementSource source = call.getSource();
List<Comment> comments = call.getComments();
List<SymbolType> paramTypes = call.getProcedureType().getParamTypes();
for(int i=0;i<paramTypes.size();i++) {
SymbolType paramType = paramTypes.get(i);
final RValue parameterVal = call.getParameters().get(i);
generateStackPushValues(parameterVal, paramType, source, comments, stmtIt);
// Clear comments - enduring they are only output once
comments = Comment.NO_COMMENTS;
}
// Push additional bytes for padding if needed
long stackFrameByteSize = CallingConventionStack.getStackFrameByteSize(call.getProcedureType());
long parametersByteSize = CallingConventionStack.getParametersByteSize(call.getProcedureType());
final long stackPadBytes = stackFrameByteSize - parametersByteSize;
if(stackFrameByteSize > parametersByteSize) {
// Add padding to the stack to make room for the return value
stmtIt.add(new StatementExprSideEffect( new StackPushPadding(new ConstantInteger(stackPadBytes)), source, comments));
stmtIt.add(new StatementExprSideEffect( new StackPushPadding(new ConstantInteger(stackPadBytes)), source, comments));
}
// Now calculate the variables.
for(int i=0;i<paramTypes.size();i++) {
SymbolType paramType = paramTypes.get(i);
final RValue parameterVal = call.getParameters().get(i);
generateStackPushValues(parameterVal, paramType, source, comments, stmtIt);
// Clear comments - enduring they are only output once
comments = Comment.NO_COMMENTS;
}
stmtIt.next();
stmtIt.remove();

View File

@ -28,9 +28,11 @@ public class Pass1GenerateControlFlowGraph extends Pass1Base {
continue;
final ProcedureCompilation procedureCompilation = getProgram().getProcedureCompilation(procedure.getRef());
final StatementSequence sequence = procedureCompilation.getStatementSequence();
if(sequence.getStatements().size()==0)
// Empty procedures should not produce any blocks
continue;
if(!procedure.isDeclaredExtern()) {
if (sequence.getStatements().size() == 0)
// Empty procedures should not produce any blocks
continue;
}
List<Graph.Block> blocks = new ArrayList<>();

View File

@ -19,24 +19,24 @@ public class Pass2AssertPhiPredecessors extends Pass2SsaAssertion {
@Override
public void check() throws AssertionFailed {
// We cannot do this assertion if the output is an assembler library,
// since an asm library does not contain a main function ...
if(this.getProgram().getAsmLibrary() == null) {
for(var block : getGraph().getAllBlocks()) {
if(block.hasPhiBlock()) {
StatementPhiBlock phiBlock = block.getPhiBlock();
List<Graph.Block> phiPredecessors = Pass1GenerateSingleStaticAssignmentForm.getPhiPredecessors(block, getProgram());
List<LabelRef> predecessors =
phiPredecessors.stream().map(Graph.Block::getLabel).toList();
for (StatementPhiBlock.PhiVariable phiVariable : phiBlock.getPhiVariables()) {
for (StatementPhiBlock.PhiRValue phiRValue : phiVariable.getValues()) {
if (!predecessors.contains(phiRValue.getPredecessor())) {
throw new CompileError("INTERNAL ERROR! Block " + block.getLabel() + " phi references non-predecessor block " + phiRValue.getPredecessor() +
"\n " + phiBlock.toString(getProgram(), false));
}
// We cannot do this assertion if the output is an assembler library,
// since an asm library does not contain a main function ...
if(this.getProgram().getAsmLibrary() == null) {
for (var block : getGraph().getAllBlocks()) {
if (block.hasPhiBlock()) {
StatementPhiBlock phiBlock = block.getPhiBlock();
List<Graph.Block> phiPredecessors = Pass1GenerateSingleStaticAssignmentForm.getPhiPredecessors(block, getProgram());
List<LabelRef> predecessors =
phiPredecessors.stream().map(Graph.Block::getLabel).toList();
for (StatementPhiBlock.PhiVariable phiVariable : phiBlock.getPhiVariables()) {
for (StatementPhiBlock.PhiRValue phiRValue : phiVariable.getValues()) {
if (!predecessors.contains(phiRValue.getPredecessor())) {
throw new CompileError("INTERNAL ERROR! Block " + block.getLabel() + " phi references non-predecessor block " + phiRValue.getPredecessor() +
"\n " + phiBlock.toString(getProgram(), false));
}
}
}
}
}
}
}
}
}

View File

@ -8,10 +8,7 @@ import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypePointer;
import dk.camelot64.kickc.model.values.*;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* Compiler Pass finding and consolidating identical constant strings

View File

@ -123,67 +123,70 @@ public class Pass4CodeGeneration {
/** Issue #820 - Reworked the code generation, avoiding code to be generated
* for procedures which are external. These must be included through linkage.
*/
Procedure procedure = block.getProcedure(program);
Procedure procedure = program.getProcedure(block);
if (!block.getScope().equals(currentScope)) {
// The current block is in a different scope. End the old scope.
if(oldProcedure != null && !oldProcedure.isDeclaredExtern()) {
if (oldProcedure != null && !oldProcedure.isDeclaredExtern()) {
// The current block is in a different scope. End the old scope.
generateScopeEnding(asm, currentScope);
}
currentScope = block.getScope();
oldProcedure = procedure;
if (program.isProcedureEntry(block)) {
Procedure procedure = program.getProcedure(block);
currentCodeSegmentName = procedure.getSegmentCode();
}
setCurrentSegment(currentCodeSegmentName, asm);
asm.startChunk(currentScope, null, block.getLabel().getFullName());
if (!procedure.isDeclaredExtern()) {
setCurrentSegment(currentCodeSegmentName, asm);
asm.startChunk(currentScope, null, block.getLabel().getFullName());
}
// Add any procedure comments
if (program.isProcedureEntry(block)) {
Procedure procedure = program.getProcedure(block);
generateComments(asm, procedure.getComments());
// Generate parameter information
generateSignatureComments(asm, procedure);
}
// Start the new scope
asm.addScopeBegin(AsmFormat.asmFix(block.getLabel().getFullName()));
// Add all ZP labels for the scope
addConstantsAndLabels(asm, currentScope);
}
generateComments(asm, block.getComments());
// Generate entry points (if needed)
genBlockEntryPoints(asm, block);
if (program.isProcedureEntry(block)) {
// Generate interrupt entry if needed
Procedure procedure = program.getProcedure(block);
if (procedure != null && procedure.getInterruptType() != null) {
generateInterruptEntry(asm, procedure);
if (!procedure.isDeclaredExtern()) {
// Start the new scope
asm.addScopeBegin(AsmFormat.asmFix(block.getLabel().getFullName()));
// Add all ZP labels for the scope
addConstantsAndLabels(asm, currentScope);
}
} else {
// Generate label for block inside procedure
asm.startChunk(currentScope, null, block.getLabel().getFullName());
asm.addLabel(AsmFormat.asmFix(block.getLabel().getLocalName()));
}
// Generate statements
genStatements(asm, block);
// Generate exit
Graph.Block defaultSuccessor = getGraph().getDefaultSuccessor(block);
if (defaultSuccessor != null) {
if (defaultSuccessor.hasPhiBlock()) {
PhiTransitions.PhiTransition transition = getTransitions(defaultSuccessor).getTransition(block);
if (!transitionIsGenerated(transition)) {
genBlockPhiTransition(asm, block, defaultSuccessor, defaultSuccessor.getScope());
String label = AsmFormat.asmFix(defaultSuccessor.getLabel().getLocalName());
asm.addInstruction("JMP", CpuAddressingMode.ABS, label, false);
} else {
String label = AsmFormat.asmFix(defaultSuccessor.getLabel().getLocalName() + "_from_" + block.getLabel().getLocalName());
asm.addInstruction("JMP", CpuAddressingMode.ABS, label, false);
if (!program.getProcedure(block).isDeclaredExtern()) {
generateComments(asm, block.getComments());
// Generate entry points (if needed)
genBlockEntryPoints(asm, block);
if (program.isProcedureEntry(block)) {
// Generate interrupt entry if needed
if (procedure != null && procedure.getInterruptType() != null) {
generateInterruptEntry(asm, procedure);
}
} else {
String label = AsmFormat.asmFix(defaultSuccessor.getLabel().getLocalName());
asm.addInstruction("JMP", CpuAddressingMode.ABS, label, false);
// Generate label for block inside procedure
asm.startChunk(currentScope, null, block.getLabel().getFullName());
asm.addLabel(AsmFormat.asmFix(block.getLabel().getLocalName()));
}
// Generate statements
genStatements(asm, block);
// Generate exit
Graph.Block defaultSuccessor = getGraph().getDefaultSuccessor(block);
if (defaultSuccessor != null) {
if (defaultSuccessor.hasPhiBlock()) {
PhiTransitions.PhiTransition transition = getTransitions(defaultSuccessor).getTransition(block);
if (!transitionIsGenerated(transition)) {
genBlockPhiTransition(asm, block, defaultSuccessor, defaultSuccessor.getScope());
String label = AsmFormat.asmFix(defaultSuccessor.getLabel().getLocalName());
asm.addInstruction("JMP", CpuAddressingMode.ABS, label, false);
} else {
String label = AsmFormat.asmFix(defaultSuccessor.getLabel().getLocalName() + "_from_" + block.getLabel().getLocalName());
asm.addInstruction("JMP", CpuAddressingMode.ABS, label, false);
}
} else {
String label = AsmFormat.asmFix(defaultSuccessor.getLabel().getLocalName());
asm.addInstruction("JMP", CpuAddressingMode.ABS, label, false);
}
}
}
}
@ -200,11 +203,11 @@ public class Pass4CodeGeneration {
}
// Name of the current data segment
private String currentCodeSegmentName = Scope.SEGMENT_CODE_DEFAULT;
String currentCodeSegmentName = Scope.SEGMENT_CODE_DEFAULT;
// Name of the current code segment
private final String currentDataSegmentName = Scope.SEGMENT_DATA_DEFAULT;
final String currentDataSegmentName = Scope.SEGMENT_DATA_DEFAULT;
// Name of the current active segment
private String currentSegmentName = "";
String currentSegmentName = "";
/**
* Set the current ASM segment - if needed
@ -498,7 +501,7 @@ public class Pass4CodeGeneration {
// Ensure encoding is good
AsmEncodingHelper.ensureEncoding(asm, variable.getInitValue());
// Find the constant value calculation
asm.addLabelDecl(AsmFormat.asmFix(asmName), asmConstant);
asm.addLabelDecl(AsmFormat.asmFix2(asmName, variable.getScope().getLocalName()), asmConstant);
}
/**
@ -583,7 +586,7 @@ public class Pass4CodeGeneration {
if (constantValue instanceof ConstantArray || constantValue instanceof ConstantString || constantValue instanceof ConstantStructValue) {
AsmDataChunk asmDataChunk = new AsmDataChunk();
addChunkData(asmDataChunk, constantValue, constantVar.getType(), constantVar.getArraySpec(), scopeRef);
asmDataChunk.addToAsm(AsmFormat.asmFix(asmName), asm);
asmDataChunk.addToAsm(AsmFormat.asmFix2(asmName, constantVar.getScope().getLocalName()), asm);
} else {
throw new InternalError("Constant Variable not handled " + constantVar.toString(program));
}
@ -624,14 +627,15 @@ public class Pass4CodeGeneration {
ConstantValue constantValue = variable.getInitValue();
AsmDataChunk asmDataChunk = new AsmDataChunk();
addChunkData(asmDataChunk, constantValue, variable.getType(), variable.getArraySpec(), scopeRef);
asmDataChunk.addToAsm(AsmFormat.asmFix(variable.getAsmName()), asm);
asmDataChunk.addToAsm(AsmFormat.asmFix2(variable.getAsmName(), variable.getScope().getLocalName()), asm);
} else {
// Zero-fill variable
AsmDataChunk asmDataChunk = new AsmDataChunk();
ConstantValue zeroValue = Initializers.createZeroValue(new Initializers.ValueTypeSpec(variable.getType()), null);
addChunkData(asmDataChunk, zeroValue, variable.getType(), variable.getArraySpec(), scopeRef);
asmDataChunk.addToAsm(AsmFormat.asmFix(variable.getAsmName()), asm);
asmDataChunk.addToAsm(AsmFormat.asmFix2(variable.getAsmName(), variable.getScope().getLocalName()), asm);
}
}
added.add(variable.getAsmName());
}
@ -907,7 +911,14 @@ public class Pass4CodeGeneration {
} else if (Procedure.CallingConvention.STACK_CALL.equals(toProcedure.getCallingConvention())) {
final Bank.CallingDistance callingDistance = Bank.CallingDistance.forCall(fromProcedure.getBank(), toProcedure.getBank());
if(Bank.CallingDistance.NEAR.equals(callingDistance)) {
asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
if(toProcedure.getLibrary() == null) {
asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
} else {
// Call the library routine and clobber all registers.
// Possibly an option to be given that specific routines only clobber specific registers.
asm.addInstruction("jsr", CpuAddressingMode.ABS, toProcedure.getLibrary() + "." + call.getProcedure().getFullName(), false);
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
}
} else {
throw new CompileError("Stack Call procedure not supported in banked mode " + toProcedure.toString(program));
}
@ -918,14 +929,26 @@ public class Pass4CodeGeneration {
if(procedureRef != null) {
ProgramScope scope = getScope();
Procedure toProcedure = scope.getProcedure(procedureRef);
Procedure fromProcedure = program.getProcedure(block);
final Bank.CallingDistance callingDistance = Bank.CallingDistance.forCall(fromProcedure.getBank(), toProcedure.getBank());
if(Bank.CallingDistance.NEAR.equals(callingDistance)) {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program);
} else {
throw new CompileError("Stack Call procedure not supported in banked mode " + toProcedure.toString(program));
}
Procedure fromProcedure = program.getProcedure(block); // We obtain from where the procedure is called, to validate the bank equality.
RValue procedureRVal = call.getProcedureRVal();
// Same as PHI
final Bank.CallingDistance callingDistance = Bank.CallingDistance.forCall(fromProcedure.getBank(), toProcedure.getBank());
if (Bank.CallingDistance.FAR.equals(callingDistance)) {
// if (toProcedure.isDeclaredBanked() && fromProcedure.getBank() != toProcedure.getBank()) {
throw new CompileError("Stack Call procedure not supported in banked mode " + toProcedure.toString(program));
} else {
if (toProcedure.getLibrary() == null) {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program);
} else {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.callBanked(toProcedure, callingDistance, program), program);
// Call the library routine and clobber all registers.
// Possibly an option to be given that specific routines only clobber specific registers.
//asm.addInstruction("jsr", CpuAddressingMode.ABS, toProcedure.getLibrary() + "." + call.getProcedure().getFullName(), false);
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
}
}
// RValue procedureRVal = call.getProcedureRVal();
if (!(procedureRVal instanceof ProcedureRef)) {
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
}
@ -969,11 +992,47 @@ public class Pass4CodeGeneration {
}
}
}
/* author: sven.van.de.velde@telenet.be - 2023-03-21
The following logic ensures that inlined asm{} blocks placed in inline functions()
are replacing the used C constants or variables with the inlined version
of these C constants or variables.
During pass1, in the functions inlineStatement() and execute() in the Pass1ProcedureInline.c,
the asm fragment gets scanned for referenced constants or variables and the
referenced constant names get updated with the modified inlined referenced constant names.
These modified reference names are then used here in the asm generation, to scan the asm fragment,
of which the source is the bare, parsed antlr source, and really directly in the cut/pasted source
search for any constant or variable in operand1 and replace with the inlined reference
constant or variable name using the Operand1 structure.
This is the only pragmatic way I saw possible.
However, this section gets called during coalescing many, many times and
slows the compiler. However, such solution requires a complete redesign and cannot
just be put in scope to add this fix. So I hope that this change is acceptable.
*/
for (AsmLine asmLine : currentChunk.getLines()) {
if (asmLine instanceof AsmInstruction) {
AsmInstruction asmInstruction = (AsmInstruction) asmLine;
Map<String, SymbolRef> referenced = statementAsm.getReferenced();
for(String reference : referenced.keySet()) {
String operand = asmInstruction.getOperand1();
if(operand != null) {
String replace = referenced.get(reference).getLocalName();
if(operand.startsWith(replace)) {
} else {
if(operand.contains(reference)) {
operand = operand.replaceAll(reference, replace);
asmInstruction.setOperand1(operand);
}
}
}
}
}
}
} else if (statement instanceof StatementKickAsm) {
StatementKickAsm statementKasm = (StatementKickAsm) statement;
addKickAsm(asm, statementKasm);
AsmChunk currentChunk = asm.getCurrentChunk();
if (statementKasm.getDeclaredClobber() != null) {
asm.getCurrentChunk().setClobberOverwrite(statementKasm.getDeclaredClobber());
currentChunk.setClobberOverwrite(statementKasm.getDeclaredClobber());
}
} else if (statement instanceof StatementCallPointer) {
throw new InternalError("Statement not supported " + statement);

View File

@ -122,10 +122,13 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
for(VariableRef varRef : equivalenceClass.getVariables()) {
Variable variable = program.getScope().getVariable(varRef);
ScopeRef scopeRef = variable.getScope().getRef();
if(scopeRef.equals(ScopeRef.ROOT)) {
ProcedureRef mainThreadHead = program.getScope().getLocalProcedure(SymbolRef.MAIN_PROC_NAME).getRef();
if(!threads.contains(mainThreadHead)) {
threads.add(mainThreadHead);
if(scopeRef.equals(ScopeRef.ROOT) ) {
Procedure localProcedure = program.getScope().getLocalProcedure(SymbolRef.MAIN_PROC_NAME);
if(localProcedure != null && program.getAsmLibrary() != "") {
ProcedureRef mainThreadHead = localProcedure.getRef();
if(!threads.contains(mainThreadHead)) {
threads.add(mainThreadHead);
}
}
} else {
Collection<ScopeRef> recursiveCallers = callGraph.getRecursiveCallerProcs(scopeRef);

View File

@ -70,7 +70,7 @@ public class Pass4RegisterUpliftPotentialRegisterAnalysis extends Pass2Base {
RegisterPotentials registerPotentials = getProgram().getRegisterPotentials();
VariableReferenceInfos referenceInfo = getProgram().getVariableReferenceInfos();
for(var block : getProgram().getGraph().getAllBlocks()) {
for(var block : getProgram().getGraph().getAllBlocks()) {
for(Statement statement : block.getStatements()) {
// Find all variables referenced/assigned in the statement

View File

@ -29,7 +29,7 @@ public class PassNEliminateEmptyProcedure extends Pass2SsaOptimization {
boolean optimized = false;
for(Procedure procedure : allProcedures) {
if(procedure.isDeclaredIntrinsic()) continue;
if(hasEmptyBody(procedure.getRef())) {
if(hasEmptyBody(procedure.getRef()) && !procedure.isDeclaredExtern()) {
if(!hasExternalUsages(procedure.getRef(), getProgram()) && !SymbolRef.MAIN_PROC_NAME.equals(procedure.getLabel().getLocalName())) {
// TODO: Entry point procedures include isAddressOfUsed!
// Remove all calls

View File

@ -1,9 +1,7 @@
package dk.camelot64.kickc.passes.unwinding;
import dk.camelot64.kickc.model.Graph;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.InternalError;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.VariableBuilder;
import dk.camelot64.kickc.model.operators.Operators;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementAssignment;
@ -120,7 +118,7 @@ public class ValueSourcePointerDereferenceIndexed extends ValueSourceBase {
}
} else {
if(memberArraySpec != null)
throw new InternalError("Not implemented!");
throw new CompileError("Not implemented!", currentStmt);
Scope scope = programScope.getScope(currentBlock.getScope());
SymbolTypePointer type = new SymbolTypePointer(memberType);
Variable memberAddress = VariableBuilder.createIntermediate(scope, type, program);

View File

@ -193,7 +193,7 @@ public class TestPrograms {
ReferenceHelper helper = new ReferenceHelperFolder(refPath);
String baseFileName = FileNameUtils.removeExtension(fileName);
success &= helper.testOutput(baseFileName, ".asm", program.getAsm().toString(new AsmProgram.AsmPrintState(false, true, false, false), program));
success &= helper.testOutput(baseFileName, ".sym", program.getScope().toStringVars(program, false));
success &= helper.testOutput(baseFileName, ".sym", program.getScope().toStringVars(program, false, false));
success &= helper.testOutput(baseFileName, ".cfg", program.prettyControlFlowGraph());
success &= helper.testOutput(baseFileName, ".log", program.getLog().toString());
if(!success) {