1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-01-11 04:29:53 +00:00

Working in VariableBuilder for creating variables based on type/scope, directives and configuration.

This commit is contained in:
jespergravgaard 2019-12-22 18:19:20 +01:00
parent 4f7485f4e5
commit e103a783ce
6 changed files with 180 additions and 194 deletions

View File

@ -10,11 +10,84 @@ import dk.camelot64.kickc.model.values.*;
import java.util.*;
/** Utility methods for finding constant struct/array values from {@link dk.camelot64.kickc.model.values.ValueList}. */
public class ConstantValueLists {
/** Utility methods for initializing variables */
public class Initializers {
/**
* Add cast to a value inside a value list initializer based on the declared type of the symbol.
* Create a statement that initializes a variable with the default (zero) value. The statement has to be added to the program by the caller.
*
* @param type The type of the variable
* @param statementSource The source line
* @return The new statement
*/
public static RValue createZeroValue(SymbolType type, StatementSource statementSource) {
if(type instanceof SymbolTypeIntegerFixed) {
// Add an zero value initializer
return new ConstantInteger(0L, type);
} else if(type instanceof SymbolTypePointer) {
// Add an zero value initializer
SymbolTypePointer typePointer = (SymbolTypePointer) type;
return new ConstantPointer(0L, typePointer.getElementType());
} else if(type instanceof SymbolTypeStruct) {
// Add an zero-struct initializer
SymbolTypeStruct typeStruct = (SymbolTypeStruct) type;
return new StructZero(typeStruct);
} else {
throw new CompileError("Default initializer not implemented for type " + type.getTypeName(), statementSource);
}
}
/**
* Get a value for initializing a variable from an expression.
* If possible the value is converted to a ConstantValue.
*
* @param initValue The parsed init expression value (may be null)
* @param type The type of the constant variable (used for creating zero values)
* @param statementSource The statement (used in exceptions.
* @return The constant init-value. Null if the value cannot be turned into a constant init-value.
*/
public static RValue getInitValue(RValue initValue, SymbolType type, boolean isArray, ConstantValue arraySize, Program program, StatementSource statementSource) {
// TODO: Handle struct members
// Create zero-initializers if null
if(initValue == null) {
if(isArray) {
// Add an zero-filled array initializer
SymbolTypePointer typePointer = (SymbolTypePointer) type;
if(arraySize == null) {
throw new CompileError("Error! Array has no declared size. ", statementSource);
}
initValue = new ConstantArrayFilled(typePointer.getElementType(), arraySize);
} else {
// Add an zero-value
initValue = createZeroValue(type, statementSource);
}
}
// Convert initializer value lists to constant if possible
if((initValue instanceof ValueList)) {
ProgramValue programValue = new ProgramValue.GenericValue(initValue);
addValueCasts(type, isArray, programValue, program, statementSource);
if(programValue.get() instanceof CastValue) {
CastValue castValue = (CastValue) programValue.get();
if(castValue.getValue() instanceof ValueList) {
// Found value list with cast - look through all elements
ConstantValue constantValue = convertToConstant(castValue.getToType(), (ValueList) castValue.getValue(), program, statementSource);
if(constantValue != null) {
// Converted value list to constant!!
initValue = constantValue;
}
}
}
}
// Add pointer cast to integers
if(type instanceof SymbolTypePointer && initValue instanceof ConstantValue && SymbolType.isInteger(((ConstantValue) initValue).getType(program.getScope()))) {
initValue = new ConstantCastValue(type, (ConstantValue) initValue);
}
return initValue;
}
/**
* Add casts to a value based on the declared type of the symbol. Recurses to all sub-values.
*
* @param declaredType The declared type of the value
* @param isArray true if the declared variable is an array
@ -22,7 +95,7 @@ public class ConstantValueLists {
* @param source The current statement
* @return true if anything was modified
*/
public static boolean addValueCasts(SymbolType declaredType, boolean isArray, ProgramValue programValue, Program program, StatementSource source) {
static boolean addValueCasts(SymbolType declaredType, boolean isArray, ProgramValue programValue, Program program, StatementSource source) {
boolean exprModified = false;
Value value = programValue.get();
if(value instanceof ValueList) {
@ -103,7 +176,7 @@ public class ConstantValueLists {
* @param valueList The list of values
* @return The constant value if all list elements are constant. null if elements are not constant
*/
public static ConstantValue getConstantValue(SymbolType declaredType, ValueList valueList, Program program, StatementSource source) {
static ConstantValue convertToConstant(SymbolType declaredType, ValueList valueList, Program program, StatementSource source) {
// Examine whether all list elements are constant
List<RValue> values = valueList.getList();
List<ConstantValue> constantValues = new ArrayList<>();
@ -115,7 +188,7 @@ public class ConstantValueLists {
// Recursion may be needed
CastValue castValue = (CastValue) elmValue;
if(castValue.getValue() instanceof ValueList) {
ConstantValue constantValue = getConstantValue(castValue.getToType(), (ValueList) castValue.getValue(), program, source);
ConstantValue constantValue = convertToConstant(castValue.getToType(), (ValueList) castValue.getValue(), program, source);
if(constantValue != null) {
constantValues.add(constantValue);
} else {
@ -184,4 +257,5 @@ public class ConstantValueLists {
throw new InternalError("Not supported " + declaredType);
}
}
}

View File

@ -83,6 +83,10 @@ public class Registers {
return variableRef;
}
public void setVariableRef(VariableRef variableRef) {
this.variableRef = variableRef;
}
@Override
public RegisterType getType() {
return RegisterType.MAIN_MEM;

View File

@ -1,11 +1,7 @@
package dk.camelot64.kickc.model;
import dk.camelot64.kickc.model.statements.StatementSource;
import dk.camelot64.kickc.model.symbols.*;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypePointer;
import dk.camelot64.kickc.model.types.SymbolTypeStruct;
import dk.camelot64.kickc.model.values.ScopeRef;
import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.values.VariableRef;
import java.util.List;
@ -18,37 +14,6 @@ public class VariableBuilderContext {
public VariableBuilderContext() {
}
/**
* Find the variable kind for a declared variable (not used for intermediates or PHI-versions)
*
* @param type The type of the variable
* @param scope The scope
* @param isParameter true if the variable is a procedure parameter
* @param sourceDirectives The directives in the source code
* @param source The source line (for exceptions)
* @return The variable kind
*/
public Variable.Kind getKind(SymbolType type, Scope scope, boolean isParameter, boolean isArray, List<Directive> sourceDirectives, StatementSource source) {
// Look for const without volatile
if(hasDirective(Directive.Const.class, sourceDirectives))
if(!hasDirective(Directive.Volatile.class, sourceDirectives))
return Variable.Kind.CONSTANT;
// Look for array (which is implicitly const
if(isArray)
return Variable.Kind.CONSTANT;
// It is not a constant - determine PHI_MASTER vs LOAD_STORE
if(hasDirective(Directive.FormSsa.class, sourceDirectives))
return Variable.Kind.PHI_MASTER;
if(hasDirective(Directive.FormMa.class, sourceDirectives))
return Variable.Kind.LOAD_STORE;
if(hasDirective(Directive.Register.class, sourceDirectives))
return Variable.Kind.PHI_MASTER;
if(hasDirective(Directive.NamedRegister.class, sourceDirectives))
return Variable.Kind.PHI_MASTER;
// TODO: Add strategy for selecting LOAD_STORE for global vars
return Variable.Kind.PHI_MASTER;
}
/**
* Applies directives to a variable.
*

View File

@ -3,13 +3,13 @@ package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.NumberParser;
import dk.camelot64.kickc.SourceLoader;
import dk.camelot64.kickc.asm.AsmClobber;
import dk.camelot64.kickc.model.InternalError;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.iterator.ProgramValue;
import dk.camelot64.kickc.model.operators.*;
import dk.camelot64.kickc.model.statements.*;
import dk.camelot64.kickc.model.symbols.*;
import dk.camelot64.kickc.model.types.*;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypePointer;
import dk.camelot64.kickc.model.types.SymbolTypeProcedure;
import dk.camelot64.kickc.model.values.*;
import dk.camelot64.kickc.parser.CParser;
import dk.camelot64.kickc.parser.KickCParser;
@ -565,77 +565,69 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
@Override
public Object visitDeclVariableInitExpr(KickCParser.DeclVariableInitExprContext ctx) {
StatementSource statementSource = new StatementSource(ctx);
String varName = ctx.NAME().getText();
KickCParser.ExprContext initializer = ctx.expr();
ArraySpec arraySpec = null;
if(declIsArray) {
arraySpec = new ArraySpec(declArraySize);
}
if(initializer != null)
PrePostModifierHandler.addPreModifiers(this, initializer, statementSource);
RValue initValue = (initializer == null) ? null : (RValue) visit(initializer);
initValue = getInitValue(initValue, declVarType, declIsArray, declArraySize, program, statementSource);
VariableBuilder varBuilder = new VariableBuilder(getCurrentScope(), false, declVarType, arraySpec, declVarDirectives);
try {
// Find kind
Variable.Kind kind = directiveContext.getKind(declVarType, getCurrentScope(), false, declIsArray, declVarDirectives, statementSource);
if(kind.equals(Variable.Kind.CONSTANT)) {
// Create a Constant
ConstantValue initConstantValue = getConstantValue(initializer, declVarType, declVarStructMember, statementSource);
String varName = ctx.NAME().getText();
KickCParser.ExprContext initializer = ctx.expr();
ArraySpec arraySpec = null;
if(declIsArray) {
arraySpec = new ArraySpec(declArraySize);
}
if(declVarStructMember && (initializer != null))
throw new CompileError("Initializer not supported inside structs " + declVarType.getTypeName(), statementSource);
if(initializer != null)
PrePostModifierHandler.addPreModifiers(this, initializer, statementSource);
RValue initValue = (initializer == null) ? null : (RValue) visit(initializer);
initValue = Initializers.getInitValue(initValue, declVarType, declIsArray, declArraySize, program, statementSource);
VariableBuilder varBuilder = new VariableBuilder(getCurrentScope(), false, declVarType, arraySpec, declVarDirectives);
if(varBuilder.isConstant()) {
Scope scope = getCurrentScope();
Variable constVar = Variable.createConstant(varName, declVarType, scope, arraySpec, initConstantValue, currentDataSegment);
ConstantValue constInitValue = getConstInitValue(initValue, initializer, statementSource);
Variable constVar = Variable.createConstant(varName, declVarType, scope, arraySpec, constInitValue, currentDataSegment);
constVar.setDeclaredConst(varBuilder.isConstant());
constVar.setDeclaredVolatile(varBuilder.isVolatile());
constVar.setDeclaredExport(varBuilder.isExport());
constVar.setDeclaredAsRegister(varBuilder.isOptimize());
constVar.setDeclaredRegister(varBuilder.getRegister());
constVar.setMemoryArea(varBuilder.getMemoryArea());
constVar.setDeclaredAlignment(varBuilder.getAlignment());
scope.add(constVar);
// Add comments to constant
constVar.setComments(ensureUnusedComments(declVarComments));
directiveContext.applyDirectives(constVar, false, declIsArray, declVarDirectives, statementSource);
assert varBuilder.getKind().equals(constVar.getKind());
assert varBuilder.isArray() == constVar.isArray();
assert varBuilder.isVolatile() == constVar.isDeclaredVolatile();
assert varBuilder.isOptimize() == constVar.isDeclaredAsRegister();
assert varBuilder.isExport() == constVar.isDeclaredExport();
assert varBuilder.isConstant() == constVar.isDeclaredConst();
assert varBuilder.getMemoryArea() == constVar.getMemoryArea();
assert Objects.equals(varBuilder.getAlignment(), constVar.getDeclaredAlignment());
assert Objects.equals(varBuilder.getRegister(), constVar.getDeclaredRegister());
} else {
// Create variable
Variable lValue;
if(kind.equals(Variable.Kind.PHI_MASTER)) {
lValue = Variable.createPhiMaster(varName, declVarType, getCurrentScope(), defaultMemoryArea, currentDataSegment);
} else if(kind.equals(Variable.Kind.LOAD_STORE)) {
lValue = Variable.createLoadStore(varName, declVarType, getCurrentScope(), defaultMemoryArea, currentDataSegment);
} else {
throw new InternalError("Unexpected variable kind! " + kind.name(), statementSource);
} else if(varBuilder.isSingleStaticAssignment()) {
// Create SSA PHI-master variable
Variable lValue = Variable.createPhiMaster(varName, declVarType, getCurrentScope(), defaultMemoryArea, currentDataSegment);
lValue.setDeclaredConst(varBuilder.isConstant());
lValue.setDeclaredVolatile(varBuilder.isVolatile());
lValue.setDeclaredExport(varBuilder.isExport());
lValue.setDeclaredAsRegister(varBuilder.isOptimize());
lValue.setDeclaredRegister(varBuilder.getRegister());
if(lValue.getDeclaredRegister() instanceof Registers.RegisterMainMem) {
((Registers.RegisterMainMem) lValue.getDeclaredRegister()).setVariableRef(lValue.getVariableRef());
}
lValue.setMemoryArea(varBuilder.getMemoryArea());
lValue.setDeclaredAlignment(varBuilder.getAlignment());
getCurrentScope().add(lValue);
// Add directives
directiveContext.applyDirectives(lValue, false, declIsArray, declVarDirectives, statementSource);
if(declVarStructMember) {
if(initializer != null) {
throw new CompileError("Initializer not supported inside structs " + declVarType.getTypeName(), statementSource);
}
} else {
if(initializer == null) {
initValue = createZeroValue(declVarType, statementSource);
}
if(!declVarStructMember) {
Statement initStmt = new StatementAssignment(lValue.getVariableRef(), initValue, statementSource, ensureUnusedComments(declVarComments));
sequence.addStatement(initStmt);
}
} else {
Variable lValue = Variable.createLoadStore(varName, declVarType, getCurrentScope(), defaultMemoryArea, currentDataSegment);
lValue.setDeclaredConst(varBuilder.isConstant());
lValue.setDeclaredVolatile(varBuilder.isVolatile());
lValue.setDeclaredExport(varBuilder.isExport());
lValue.setDeclaredAsRegister(varBuilder.isOptimize());
lValue.setDeclaredRegister(varBuilder.getRegister());
if(lValue.getDeclaredRegister() instanceof Registers.RegisterMainMem) {
((Registers.RegisterMainMem) lValue.getDeclaredRegister()).setVariableRef(lValue.getVariableRef());
}
lValue.setMemoryArea(varBuilder.getMemoryArea());
lValue.setDeclaredAlignment(varBuilder.getAlignment());
getCurrentScope().add(lValue);
if(!declVarStructMember) {
Statement initStmt = new StatementAssignment(lValue.getVariableRef(), initValue, statementSource, ensureUnusedComments(declVarComments));
sequence.addStatement(initStmt);
}
assert varBuilder.getKind().equals(lValue.getKind());
assert varBuilder.isArray() == lValue.isArray();
assert varBuilder.isVolatile() == lValue.isDeclaredVolatile();
assert varBuilder.isOptimize() == lValue.isDeclaredAsRegister();
assert varBuilder.isExport() == lValue.isDeclaredExport();
assert varBuilder.isConstant() == lValue.isDeclaredConst();
assert varBuilder.getMemoryArea() == lValue.getMemoryArea();
assert Objects.equals(varBuilder.getAlignment(), lValue.getDeclaredAlignment());
if(varBuilder.getRegister() instanceof Registers.RegisterMainMem) {
assert Objects.equals(varBuilder.getRegister().getType(), lValue.getDeclaredRegister().getType());
assert Objects.equals(varBuilder.getRegister().getBytes(), lValue.getDeclaredRegister().getBytes());
assert Objects.equals(((Registers.RegisterMainMem) varBuilder.getRegister()).getAddress(), ((Registers.RegisterMainMem) lValue.getDeclaredRegister()).getAddress());
} else
assert Objects.equals(varBuilder.getRegister(), lValue.getDeclaredRegister());
}
if(initializer != null)
PrePostModifierHandler.addPostModifiers(this, initializer, statementSource);
@ -646,77 +638,23 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
}
/**
* Get a constant value from an initializer
* Ensure that tha initializer value is a constant. Fail if it is not
*
* @param initializer The initializer (may be null)
* @param type The type of the constant variable (used for creating zero values)
* @param statementSource The statement (used in exceptions.
* @return The constant value.
* @throws CompileError if the initializer does not resolve to a constant value
* @param initValue The initializer value (result from {@link Initializers#getInitValue(RValue, SymbolType, boolean, ConstantValue, Program, StatementSource)})
* @param initializer The initializer
* @param statementSource The source line
* @return The constant initializer value
*/
private ConstantValue getConstantValue(KickCParser.ExprContext initializer, SymbolType type, boolean isStructMember, StatementSource statementSource) {
private ConstantValue getConstInitValue(RValue initValue, KickCParser.ExprContext initializer, StatementSource statementSource) {
if(initializer != null && PrePostModifierHandler.hasPrePostModifiers(this, initializer, statementSource)) {
throw new CompileError("Constant value contains a pre/post-modifier.", statementSource);
}
RValue initValue = null;
if(initializer != null)
initValue = (RValue) visit(initializer);
if(initValue instanceof ForwardVariableRef) {
throw new CompileError("Variable used before being defined " + initValue.toString(), statementSource);
}
RValue constInitValue = getInitValue(initValue, type, declIsArray, declArraySize, program, statementSource);
if(constInitValue instanceof ConstantValue)
return (ConstantValue) constInitValue;
else
return null;
}
/**
* Get a value for initializing a variable from an expression.
* If possible the value is converted to a ConstantValue.
*
* @param initValue The parsed init expression value (may be null)
* @param type The type of the constant variable (used for creating zero values)
* @param statementSource The statement (used in exceptions.
* @return The constant init-value. Null if the value cannot be turned into a constant init-value.
*/
private static RValue getInitValue(RValue initValue, SymbolType type, boolean isArray, ConstantValue arraySize, Program program, StatementSource statementSource) {
// TODO: Handle struct members
// Create zero-initializers if null
if(initValue == null) {
if(isArray) {
// Add an zero-filled array initializer
SymbolTypePointer typePointer = (SymbolTypePointer) type;
if(arraySize == null) {
throw new CompileError("Error! Array has no declared size. ", statementSource);
}
initValue = new ConstantArrayFilled(typePointer.getElementType(), arraySize);
} else {
// Add an zero-value
initValue = createZeroValue(type, statementSource);
}
}
// Convert initializer value lists to constant if possible
if((initValue instanceof ValueList)) {
ProgramValue programValue = new ProgramValue.GenericValue(initValue);
ConstantValueLists.addValueCasts(type, isArray, programValue, program, statementSource);
if(programValue.get() instanceof CastValue) {
CastValue castValue = (CastValue) programValue.get();
if(castValue.getValue() instanceof ValueList) {
// Found value list with cast - look through all elements
ConstantValue constantValue = ConstantValueLists.getConstantValue(castValue.getToType(), (ValueList) castValue.getValue(), program, statementSource);
if(constantValue != null) {
// Converted value list to constant!!
initValue = constantValue;
}
}
}
}
// Add pointer cast to integers
if(type instanceof SymbolTypePointer && initValue instanceof ConstantValue && SymbolType.isInteger(((ConstantValue) initValue).getType(program.getScope()))) {
initValue = new ConstantCastValue(type, (ConstantValue) initValue);
}
return initValue;
if(!(initValue instanceof ConstantValue))
throw new CompileError("Initializer is not a constant value " + initValue.toString(), statementSource);
return (ConstantValue) initValue;
}
@Override
@ -757,30 +695,6 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
return null;
}
/**
* Create a statement that initializes a variable with the default (zero) value. The statement has to be added to the program by the caller.
*
* @param type The type of the variable
* @param statementSource The source line
* @return The new statement
*/
static RValue createZeroValue(SymbolType type, StatementSource statementSource) {
if(type instanceof SymbolTypeIntegerFixed) {
// Add an zero value initializer
return new ConstantInteger(0L, type);
} else if(type instanceof SymbolTypePointer) {
// Add an zero value initializer
SymbolTypePointer typePointer = (SymbolTypePointer) type;
return new ConstantPointer(0L, typePointer.getElementType());
} else if(type instanceof SymbolTypeStruct) {
// Add an zero-struct initializer
SymbolTypeStruct typeStruct = (SymbolTypeStruct) type;
return new StructZero(typeStruct);
} else {
throw new CompileError("Default initializer not implemented for type " + type.getTypeName(), statementSource);
}
}
/**
* Find the directives in the parse tree
*

View File

@ -248,7 +248,7 @@ public class Pass1UnwindStructValues extends Pass1Base {
membersUnwound.add(memberVarRef);
Variable memberVar = getScope().getVariable(memberVarRef);
StatementSource statementSource = assignment.getSource();
RValue initValue = Pass0GenerateStatementSequence.createZeroValue(memberVar.getType(), statementSource);
RValue initValue = Initializers.createZeroValue(memberVar.getType(), statementSource);
Statement initStmt = new StatementAssignment((LValue) memberVarRef, initValue, statementSource, Comment.NO_COMMENTS);
stmtIt.add(initStmt);
getLog().append("Adding struct value member variable default initializer " + initStmt.toString(getProgram(), false));

View File

@ -0,0 +1,29 @@
(label) @1
(label) @begin
(label) @end
(const byte*) SCREEN = (byte*) 1024
(const byte*) henry_initials[(number) $40] = (string) "hg"
(byte) idx
(byte) idx#11 reg byte x 9.75
(byte) idx#13 reg byte x 1.0
(byte) idx#18 reg byte x 4.0
(byte) idx#4 reg byte x 11.0
(const byte*) jesper_initials[(number) $40] = (string) "jg"
(void()) main()
(label) main::@1
(label) main::@return
(void()) print_person((dword) print_person::person_id , (byte*) print_person::person_initials)
(label) print_person::@1
(label) print_person::@2
(label) print_person::@3
(label) print_person::@return
(byte) print_person::i
(byte) print_person::i#1 reg byte y 22.0
(byte) print_person::i#2 reg byte y 11.0
(dword) print_person::person_id
(byte*) print_person::person_initials
(byte*) print_person::person_initials#4 person_initials zp[2]:2 3.6666666666666665
zp[2]:2 [ print_person::person_initials#4 ]
reg byte y [ print_person::i#2 print_person::i#1 ]
reg byte x [ idx#11 idx#18 idx#13 idx#4 ]