2017-07-18 16:10:12 +00:00
package dk.camelot64.kickc.passes ;
2017-05-15 15:01:11 +00:00
2020-07-28 20:40:24 +00:00
import dk.camelot64.cpufamily6502.CpuAddressingMode ;
import dk.camelot64.cpufamily6502.CpuClobber ;
2017-05-21 08:56:26 +00:00
import dk.camelot64.kickc.asm.* ;
2017-12-30 20:59:23 +00:00
import dk.camelot64.kickc.fragment.* ;
2021-12-28 13:41:00 +00:00
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentTemplateSynthesizer ;
2019-06-30 21:08:39 +00:00
import dk.camelot64.kickc.model.InternalError ;
2019-11-18 21:02:29 +00:00
import dk.camelot64.kickc.model.* ;
2018-07-21 09:13:32 +00:00
import dk.camelot64.kickc.model.operators.Operators ;
2018-03-06 19:54:49 +00:00
import dk.camelot64.kickc.model.statements.* ;
2018-08-05 11:33:23 +00:00
import dk.camelot64.kickc.model.symbols.* ;
2021-12-27 00:16:04 +00:00
import dk.camelot64.kickc.model.types.SymbolType ;
import dk.camelot64.kickc.model.types.SymbolTypeIntegerFixed ;
import dk.camelot64.kickc.model.types.SymbolTypePointer ;
import dk.camelot64.kickc.model.types.SymbolTypeStruct ;
2018-07-25 18:44:10 +00:00
import dk.camelot64.kickc.model.values.* ;
2019-08-03 23:50:00 +00:00
import dk.camelot64.kickc.passes.calcs.PassNCalcVariableReferenceInfos ;
2020-02-06 18:22:56 +00:00
import dk.camelot64.kickc.passes.utils.SizeOfConstants ;
2017-05-20 19:49:11 +00:00
2023-12-16 12:45:38 +00:00
import java.io.File ;
2017-07-13 22:15:33 +00:00
import java.util.* ;
2017-06-03 22:45:40 +00:00
2017-05-15 15:01:11 +00:00
/ * *
* Code Generation of 6502 Assembler from ICL / SSA Control Flow Graph
* /
2017-08-12 22:30:51 +00:00
public class Pass4CodeGeneration {
2017-05-15 15:01:11 +00:00
2021-12-27 00:16:04 +00:00
/ * *
* Should the generated ASM contain verbose alive info for the statements ( costs a bit more to generate ) .
* /
boolean verboseAliveInfo ;
/ * *
* Missing fragments produce a warning instead of an error .
* /
boolean warnFragmentMissing ;
/ * *
* The program being generated .
* /
private final Program program ;
/ * *
* Keeps track of the phi transitions into blocks during code generation .
* Used to ensure that duplicate transitions are only code generated once .
* Maps to - blocks to the transition information for the block
* /
private final Map < PhiTransitions . PhiTransition , Boolean > transitionsGenerated = new LinkedHashMap < > ( ) ;
/ * *
* Determines if a phi - transition has already been code - generated
*
* @param transition The transition to examine
* @return true if it has already been generated
* /
private boolean transitionIsGenerated ( PhiTransitions . PhiTransition transition ) {
return Boolean . TRUE . equals ( transitionsGenerated . get ( transition ) ) ;
}
/ * *
* Mark a Phi transition as generated
*
* @param transition The transition
* /
private void transitionSetGenerated ( PhiTransitions . PhiTransition transition ) {
transitionsGenerated . put ( transition , Boolean . TRUE ) ;
}
public Pass4CodeGeneration ( Program program , boolean verboseAliveInfo , boolean warnFragmentMissing ) {
this . program = program ;
this . verboseAliveInfo = verboseAliveInfo ;
this . warnFragmentMissing = warnFragmentMissing ;
}
2023-04-08 20:52:15 +00:00
Graph getGraph ( ) {
2021-12-27 00:16:04 +00:00
return program . getGraph ( ) ;
}
ProgramScope getScope ( ) {
return program . getScope ( ) ;
}
2023-12-14 19:44:03 +00:00
public boolean generateProcedure ( Procedure procedure ) {
if ( ! procedure . isDeclaredExtern ( ) ) {
/ * In search for further source code library or object file filtering .
String primaryCFile = program . getOutputFileManager ( ) . getPrimaryCFile ( ) . getFileName ( ) . toString ( ) ;
if ( procedure . getDefinitionSource ( ) ! = null ) {
if ( procedure . getDefinitionSource ( ) . getFileName ( ) . equals ( primaryCFile ) | | procedure . isAsmExportLibrary ( ) ) {
return true ;
}
} else {
return true ;
}
* /
return true ;
}
return false ;
}
2021-12-27 00:16:04 +00:00
public void generate ( ) {
AsmProgram asm = new AsmProgram ( program . getTargetCpu ( ) ) ;
2024-01-01 15:50:19 +00:00
String asmLibraryName = program . getAsmLibraryName ( ) ;
2021-12-27 00:16:04 +00:00
ScopeRef currentScope = ScopeRef . ROOT ;
2024-04-19 13:55:24 +00:00
asm . startChunk ( currentScope , null , null ) ;
2023-12-30 19:33:13 +00:00
// 820/25 - #import .asm libraries only once!
asm . addImportOnce ( ) ;
2021-12-27 00:16:04 +00:00
// Add file level comments
asm . startChunk ( currentScope , null , " File Comments " ) ;
generateComments ( asm , program . getMainFileComments ( ) ) ;
2023-04-22 12:58:33 +00:00
2023-12-30 19:33:13 +00:00
// #820/24 - Create a namespace if the output of the compile is an .asm library instead of a program.
2024-01-01 15:50:19 +00:00
if ( program . getAsmLibraryName ( ) ! = null ) {
2023-04-22 12:58:33 +00:00
asm . startChunk ( currentScope , null , " Library " ) ;
2024-04-19 13:55:24 +00:00
asm . addNamespaceBegin ( program ) ;
2023-04-22 12:58:33 +00:00
}
2021-12-27 00:16:04 +00:00
asm . startChunk ( currentScope , null , " Upstart " ) ;
2023-04-22 12:58:33 +00:00
2021-12-27 00:16:04 +00:00
final TargetPlatform targetPlatform = program . getTargetPlatform ( ) ;
// Add Target CPU - if it is not the default
final TargetCpu targetCpu = program . getTargetCpu ( ) ;
if ( ! targetCpu . equals ( TargetCpu . DEFAULT ) ) asm . addLine ( new AsmSetCpu ( targetCpu ) ) ;
2023-12-16 12:45:38 +00:00
File linkScriptFile = targetPlatform . getLinkScriptFile ( ) ;
String linkScriptBody = " " ;
2024-01-01 15:50:19 +00:00
if ( ( asmLibraryName = = null ) | | ( ( asmLibraryName ! = null ) & & ( ! targetPlatform . isDefaultLinkScriptFile ( ) ) ) ) {
2023-12-16 12:45:38 +00:00
linkScriptBody + = targetPlatform . getLinkScriptBody ( ) ;
2023-12-12 07:20:06 +00:00
String outputFileName = program . getOutputFileManager ( ) . getBinaryOutputFile ( ) . getFileName ( ) . toString ( ) ;
linkScriptBody = linkScriptBody . replace ( " %O " , outputFileName ) ;
linkScriptBody = linkScriptBody . replace ( " %_O " , outputFileName . toLowerCase ( ) ) ;
linkScriptBody = linkScriptBody . replace ( " %^O " , outputFileName . toUpperCase ( ) ) ;
String entryName = program . getStartProcedure ( ) . getFullName ( ) ;
linkScriptBody = linkScriptBody . replace ( " %E " , entryName ) ;
Number startAddress = program . getTargetPlatform ( ) . getStartAddress ( ) ;
if ( startAddress ! = null )
linkScriptBody = linkScriptBody . replace ( " %P " , AsmFormat . getAsmNumber ( startAddress ) ) ;
} else {
// #820/31 - Generate Code and Data segment stub for .asm libraries,
// to have a Code and Data segment defined when exporting, but not when importing.
2023-12-16 12:45:38 +00:00
// This standard import will only get executed when there isn't a link script.
linkScriptBody + = " \ n " +
2023-12-12 07:20:06 +00:00
" .segmentdef Code \ n " +
2024-01-01 15:50:19 +00:00
" .segmentdef Data \ n " ;
}
if ( asmLibraryName ! = null ) {
asm . addIgnoredImportInlinedKickAsm ( asmLibraryName , linkScriptBody , 0L , 0L , CpuClobber . CLOBBER_ALL ) ;
} else {
asm . addInlinedKickAsm ( linkScriptBody , 0L , 0L , CpuClobber . CLOBBER_ALL ) ;
2023-12-12 07:20:06 +00:00
}
2021-12-27 00:16:04 +00:00
// Generate global ZP labels
asm . startChunk ( currentScope , null , " Global Constants & labels " ) ;
2024-01-02 18:41:03 +00:00
addConstantsAndLabels ( asm , currentScope , null ) ;
2023-12-30 19:33:13 +00:00
2023-04-22 12:58:33 +00:00
Procedure oldProcedure = null ;
2023-04-08 20:52:15 +00:00
for ( Graph . Block block : getGraph ( ) . getAllBlocks ( ) ) {
2023-12-14 19:44:03 +00:00
/ * * # 820 - Reworked the code generation , avoiding code to be generated
2023-04-22 12:58:33 +00:00
* for procedures which are external . These must be included through linkage .
2023-12-14 19:44:03 +00:00
* Also , code that is not part of the main library , is not generated .
2023-04-22 12:58:33 +00:00
* /
2023-10-27 13:31:51 +00:00
Procedure procedure = program . getProcedure ( block ) ;
2021-12-27 00:16:04 +00:00
if ( ! block . getScope ( ) . equals ( currentScope ) ) {
2023-10-27 13:31:51 +00:00
2023-12-14 19:44:03 +00:00
if ( oldProcedure ! = null & & generateProcedure ( oldProcedure ) ) {
2023-04-22 12:58:33 +00:00
// The current block is in a different scope. End the old scope.
generateScopeEnding ( asm , currentScope ) ;
}
2021-12-27 00:16:04 +00:00
currentScope = block . getScope ( ) ;
2023-04-22 12:58:33 +00:00
oldProcedure = procedure ;
2023-12-14 19:44:03 +00:00
if ( generateProcedure ( procedure ) ) {
if ( program . isProcedureEntry ( block ) ) {
currentCodeSegmentName = procedure . getSegmentCode ( ) ;
}
// Set segment and procedure label.
2023-10-27 13:31:51 +00:00
setCurrentSegment ( currentCodeSegmentName , asm ) ;
asm . startChunk ( currentScope , null , block . getLabel ( ) . getFullName ( ) ) ;
2023-12-14 19:44:03 +00:00
// Add any procedure comments
if ( program . isProcedureEntry ( block ) ) {
generateComments ( asm , procedure . getComments ( ) ) ;
// Generate parameter information
generateSignatureComments ( asm , procedure ) ;
}
2023-10-27 13:31:51 +00:00
// Start the new scope
asm . addScopeBegin ( AsmFormat . asmFix ( block . getLabel ( ) . getFullName ( ) ) ) ;
// Add all ZP labels for the scope
2024-01-02 18:41:03 +00:00
addConstantsAndLabels ( asm , currentScope , null ) ;
2023-10-27 13:31:51 +00:00
}
2019-02-16 22:23:55 +00:00
}
2018-08-10 10:46:44 +00:00
2023-12-14 19:44:03 +00:00
if ( generateProcedure ( procedure ) ) {
2023-10-27 13:31:51 +00:00
generateComments ( asm , block . getComments ( ) ) ;
// Generate entry points (if needed)
genBlockEntryPoints ( asm , block ) ;
2018-08-10 10:46:44 +00:00
2023-10-27 13:31:51 +00:00
if ( program . isProcedureEntry ( block ) ) {
// Generate interrupt entry if needed
if ( procedure ! = null & & procedure . getInterruptType ( ) ! = null ) {
generateInterruptEntry ( asm , procedure ) ;
}
} else {
// Generate label for block inside procedure
asm . startChunk ( currentScope , null , block . getLabel ( ) . getFullName ( ) ) ;
asm . addLabel ( AsmFormat . asmFix ( block . getLabel ( ) . getLocalName ( ) ) ) ;
2021-12-27 00:16:04 +00:00
}
2023-10-27 13:31:51 +00:00
// 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 ) ;
}
2021-12-27 00:16:04 +00:00
} else {
2023-10-27 13:31:51 +00:00
String label = AsmFormat . asmFix ( defaultSuccessor . getLabel ( ) . getLocalName ( ) ) ;
2021-12-27 00:16:04 +00:00
asm . addInstruction ( " JMP " , CpuAddressingMode . ABS , label , false ) ;
}
}
}
}
2023-11-29 13:15:52 +00:00
// 820/24 - Scope ending of the normal assembler and/or asm library export generation.
2023-12-14 19:44:03 +00:00
if ( oldProcedure ! = null & & generateProcedure ( oldProcedure ) ) {
2023-11-29 13:15:52 +00:00
// The current block is in a different scope. End the old scope.
generateScopeEnding ( asm , currentScope ) ;
}
2021-12-27 00:16:04 +00:00
2024-04-19 13:55:24 +00:00
if ( program . getAsmLibraryName ( ) = = null ) {
asm . startChunk ( ScopeRef . ROOT , null , " File Data Internal or Ignore " ) ;
addData ( asm , ScopeRef . ROOT , null ) ;
addAbsoluteAddressData ( asm , ScopeRef . ROOT , null ) ;
} else {
// #820/34 - Export the global data, but when the compilation is an .asm export library.
asm . startChunk ( ScopeRef . ROOT , null , " Exported Global Data " ) ;
2024-01-02 18:41:03 +00:00
// Add the global data elements for the ROOT scope of the .asm library.
addData ( asm , ScopeRef . ROOT , program . getAsmLibraryName ( ) ) ;
addAbsoluteAddressData ( asm , ScopeRef . ROOT , program . getAsmLibraryName ( ) ) ;
2024-04-19 13:55:24 +00:00
// #820/24 - Close the namespace of the asm library.
asm . addNamespaceEnd ( program ) ;
2024-01-02 18:41:03 +00:00
}
2024-01-01 15:50:19 +00:00
2024-04-19 13:55:24 +00:00
// #820/25 - .asm library #import stub code.
generateImportAsmLibraries ( asm , program ) ;
2023-11-18 09:40:50 +00:00
2021-12-27 00:16:04 +00:00
program . setAsm ( asm ) ;
}
// Name of the current data segment
2023-10-27 13:31:51 +00:00
String currentCodeSegmentName = Scope . SEGMENT_CODE_DEFAULT ;
2021-12-27 00:16:04 +00:00
// Name of the current code segment
2023-10-27 13:31:51 +00:00
final String currentDataSegmentName = Scope . SEGMENT_DATA_DEFAULT ;
2021-12-27 00:16:04 +00:00
// Name of the current active segment
2023-10-27 13:31:51 +00:00
String currentSegmentName = " " ;
2021-12-27 00:16:04 +00:00
/ * *
* Set the current ASM segment - if needed
*
* @param segmentName The segment name we want
* @param asm The ASM program ( where a . segment line is added if needed )
* /
private void setCurrentSegment ( String segmentName , AsmProgram asm ) {
if ( ! currentSegmentName . equals ( segmentName ) ) {
asm . addLine ( new AsmSegment ( segmentName ) ) ;
currentSegmentName = segmentName ;
}
}
/ * *
* Counter used to separate indirect calls
* /
private int indirectCallCount = 1 ;
/ * *
* Generate the end of a scope
*
* @param asm The assembler program being generated
* @param currentScope The current scope , which is ending here
* /
private void generateScopeEnding ( AsmProgram asm , ScopeRef currentScope ) {
if ( ! ScopeRef . ROOT . equals ( currentScope ) ) {
if ( asm . hasStash ( ) ) asm . startChunk ( currentScope , null , " Outside Flow " ) ;
// Generate all stashed ASM lines
asm . addStash ( ) ;
2024-04-19 13:55:24 +00:00
addData ( asm , currentScope , program . getAsmLibraryName ( ) ) ;
2021-12-27 00:16:04 +00:00
asm . addScopeEnd ( ) ;
}
}
/ * *
2023-04-12 04:57:25 +00:00
* Generate a comment that describes the procedure signature and parameter transfer .
2021-12-27 00:16:04 +00:00
*
* @param asm The assembler program being generated
* @param procedure The procedure
* /
private void generateSignatureComments ( AsmProgram asm , Procedure procedure ) {
StringBuilder signature = new StringBuilder ( ) ;
signature . append ( " " ) ;
generateSignatureVar ( procedure . getLocalVar ( " return " ) , procedure , signature ) ;
signature . append ( procedure . getReturnType ( ) . toCDecl ( ) ) ;
signature . append ( " " ) . append ( procedure . getLocalName ( ) ) . append ( " ( " ) ;
int i = 0 ;
for ( Variable parameter : procedure . getParameters ( ) ) {
if ( i + + > 0 ) signature . append ( " , " ) ;
Variable param = generateSignatureVar ( parameter , procedure , signature ) ;
signature . append ( param . getType ( ) . toCDecl ( parameter . getLocalName ( ) ) ) ;
}
signature . append ( " ) " ) ;
2023-11-18 09:40:50 +00:00
// Always add the signature comments...
asm . addComment ( signature . toString ( ) , false ) ;
2023-04-23 20:04:23 +00:00
if ( ! procedure . getBank ( ) . isCommon ( ) ) {
2023-04-23 09:54:47 +00:00
asm . addComment ( " " + procedure . getBank ( ) , false ) ;
2023-04-12 04:57:25 +00:00
}
2021-12-27 00:16:04 +00:00
}
/ * *
* Generate part of a comment that describes a returnvalue / parameter
*
* @param param The variable to describe
* @param scope The scope ( procedure )
* @param signature The signature to append to
* @return The version of the variable chosen
* /
Variable generateSignatureVar ( Variable param , Scope scope , StringBuilder signature ) {
if ( param = = null ) return param ;
if ( param . isKindPhiMaster ( ) ) {
List < Variable > versions = new ArrayList < > ( scope . getVersions ( param ) ) ;
if ( versions . size ( ) > 0 ) if ( param . getLocalName ( ) . equals ( " return " ) ) {
// Choose the last version for return values
param = versions . get ( versions . size ( ) - 1 ) ;
2021-08-10 15:48:55 +00:00
} else {
2021-12-27 00:16:04 +00:00
// Choose the first version for parameters
param = versions . get ( 0 ) ;
2021-08-10 15:48:55 +00:00
}
2021-12-27 00:16:04 +00:00
else
// Parameter optimized away to a constant or unused
return param ;
}
Registers . Register allocation = param . getAllocation ( ) ;
if ( allocation instanceof Registers . RegisterZpMem ) {
Registers . RegisterZpMem registerZp = ( Registers . RegisterZpMem ) allocation ;
signature . append ( " __zp( " ) . append ( AsmFormat . getAsmNumber ( registerZp . getZp ( ) ) ) . append ( " ) " ) ;
} else if ( allocation instanceof Registers . RegisterMainMem ) {
Registers . RegisterMainMem registerMainMem = ( Registers . RegisterMainMem ) allocation ;
signature . append ( " __mem( " ) . append ( registerMainMem . getAddress ( ) = = null ? " " : AsmFormat . getAsmNumber ( registerMainMem . getAddress ( ) ) ) . append ( " ) " ) ;
} else if ( allocation instanceof Registers . RegisterAByte ) {
signature . append ( " __register(A) " ) ;
} else if ( allocation instanceof Registers . RegisterXByte ) {
signature . append ( " __register(X) " ) ;
} else if ( allocation instanceof Registers . RegisterYByte ) {
signature . append ( " __register(Y) " ) ;
} else if ( allocation instanceof Registers . RegisterZByte ) {
signature . append ( " __register(Z) " ) ;
}
return param ;
}
/ * *
* Add comments to the assembler program
*
* @param asm The assembler program
* @param comments The comments to add
* /
private void generateComments ( AsmProgram asm , List < Comment > comments ) {
for ( Comment comment : comments ) {
asm . addComment ( comment . getComment ( ) , comment . isBlock ( ) ) ;
}
}
2023-12-30 19:33:13 +00:00
/ * *
* 820 / 25 - Generate the # import stub kickasm pre - processor code ,
* to # import any . asm library used in the main program .
* Inside the . asm library code , the # define will be # undef - ined to ensure
* the . asm library is # import - ed only once .
*
* @param asm The assembler source code being generated .
* @param program The main program .
* /
private void generateImportAsmLibraries ( AsmProgram asm , Program program ) {
2024-04-26 09:40:14 +00:00
for ( AsmImportLibrary asmImportLibrary : program . getAsmImportLibraries ( ) . values ( ) ) {
2024-01-01 15:50:19 +00:00
asm . addAsmImportLibrary ( asmImportLibrary ) ;
2023-12-30 19:33:13 +00:00
}
}
2024-01-01 15:50:19 +00:00
private boolean hasExportAsmLibrary ( Variable constantVar , String exportAsmLibrary ) {
String varExportAsmLibrary = constantVar . getExportAsmLibrary ( ) ;
if ( exportAsmLibrary ! = null ) {
if ( varExportAsmLibrary ! = null ) {
2024-01-02 18:41:03 +00:00
return varExportAsmLibrary . equals ( exportAsmLibrary ) ;
2024-01-01 15:50:19 +00:00
} else {
2024-04-19 13:55:24 +00:00
return true ; // Bugfix
2024-01-01 15:50:19 +00:00
}
} else {
2024-04-19 13:55:24 +00:00
boolean isImportAsmLibraryGlobal = constantVar . isAsmImportLibraryGlobal ( ) ;
boolean isExportAsmLibraryParameter = constantVar . isAsmExportLibraryParameter ( ) ;
boolean isExportAsmLibraryReturn = constantVar . isAsmExportLibraryReturn ( ) ;
return ( varExportAsmLibrary = = null | | isExportAsmLibraryParameter | | isExportAsmLibraryReturn )
& & ! isImportAsmLibraryGlobal ;
2024-01-01 15:50:19 +00:00
}
2024-04-19 13:55:24 +00:00
}
2023-12-30 19:33:13 +00:00
2021-12-27 00:16:04 +00:00
private boolean hasData ( Variable constantVar ) {
ConstantValue constantValue = constantVar . getInitValue ( ) ;
if ( constantValue instanceof ConstantArray ) return true ;
else if ( constantValue instanceof ConstantStructValue ) return true ;
else if ( constantValue instanceof ConstantString ) return true ;
else return false ;
}
/ * *
* Determines whether to use a . label instead of . const for a constant .
* This can be necessary because KickAssembler does not allow constant references between scopes .
* If a constant in one scope is referenced from another scope a . label is generated in stead - to allow the cross - scope reference .
*
* @param scopeRef The current scope
* @param constantVar The constant to examine
* @return true if a . label should be used in the generated ASM
* /
private boolean useLabelForConst ( ScopeRef scopeRef , Variable constantVar ) {
boolean useLabel = false ;
Collection < Integer > constRefStatements = program . getVariableReferenceInfos ( ) . getConstRefStatements ( constantVar . getConstantRef ( ) ) ;
if ( constRefStatements ! = null ) {
for ( Integer constRefStmtIdx : constRefStatements ) {
Statement statement = program . getStatementInfos ( ) . getStatement ( constRefStmtIdx ) ;
ScopeRef refScope = program . getStatementInfos ( ) . getBlock ( constRefStmtIdx ) . getScope ( ) ;
if ( statement instanceof StatementPhiBlock ) {
// Const reference in PHI block - examine if the only predecessor is current scope
boolean found = false ;
for ( StatementPhiBlock . PhiVariable phiVariable : ( ( StatementPhiBlock ) statement ) . getPhiVariables ( ) ) {
for ( StatementPhiBlock . PhiRValue phiRValue : phiVariable . getValues ( ) ) {
RValue phiRRValue = phiRValue . getrValue ( ) ;
Collection < ConstantRef > phiRValueConstRefs = PassNCalcVariableReferenceInfos . getReferencedConsts ( phiRRValue ) ;
for ( ConstantRef phiRValueConstRef : phiRValueConstRefs ) {
if ( phiRValueConstRef . equals ( constantVar . getRef ( ) ) ) {
found = true ;
// Found the constant
LabelRef pred = phiRValue . getPredecessor ( ) ;
2023-04-08 20:52:15 +00:00
Graph . Block predBlock = program . getGraph ( ) . getBlock ( pred ) ;
2021-12-27 00:16:04 +00:00
ScopeRef predScope = predBlock . getScope ( ) ;
if ( ! predScope . equals ( scopeRef ) ) {
// Scopes in PHI RValue differs from const scope - generate label
useLabel = true ;
}
}
}
2018-02-18 18:39:08 +00:00
}
2021-12-27 00:16:04 +00:00
}
if ( ! found ) {
// PHI-reference is complex - generate label
program . getLog ( ) . append ( " Warning: Complex PHI-value using constant. Using .label as fallback. " + statement ) ;
useLabel = true ;
}
} else if ( ! refScope . equals ( scopeRef ) ) {
// Used in a non-PHI statement in another scope - generate label
useLabel = true ;
}
2018-02-18 18:39:08 +00:00
}
2021-12-27 00:16:04 +00:00
}
Collection < SymbolVariableRef > symbolRefConsts = program . getVariableReferenceInfos ( ) . getConstRefSymbols ( constantVar . getConstantRef ( ) ) ;
if ( symbolRefConsts ! = null ) {
for ( SymbolVariableRef symbolRefConst : symbolRefConsts ) {
Variable symbolRefVar = ( Variable ) program . getScope ( ) . getSymbol ( symbolRefConst ) ;
if ( ! symbolRefVar . getScope ( ) . getRef ( ) . equals ( scopeRef ) ) {
// Used in constant in another scope - generate label
useLabel = true ;
break ;
}
2019-10-20 09:41:56 +00:00
}
2021-12-27 00:16:04 +00:00
}
return useLabel ;
}
2024-01-02 18:41:03 +00:00
/ * *
* Add global exported struct size constant declarations for structure variables that are exported and thus global .
* Added for all structure variables in a library at the end code of the ROOT scope .
*
* This is a dirty copy but I feel that the code to handle and create the size structure type symbols
* was also a bit " dirty " and this whole size thing is candidate for a some serious code rework overall . . .
*
* @param asm The ASM program
* @param scopeRef The scope
* /
private void addGlobalStructSizeConstant ( AsmProgram asm , ScopeRef scopeRef , Variable var ) {
Scope scope = program . getScope ( ) . getScope ( scopeRef ) ;
Set < String > added = new LinkedHashSet < > ( ) ;
Collection < Variable > scopeConstants = scope . getAllConstants ( false ) ;
2024-01-16 05:01:05 +00:00
String asmExportLibraryName = program . getAsmLibraryName ( ) ;
if ( var . isStruct ( ) ) {
2024-04-19 13:55:24 +00:00
// String typeVar = "SIZEOF_" + var.getType().getConstantFriendlyName();
// // First add all constants without data that can become constants in KickAsm
// for (Variable constantVar : scopeConstants) {
// if (!hasData(constantVar)) {
// String asmName = constantVar.getAsmName() == null ? constantVar.getLocalName() : constantVar.getAsmName();
// if (asmName != null && !added.contains(asmName)) {
// if (constantVar.getLocalName().equals(typeVar)) {
// // Use constant otherwise
// added.add(asmName);
// // Find the constant value calculation
// String asmConstant = AsmFormat.getAsmConstant(program, constantVar.getInitValue(), 99, scopeRef);
// addConstant(asmName, constantVar, asmConstant, asm);
// }
// }
// }
// }
2024-01-02 18:41:03 +00:00
}
}
2021-12-27 00:16:04 +00:00
/ * *
* Add constant declarations for constants without data and labels for memory variables without data .
* Added before the the code of the scope .
*
* @param asm The ASM program
* @param scopeRef The scope
* /
2024-01-02 18:41:03 +00:00
private void addConstantsAndLabels ( AsmProgram asm , ScopeRef scopeRef , String exportAsmLibrary ) {
2021-12-27 00:16:04 +00:00
Scope scope = program . getScope ( ) . getScope ( scopeRef ) ;
Set < String > added = new LinkedHashSet < > ( ) ;
Collection < Variable > scopeConstants = scope . getAllConstants ( false ) ;
// First add all constants without data that can become constants in KickAsm
for ( Variable constantVar : scopeConstants ) {
2024-01-02 18:41:03 +00:00
if ( ! hasData ( constantVar ) & & hasExportAsmLibrary ( constantVar , exportAsmLibrary ) ) {
2021-12-27 00:16:04 +00:00
String asmName = constantVar . getAsmName ( ) = = null ? constantVar . getLocalName ( ) : constantVar . getAsmName ( ) ;
if ( asmName ! = null & & ! added . contains ( asmName ) ) {
if ( isIntTypeInScope ( constantVar ) ) {
// Use label for integers/bools referenced in other scope - to allow cross-scope referencing
if ( ! useLabelForConst ( scopeRef , constantVar ) ) {
// Use constant for constant integers not referenced outside scope
added . add ( asmName ) ;
2024-04-19 13:55:24 +00:00
program . getAsmLibraryNamespaceSymbols ( ) . put ( asmName , constantVar ) ;
2021-12-27 00:16:04 +00:00
// Find the constant value calculation
String asmConstant = AsmFormat . getAsmConstant ( program , constantVar . getInitValue ( ) , 99 , scopeRef ) ;
addConstant ( asmName , constantVar , asmConstant , asm ) ;
}
} else if ( ! ( constantVar . getType ( ) instanceof SymbolTypePointer ) ) {
// Use constant otherwise
added . add ( asmName ) ;
2024-04-19 13:55:24 +00:00
program . getAsmLibraryNamespaceSymbols ( ) . put ( asmName , constantVar ) ;
2021-12-27 00:16:04 +00:00
// Find the constant value calculation
String asmConstant = AsmFormat . getAsmConstant ( program , constantVar . getInitValue ( ) , 99 , scopeRef ) ;
2024-04-19 13:55:24 +00:00
// constantVar.setAsmExportLibrary(exportAsmLibrary);
addConstantLabelDecl ( asmName , constantVar , asmConstant , asm ) ;
// addConstant(asmName, constantVar, asmConstant, asm);
2021-12-27 00:16:04 +00:00
}
}
2019-02-16 23:49:19 +00:00
}
2021-12-27 00:16:04 +00:00
}
// Add constants without data that must be labels in KickAsm
for ( Variable constantVar : scopeConstants ) {
2024-01-02 18:41:03 +00:00
if ( ! hasData ( constantVar ) & & hasExportAsmLibrary ( constantVar , exportAsmLibrary ) ) {
2021-12-27 00:16:04 +00:00
String asmName = constantVar . getAsmName ( ) = = null ? constantVar . getLocalName ( ) : constantVar . getAsmName ( ) ;
if ( asmName ! = null & & ! added . contains ( asmName ) ) {
if ( constantVar . getType ( ) instanceof SymbolTypePointer ) {
// Must use a label for pointers
added . add ( asmName ) ;
2024-04-19 13:55:24 +00:00
program . getAsmLibraryNamespaceSymbols ( ) . put ( asmName , constantVar ) ;
2021-12-27 00:16:04 +00:00
String asmConstant = AsmFormat . getAsmConstant ( program , constantVar . getInitValue ( ) , 99 , scopeRef ) ;
addConstantLabelDecl ( asmName , constantVar , asmConstant , asm ) ;
} else if ( isIntTypeInScope ( constantVar ) ) {
// Use label for integers referenced in other scope - to allow cross-scope referencing
if ( useLabelForConst ( scopeRef , constantVar ) ) {
// Use label for integers referenced in other scope - to allow cross-scope referencing
added . add ( asmName ) ;
2024-04-19 13:55:24 +00:00
program . getAsmLibraryNamespaceSymbols ( ) . put ( asmName , constantVar ) ;
2021-12-27 00:16:04 +00:00
// Add any comments
String asmConstant = AsmFormat . getAsmConstant ( program , constantVar . getInitValue ( ) , 99 , scopeRef ) ;
addConstantLabelDecl ( asmName , constantVar , asmConstant , asm ) ;
}
}
}
2019-02-16 23:49:19 +00:00
}
2021-12-27 00:16:04 +00:00
}
// Add labels for memory variables without data
Collection < Variable > scopeVars = scope . getAllVariables ( false ) ;
for ( Variable scopeVar : scopeVars ) {
Registers . Register register = scopeVar . getAllocation ( ) ;
if ( register ! = null ) {
if ( Registers . RegisterType . ZP_MEM . equals ( register . getType ( ) ) ) {
Registers . RegisterZpMem registerZp = ( Registers . RegisterZpMem ) register ;
String asmName = scopeVar . getAsmName ( ) ;
if ( asmName ! = null & & ! added . contains ( asmName ) ) {
added . add ( asmName ) ;
2024-04-19 13:55:24 +00:00
program . getAsmLibraryNamespaceSymbols ( ) . put ( asmName , scopeVar ) ;
2021-12-27 00:16:04 +00:00
addConstantLabelDecl ( asmName , scopeVar , AsmFormat . getAsmNumber ( registerZp . getZp ( ) ) , asm ) ;
}
} else if ( Registers . RegisterType . MAIN_MEM . equals ( register . getType ( ) ) & & ( ( Registers . RegisterMainMem ) register ) . getAddress ( ) ! = null ) {
String asmName = scopeVar . getAsmName ( ) ;
if ( asmName ! = null & & ! added . contains ( asmName ) ) {
added . add ( asmName ) ;
2024-04-19 13:55:24 +00:00
program . getAsmLibraryNamespaceSymbols ( ) . put ( asmName , scopeVar ) ;
2021-12-27 00:16:04 +00:00
// Add the label declaration
Long address = ( ( Registers . RegisterMainMem ) register ) . getAddress ( ) ;
addConstantLabelDecl ( asmName , scopeVar , AsmFormat . getAsmNumber ( address ) , asm ) ;
}
}
2018-07-21 09:13:32 +00:00
}
2021-12-27 00:16:04 +00:00
}
}
private boolean isIntTypeInScope ( Variable constantVar ) {
return ( SymbolType . isInteger ( constantVar . getType ( ) ) | | SymbolType . BOOLEAN . equals ( constantVar . getType ( ) ) ) & & constantVar . getRef ( ) . getScopeDepth ( ) > 0 ;
}
private void addConstant ( String asmName , Variable constantVar , String asmConstant , AsmProgram asm ) {
// Add any comments
generateComments ( asm , constantVar . getComments ( ) ) ;
// Ensure encoding is good
AsmEncodingHelper . ensureEncoding ( asm , constantVar . getInitValue ( ) ) ;
asm . addConstant ( AsmFormat . asmFix ( asmName ) , asmConstant ) ;
}
private void addConstantLabelDecl ( String asmName , Variable variable , String asmConstant , AsmProgram asm ) {
// Add any comments
generateComments ( asm , variable . getComments ( ) ) ;
// Ensure encoding is good
AsmEncodingHelper . ensureEncoding ( asm , variable . getInitValue ( ) ) ;
// Find the constant value calculation
2023-10-27 13:31:51 +00:00
asm . addLabelDecl ( AsmFormat . asmFix2 ( asmName , variable . getScope ( ) . getLocalName ( ) ) , asmConstant ) ;
2021-12-27 00:16:04 +00:00
}
/ * *
* Add all constants with data that must be placed at an absolute address
* Added at the end of the file
*
* @param asm The ASM program
* @param scopeRef The scope
* /
2024-01-01 15:50:19 +00:00
private void addAbsoluteAddressData ( AsmProgram asm , ScopeRef scopeRef , String exportAsmLibrary ) {
2021-12-27 00:16:04 +00:00
Scope scope = program . getScope ( ) . getScope ( scopeRef ) ;
Collection < Variable > scopeConstants = scope . getAllConstants ( false ) ;
Set < String > added = new LinkedHashSet < > ( ) ;
// Add all constants arrays incl. strings with data
for ( Variable constantVar : scopeConstants ) {
2024-01-01 15:50:19 +00:00
if ( hasData ( constantVar ) & & hasExportAsmLibrary ( constantVar , exportAsmLibrary ) ) {
2021-12-27 00:16:04 +00:00
// Skip if already added
String asmName = constantVar . getAsmName ( ) = = null ? constantVar . getLocalName ( ) : constantVar . getAsmName ( ) ;
if ( added . contains ( asmName ) ) {
continue ;
}
// Skip if address is not absolute
if ( constantVar . getMemoryAddress ( ) = = null ) continue ;
// Set segment
setCurrentSegment ( constantVar . getDataSegment ( ) , asm ) ;
// Set absolute address
asm . addLine ( new AsmSetPc ( asmName , AsmFormat . getAsmConstant ( program , constantVar . getMemoryAddress ( ) , 99 , scopeRef ) ) ) ;
// Add any comments
generateComments ( asm , constantVar . getComments ( ) ) ;
// Add any alignment
Integer declaredAlignment = constantVar . getMemoryAlignment ( ) ;
if ( declaredAlignment ! = null ) {
String alignment = AsmFormat . getAsmNumber ( declaredAlignment ) ;
asm . addDataAlignment ( alignment ) ;
}
ConstantValue constantValue = constantVar . getInitValue ( ) ;
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 ) ;
} else {
throw new InternalError ( " Constant Variable not handled " + constantVar . toString ( program ) ) ;
}
added . add ( asmName ) ;
2019-09-26 12:35:02 +00:00
}
2021-12-27 00:16:04 +00:00
}
}
/ * *
* Add constants with data and memory variables with data for a scope .
* Added after the the code of the scope .
*
* @param asm The ASM program
* @param scopeRef The scope
* /
2024-01-01 15:50:19 +00:00
private void addData ( AsmProgram asm , ScopeRef scopeRef , String exportAsmLibrary ) {
2021-12-27 00:16:04 +00:00
Scope scope = program . getScope ( ) . getScope ( scopeRef ) ;
Collection < Variable > scopeConstants = scope . getAllConstants ( false ) ;
Set < String > added = new LinkedHashSet < > ( ) ;
// Add all constants arrays incl. strings with data
for ( Variable constantVar : scopeConstants ) {
2024-01-01 15:50:19 +00:00
if ( hasData ( constantVar ) & & hasExportAsmLibrary ( constantVar , exportAsmLibrary ) ) {
2021-12-27 00:16:04 +00:00
// Skip if already added
String asmName = constantVar . getAsmName ( ) = = null ? constantVar . getLocalName ( ) : constantVar . getAsmName ( ) ;
if ( added . contains ( asmName ) ) {
continue ;
}
// Skip if address is absolute
if ( constantVar . getMemoryAddress ( ) ! = null ) continue ;
// Set segment
2024-01-01 15:50:19 +00:00
// We use a dummy segment if an .asm library for data to be ignored during import.
String dataSegment = constantVar . getDataSegment ( ) ;
// if(exportAsmLibrary == null && scopeRef == ScopeRef.ROOT) {
// dataSegment = "Default";
// }
setCurrentSegment ( dataSegment , asm ) ;
2021-12-27 00:16:04 +00:00
// Add any comments
generateComments ( asm , constantVar . getComments ( ) ) ;
// Add any alignment
Integer declaredAlignment = constantVar . getMemoryAlignment ( ) ;
if ( declaredAlignment ! = null ) {
String alignment = AsmFormat . getAsmNumber ( declaredAlignment ) ;
asm . addDataAlignment ( alignment ) ;
}
ConstantValue constantValue = constantVar . getInitValue ( ) ;
if ( constantValue instanceof ConstantArray | | constantValue instanceof ConstantString | | constantValue instanceof ConstantStructValue ) {
AsmDataChunk asmDataChunk = new AsmDataChunk ( ) ;
addChunkData ( asmDataChunk , constantValue , constantVar . getType ( ) , constantVar . getArraySpec ( ) , scopeRef ) ;
2023-10-27 13:31:51 +00:00
asmDataChunk . addToAsm ( AsmFormat . asmFix2 ( asmName , constantVar . getScope ( ) . getLocalName ( ) ) , asm ) ;
2021-12-27 00:16:04 +00:00
} else {
throw new InternalError ( " Constant Variable not handled " + constantVar . toString ( program ) ) ;
}
added . add ( asmName ) ;
2021-07-23 15:26:47 +00:00
}
2021-12-27 00:16:04 +00:00
}
// Add all memory variables
Collection < Variable > scopeVariables = scope . getAllVariables ( false ) ;
for ( Variable variable : scopeVariables ) {
2024-01-01 15:50:19 +00:00
if ( hasExportAsmLibrary ( variable , exportAsmLibrary ) ) {
Registers . Register allocation = variable . getAllocation ( ) ;
if ( variable . getAllocation ( ) instanceof Registers . RegisterMainMem ) {
Registers . RegisterMainMem registerMainMem = ( Registers . RegisterMainMem ) allocation ;
// Skip PHI masters
if ( variable . isKindPhiMaster ( ) ) continue ;
// Skip if already added
if ( added . contains ( variable . getAsmName ( ) ) ) continue ;
if ( variable . isKindLoadStore ( ) | | variable . isKindPhiVersion ( ) | | variable . isKindIntermediate ( ) ) {
final Variable mainVar = program . getScope ( ) . getVariable ( registerMainMem . getVariableRef ( ) ) ;
if ( registerMainMem . getAddress ( ) = = null ) {
// Generate into the data segment
// Set segment
setCurrentSegment ( variable . getDataSegment ( ) , asm ) ;
// Add any comments
generateComments ( asm , variable . getComments ( ) ) ;
final String mainAsmName = AsmFormat . getAsmConstant ( program , new ConstantSymbolPointer ( mainVar . getRef ( ) ) , 99 , scopeRef ) ;
final String asmSymbolName = AsmFormat . getAsmSymbolName ( program , variable , scopeRef ) ;
if ( ! mainAsmName . equals ( asmSymbolName ) ) {
asm . addLabelDecl ( asmSymbolName , mainAsmName ) ;
2021-12-27 00:16:04 +00:00
} else {
2024-01-01 15:50:19 +00:00
// Add any alignment
Integer declaredAlignment = variable . getMemoryAlignment ( ) ;
if ( declaredAlignment ! = null ) {
String alignment = AsmFormat . getAsmNumber ( declaredAlignment ) ;
asm . addDataAlignment ( alignment ) ;
}
if ( variable . getInitValue ( ) ! = null ) {
// Variable has a constant init Value
2024-01-02 18:41:03 +00:00
if ( exportAsmLibrary ! = null )
addGlobalStructSizeConstant ( asm , scopeRef , variable ) ;
2024-01-01 15:50:19 +00:00
ConstantValue constantValue = variable . getInitValue ( ) ;
AsmDataChunk asmDataChunk = new AsmDataChunk ( ) ;
addChunkData ( asmDataChunk , constantValue , variable . getType ( ) , variable . getArraySpec ( ) , scopeRef ) ;
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 . asmFix2 ( variable . getAsmName ( ) , variable . getScope ( ) . getLocalName ( ) ) , asm ) ;
}
2023-10-27 13:31:51 +00:00
2024-01-01 15:50:19 +00:00
}
added . add ( variable . getAsmName ( ) ) ;
2021-12-27 00:16:04 +00:00
}
2024-01-01 15:50:19 +00:00
} else {
throw new InternalError ( " Not handled variable storage " + variable . toString ( ) ) ;
2021-12-27 00:16:04 +00:00
}
}
2020-01-08 23:30:16 +00:00
}
2021-12-27 00:16:04 +00:00
}
}
/ * *
* Get the declared size of an array type as an integer
*
* @param declaredSize The declared size
* @return The integer size . Null if it can ' t be determined
* /
private Integer getArrayDeclaredSize ( ConstantValue declaredSize ) {
if ( declaredSize ! = null ) {
ConstantLiteral declaredSizeVal = declaredSize . calculateLiteral ( getScope ( ) ) ;
if ( ! ( declaredSizeVal instanceof ConstantInteger ) ) {
throw new CompileError ( " Error! Array declared size is not integer " + declaredSize . toString ( ) ) ;
2019-09-13 09:56:05 +00:00
}
2021-12-27 00:16:04 +00:00
return ( ( ConstantInteger ) declaredSizeVal ) . getInteger ( ) . intValue ( ) ;
}
return null ;
}
/ * *
* Fill the data of a constant value into a data chunk
*
* @param dataChunk The data chunk
* @param value The constant value
* @param valueType The declared type of the value
* @param valueArraySpec The array properties of the value
* @param scopeRef The scope containing the data chunk
* /
private void addChunkData ( AsmDataChunk dataChunk , ConstantValue value , SymbolType valueType , ArraySpec valueArraySpec , ScopeRef scopeRef ) {
if ( valueType instanceof SymbolTypeStruct ) {
if ( value instanceof ConstantStructValue ) {
// Add each struct member recursively
ConstantStructValue structValue = ( ConstantStructValue ) value ;
int size = 0 ;
for ( SymbolVariableRef memberRef : structValue . getMembers ( ) ) {
ConstantValue memberValue = structValue . getValue ( memberRef ) ;
Variable memberVariable = getScope ( ) . getVar ( memberRef ) ;
addChunkData ( dataChunk , memberValue , memberVariable . getType ( ) , memberVariable . getArraySpec ( ) , scopeRef ) ;
size + = SymbolTypeStruct . getMemberSizeBytes ( memberVariable . getType ( ) , memberVariable . getArraySize ( ) , getScope ( ) ) ;
}
// Add padding if this is a union and the first member does not use all bytes
final int declaredSize = structValue . getStructType ( ) . getSizeBytes ( ) ;
if ( size < declaredSize ) {
long paddingSize = declaredSize - size ;
// TODO: Use SIZEOF constant
ConstantValue paddingSizeVal = new ConstantInteger ( paddingSize ) ;
String paddingBytesAsm = AsmFormat . getAsmConstant ( program , paddingSizeVal , 99 , scopeRef ) ;
ConstantValue zeroValue = new ConstantInteger ( 0l , SymbolType . BYTE ) ;
dataChunk . addDataZeroFilled ( AsmDataNumeric . Type . BYTE , paddingBytesAsm , ( int ) paddingSize , AsmEncodingHelper . getEncoding ( zeroValue ) ) ;
}
} else if ( value instanceof StructZero ) {
final SymbolTypeStruct typeStruct = ( ( StructZero ) value ) . getTypeStruct ( ) ;
final ConstantRef structSize = SizeOfConstants . getSizeOfConstantVar ( getScope ( ) , typeStruct ) ;
String totalSizeBytesAsm = AsmFormat . getAsmConstant ( program , structSize , 99 , scopeRef ) ;
int totalSizeBytes = typeStruct . getSizeBytes ( ) ;
dataChunk . addDataZeroFilled ( AsmDataNumeric . Type . BYTE , totalSizeBytesAsm , totalSizeBytes , null ) ;
2019-09-13 09:56:05 +00:00
}
2021-12-27 00:16:04 +00:00
} else if ( valueType instanceof SymbolTypePointer & & valueArraySpec ! = null ) {
SymbolTypePointer constTypeArray = ( SymbolTypePointer ) valueType ;
SymbolType elementType = constTypeArray . getElementType ( ) ;
SymbolType dataType = value . getType ( program . getScope ( ) ) ;
int dataNumElements = 0 ;
if ( value instanceof ConstantArrayFilled ) {
ConstantArrayFilled constantArrayFilled = ( ConstantArrayFilled ) value ;
ConstantValue arraySize = constantArrayFilled . getSize ( ) ;
ConstantLiteral arraySizeConst = arraySize . calculateLiteral ( getScope ( ) ) ;
if ( ! ( arraySizeConst instanceof ConstantInteger ) ) {
throw new Pass2SsaAssertion . AssertionFailed ( " Error! Array size is not constant integer " + arraySize . toString ( program ) ) ;
}
dataNumElements = ( ( ConstantInteger ) arraySizeConst ) . getInteger ( ) . intValue ( ) ;
int elementSizeBytes = elementType . getSizeBytes ( ) ;
String totalSizeBytesAsm ;
if ( elementSizeBytes > 1 ) {
// TODO: Use a SIZEOF constant for the element size ASM
totalSizeBytesAsm = AsmFormat . getAsmConstant ( program , new ConstantBinary ( new ConstantInteger ( ( long ) elementSizeBytes , SymbolType . NUMBER ) , Operators . MULTIPLY , arraySize ) , 99 , scopeRef ) ;
} else {
totalSizeBytesAsm = AsmFormat . getAsmConstant ( program , arraySize , 99 , scopeRef ) ;
}
if ( elementType instanceof SymbolTypeIntegerFixed | | elementType instanceof SymbolTypePointer ) {
// Use an ASM type in the fill that matches the element type
dataChunk . addDataZeroFilled ( getNumericType ( elementType ) , totalSizeBytesAsm , dataNumElements , null ) ;
} else {
// Complex fill type - calculate byte size and use that
int totalSizeBytes = elementSizeBytes * dataNumElements ;
dataChunk . addDataZeroFilled ( AsmDataNumeric . Type . BYTE , totalSizeBytesAsm , totalSizeBytes , null ) ;
}
} else if ( value instanceof ConstantArrayKickAsm ) {
ConstantArrayKickAsm kickAsm = ( ConstantArrayKickAsm ) value ;
// default - larger then 256
int bytes = 1023 ;
Integer declaredSize = getArrayDeclaredSize ( valueArraySpec . getArraySize ( ) ) ;
if ( declaredSize ! = null ) {
bytes = declaredSize * elementType . getSizeBytes ( ) ;
}
dataChunk . addDataKickAsm ( bytes , kickAsm . getKickAsmCode ( ) , AsmEncodingHelper . getEncoding ( value ) ) ;
dataNumElements = bytes ;
} else if ( value instanceof ConstantString ) {
ConstantString stringValue = ( ConstantString ) value ;
String asmConstant = AsmFormat . getAsmConstant ( program , stringValue , 99 , scopeRef ) ;
dataChunk . addDataString ( asmConstant , AsmEncodingHelper . getEncoding ( stringValue ) ) ;
if ( stringValue . isZeroTerminated ( ) ) {
dataChunk . addDataNumeric ( AsmDataNumeric . Type . BYTE , " 0 " , null ) ;
}
dataNumElements = stringValue . getStringLength ( ) ;
2019-09-13 19:21:32 +00:00
} else {
2021-12-27 00:16:04 +00:00
// Assume we have a ConstantArrayList
ConstantArrayList constantArrayList = ( ConstantArrayList ) value ;
// Output each element to the chunk
for ( ConstantValue element : constantArrayList . getElements ( ) ) {
addChunkData ( dataChunk , element , elementType , null , scopeRef ) ;
}
dataNumElements = constantArrayList . getElements ( ) . size ( ) ;
2019-09-13 09:56:05 +00:00
}
2021-12-27 00:16:04 +00:00
// Pad output to match declared size (if larger than the data list)
if ( ! ( value instanceof ConstantArrayKickAsm ) ) {
Integer declaredSize = getArrayDeclaredSize ( valueArraySpec . getArraySize ( ) ) ;
if ( declaredSize ! = null & & declaredSize > dataNumElements ) {
long paddingSize = declaredSize - dataNumElements ;
ConstantValue paddingSizeVal = new ConstantInteger ( paddingSize ) ;
int elementSizeBytes = elementType . getSizeBytes ( ) ;
String paddingBytesAsm ;
if ( elementSizeBytes > 1 ) {
// TODO: Use a SIZEOF constant for the element size ASM - combine this with ConstantArrayFilled above
paddingBytesAsm = AsmFormat . getAsmConstant ( program , new ConstantBinary ( new ConstantInteger ( ( long ) elementSizeBytes , SymbolType . NUMBER ) , Operators . MULTIPLY , paddingSizeVal ) , 99 , scopeRef ) ;
} else {
paddingBytesAsm = AsmFormat . getAsmConstant ( program , paddingSizeVal , 99 , scopeRef ) ;
}
ConstantValue zeroValue = Initializers . createZeroValue ( new Initializers . ValueTypeSpec ( elementType ) , null ) ;
if ( zeroValue instanceof ConstantInteger | zeroValue instanceof ConstantPointer ) {
dataChunk . addDataZeroFilled ( getNumericType ( elementType ) , paddingBytesAsm , ( int ) paddingSize , AsmEncodingHelper . getEncoding ( zeroValue ) ) ;
} else {
for ( int i = 0 ; i < paddingSize ; i + + ) {
addChunkData ( dataChunk , zeroValue , elementType , null , scopeRef ) ;
}
}
}
2019-09-13 18:05:31 +00:00
}
2021-12-27 00:16:04 +00:00
} else if ( value instanceof ConstantString ) {
2020-02-06 21:52:23 +00:00
ConstantString stringValue = ( ConstantString ) value ;
2021-12-27 00:16:04 +00:00
// Ensure encoding is good
2020-02-06 21:52:23 +00:00
String asmConstant = AsmFormat . getAsmConstant ( program , stringValue , 99 , scopeRef ) ;
2021-12-26 23:33:30 +00:00
dataChunk . addDataString ( asmConstant , AsmEncodingHelper . getEncoding ( stringValue ) ) ;
2021-12-27 00:16:04 +00:00
if ( stringValue . isZeroTerminated ( ) ) {
dataChunk . addDataNumeric ( AsmDataNumeric . Type . BYTE , " 0 " , null ) ;
2017-06-03 22:45:40 +00:00
}
2021-12-27 00:16:04 +00:00
} else if ( SymbolType . BYTE . equals ( valueType ) | | SymbolType . SBYTE . equals ( valueType ) ) {
dataChunk . addDataNumeric ( AsmDataNumeric . Type . BYTE , AsmFormat . getAsmConstant ( program , value , 99 , scopeRef ) , AsmEncodingHelper . getEncoding ( value ) ) ;
} else if ( SymbolType . WORD . equals ( valueType ) | | SymbolType . SWORD . equals ( valueType ) ) {
dataChunk . addDataNumeric ( AsmDataNumeric . Type . WORD , AsmFormat . getAsmConstant ( program , value , 99 , scopeRef ) , AsmEncodingHelper . getEncoding ( value ) ) ;
} else if ( SymbolType . DWORD . equals ( valueType ) | | SymbolType . SDWORD . equals ( valueType ) ) {
dataChunk . addDataNumeric ( AsmDataNumeric . Type . DWORD , AsmFormat . getAsmConstant ( program , value , 99 , scopeRef ) , AsmEncodingHelper . getEncoding ( value ) ) ;
} else if ( valueType instanceof SymbolTypePointer ) {
dataChunk . addDataNumeric ( AsmDataNumeric . Type . WORD , AsmFormat . getAsmConstant ( program , value , 99 , scopeRef ) , AsmEncodingHelper . getEncoding ( value ) ) ;
} else if ( SymbolType . BOOLEAN . equals ( valueType ) ) {
dataChunk . addDataNumeric ( AsmDataNumeric . Type . BYTE , AsmFormat . getAsmConstant ( program , value , 99 , scopeRef ) , AsmEncodingHelper . getEncoding ( value ) ) ;
} else {
throw new InternalError ( " Unhandled array element type " + valueType . toString ( ) + " value " + value . toString ( program ) ) ;
}
}
/ * *
* Get the numeric data type to use when outputting a value type to ASM
*
* @param valueType The value type
* @return The numeric data type
* /
private static AsmDataNumeric . Type getNumericType ( SymbolType valueType ) {
if ( SymbolType . BYTE . equals ( valueType ) | | SymbolType . SBYTE . equals ( valueType ) ) {
return AsmDataNumeric . Type . BYTE ;
} else if ( SymbolType . WORD . equals ( valueType ) | | SymbolType . SWORD . equals ( valueType ) ) {
return AsmDataNumeric . Type . WORD ;
} else if ( SymbolType . DWORD . equals ( valueType ) | | SymbolType . SDWORD . equals ( valueType ) ) {
return AsmDataNumeric . Type . DWORD ;
} else if ( valueType instanceof SymbolTypePointer ) {
return AsmDataNumeric . Type . WORD ;
} else {
throw new InternalError ( " Unhandled type " + valueType . toString ( ) ) ;
}
}
2023-04-08 20:52:15 +00:00
private void genStatements ( AsmProgram asm , Graph . Block block ) {
2021-12-27 00:16:04 +00:00
Iterator < Statement > statementsIt = block . getStatements ( ) . iterator ( ) ;
while ( statementsIt . hasNext ( ) ) {
Statement statement = statementsIt . next ( ) ;
if ( ! ( statement instanceof StatementPhiBlock ) ) {
try {
generateStatementAsm ( asm , block , statement , true ) ;
} catch ( AsmFragmentTemplateSynthesizer . UnknownFragmentException e ) {
StatementSource statementSource = statement . getSource ( ) ;
if ( warnFragmentMissing ) {
String stmtFormat = " " ;
if ( statementSource ! = null ) stmtFormat = statementSource . format ( ) ;
program . getLog ( ) . append ( " Warning! Unknown fragment for statement " + statement . toString ( program , false ) + " \ nMissing ASM fragment " + e . getFragmentSignature ( ) + " \ n " + stmtFormat ) ;
asm . addLine ( new AsmInlineKickAsm ( " .assert \" Missing ASM fragment " + e . getFragmentSignature ( ) + " \" , 0, 1 " , 0L , 0L , CpuClobber . CLOBBER_NONE ) ) ;
} else {
throw new CompileError ( " Unknown fragment for statement " + statement . toString ( program , false ) + " \ nMissing ASM fragment " + e . getFragmentSignature ( ) , statementSource ) ;
}
} catch ( CompileError e ) {
if ( e . getSource ( ) = = null ) {
throw new CompileError ( e . getMessage ( ) , statement ) ;
}
}
2019-09-22 20:20:45 +00:00
}
2021-12-27 00:16:04 +00:00
}
}
/ * *
* Generate ASM code for a single statement
*
* @param asm The ASM program to generate into
* @param block The block containing the statement
* @param statement The statement to generate ASM code for
* /
2023-04-08 20:52:15 +00:00
void generateStatementAsm ( AsmProgram asm , Graph . Block block , Statement statement , boolean genCallPhiEntry ) {
2021-12-27 00:16:04 +00:00
asm . startChunk ( block . getScope ( ) , statement . getIndex ( ) , statement . toString ( program , verboseAliveInfo ) ) ;
generateComments ( asm , statement . getComments ( ) ) ;
if ( ! ( statement instanceof StatementPhiBlock ) ) {
if ( statement instanceof StatementAssignment ) {
StatementAssignment assignment = ( StatementAssignment ) statement ;
LValue lValue = assignment . getlValue ( ) ;
if ( assignment . getOperator ( ) = = null & & assignment . getrValue1 ( ) = = null & & isRegisterCopy ( lValue , assignment . getrValue2 ( ) ) ) {
//asm.addComment(lValue.toString(program) + " = " + assignment.getrValue2().toString(program) + " // register copy " + getRegister(lValue));
} else {
2022-11-16 21:30:37 +00:00
// sven - catch this error decently, it sometimes throws an exception!
try {
AsmFragmentCodeGenerator . generateAsm ( asm , AsmFragmentInstanceSpecBuilder . assignment ( assignment , program ) , program ) ;
} catch ( AsmFragmentTemplateSynthesizer . UnknownFragmentException e ) {
throw new AsmFragmentTemplateSynthesizer . UnknownFragmentException ( e . getMessage ( ) ) ;
} catch ( RuntimeException e ) {
throw new CompileError ( " Problem with source, runtime Exception: " + e . getMessage ( ) , statement ) ;
}
2021-12-27 00:16:04 +00:00
}
} else if ( statement instanceof StatementConditionalJump ) {
AsmFragmentCodeGenerator . generateAsm ( asm , AsmFragmentInstanceSpecBuilder . conditionalJump ( ( StatementConditionalJump ) statement , block , program ) , program ) ;
} else if ( statement instanceof StatementCall ) {
StatementCall call = ( StatementCall ) statement ;
2023-04-11 06:35:53 +00:00
Procedure toProcedure = getScope ( ) . getProcedure ( call . getProcedure ( ) ) ;
2023-05-21 17:16:51 +00:00
Procedure fromProcedure = program . getProcedure ( block ) ; // We obtain from where the procedure is called, to validate the bank equality.
2023-04-11 06:35:53 +00:00
if ( toProcedure . isDeclaredIntrinsic ( ) ) {
if ( Pass1ByteXIntrinsicRewrite . INTRINSIC_MAKELONG4 . equals ( toProcedure . getFullName ( ) ) ) {
2021-12-27 00:16:04 +00:00
AsmFragmentCodeGenerator . generateAsm ( asm , AsmFragmentInstanceSpecBuilder . makelong4 ( call , program ) , program ) ;
} else {
2023-04-11 06:35:53 +00:00
throw new CompileError ( " Intrinsic procedure not supported " + toProcedure . toString ( program ) ) ;
2021-12-27 00:16:04 +00:00
}
2023-04-11 06:35:53 +00:00
} else if ( Procedure . CallingConvention . PHI_CALL . equals ( toProcedure . getCallingConvention ( ) ) ) {
2021-12-27 00:16:04 +00:00
// Generate PHI transition
if ( genCallPhiEntry ) {
2023-04-08 20:52:15 +00:00
Graph . Block callSuccessor = getGraph ( ) . getCallSuccessor ( block ) ;
2021-12-27 00:16:04 +00:00
if ( callSuccessor ! = null & & callSuccessor . hasPhiBlock ( ) ) {
PhiTransitions . PhiTransition transition = getTransitions ( callSuccessor ) . getTransition ( block ) ;
if ( transitionIsGenerated ( transition ) ) {
throw new InternalError ( " Error! JSR transition already generated. Must modify PhiTransitions code to ensure this does not happen. " ) ;
}
genBlockPhiTransition ( asm , block , callSuccessor , block . getScope ( ) ) ;
}
}
2023-04-23 20:20:13 +00:00
final Bank . CallingDistance callingDistance = Bank . CallingDistance . forCall ( fromProcedure . getBank ( ) , toProcedure . getBank ( ) ) ;
if ( Bank . CallingDistance . NEAR . equals ( callingDistance ) ) {
2023-11-29 13:15:52 +00:00
asm . addInstruction ( " jsr " , CpuAddressingMode . ABS , toProcedure . getCallFullName ( ) , false ) ;
2023-04-23 09:54:47 +00:00
} else {
2023-04-23 19:07:16 +00:00
AsmFragmentCodeGenerator . generateAsm ( asm , AsmFragmentInstanceSpecBuilder . callBanked ( toProcedure , callingDistance , program ) , program ) ;
2023-04-23 09:54:47 +00:00
}
2023-04-11 06:35:53 +00:00
} else if ( Procedure . CallingConvention . STACK_CALL . equals ( toProcedure . getCallingConvention ( ) ) ) {
2023-04-23 20:20:13 +00:00
final Bank . CallingDistance callingDistance = Bank . CallingDistance . forCall ( fromProcedure . getBank ( ) , toProcedure . getBank ( ) ) ;
if ( Bank . CallingDistance . NEAR . equals ( callingDistance ) ) {
2023-11-18 09:40:50 +00:00
if ( toProcedure . getAsmLibrary ( ) = = null ) {
2023-10-27 13:31:51 +00:00
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.
2023-11-27 08:36:57 +00:00
asm . addInstruction ( " jsr " , CpuAddressingMode . ABS , toProcedure . getCallFullName ( ) , false ) ;
2023-10-27 13:31:51 +00:00
asm . getCurrentChunk ( ) . setClobberOverwrite ( CpuClobber . CLOBBER_ALL ) ;
}
2023-04-23 09:54:47 +00:00
} else {
throw new CompileError ( " Stack Call procedure not supported in banked mode " + toProcedure . toString ( program ) ) ;
2022-11-15 16:32:04 +00:00
}
2021-12-27 00:16:04 +00:00
}
} else if ( statement instanceof StatementCallExecute ) {
StatementCallExecute call = ( StatementCallExecute ) statement ;
2023-04-10 05:22:06 +00:00
ProcedureRef procedureRef = call . getProcedure ( ) ;
if ( procedureRef ! = null ) {
ProgramScope scope = getScope ( ) ;
2023-04-11 06:35:53 +00:00
Procedure toProcedure = scope . getProcedure ( procedureRef ) ;
2023-10-27 13:31:51 +00:00
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
2023-04-23 20:20:13 +00:00
final Bank . CallingDistance callingDistance = Bank . CallingDistance . forCall ( fromProcedure . getBank ( ) , toProcedure . getBank ( ) ) ;
2023-10-27 13:31:51 +00:00
if ( Bank . CallingDistance . FAR . equals ( callingDistance ) ) {
// if (toProcedure.isDeclaredBanked() && fromProcedure.getBank() != toProcedure.getBank()) {
2023-04-23 21:25:28 +00:00
throw new CompileError ( " Stack Call procedure not supported in banked mode " + toProcedure . toString ( program ) ) ;
2023-10-27 13:31:51 +00:00
} else {
2023-11-18 09:40:50 +00:00
if ( toProcedure . getAsmLibrary ( ) = = null ) {
2023-10-27 13:31:51 +00:00
AsmFragmentCodeGenerator . generateAsm ( asm , AsmFragmentInstanceSpecBuilder . call ( call , indirectCallCount + + , program ) , program ) ;
2024-01-16 05:01:05 +00:00
asm . getCurrentChunk ( ) . setClobberOverwrite ( CpuClobber . CLOBBER_ALL ) ;
2023-10-27 13:31:51 +00:00
} 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 ) ;
}
2023-04-10 05:22:06 +00:00
}
2023-10-27 13:31:51 +00:00
// RValue procedureRVal = call.getProcedureRVal();
2023-04-10 05:22:06 +00:00
if ( ! ( procedureRVal instanceof ProcedureRef ) ) {
asm . getCurrentChunk ( ) . setClobberOverwrite ( CpuClobber . CLOBBER_ALL ) ;
}
} else {
RValue procedureRVal = call . getProcedureRVal ( ) ;
// Generate ASM for a call
AsmFragmentCodeGenerator . generateAsm ( asm , AsmFragmentInstanceSpecBuilder . call ( call , indirectCallCount + + , program ) , program ) ;
if ( ! ( procedureRVal instanceof ProcedureRef ) ) {
asm . getCurrentChunk ( ) . setClobberOverwrite ( CpuClobber . CLOBBER_ALL ) ;
}
2021-12-27 00:16:04 +00:00
}
} else if ( statement instanceof StatementExprSideEffect ) {
AsmFragmentCodeGenerator . generateAsm ( asm , AsmFragmentInstanceSpecBuilder . exprSideEffect ( ( StatementExprSideEffect ) statement , program ) , program ) ;
} else if ( statement instanceof StatementReturn ) {
Procedure procedure = null ;
ScopeRef scope = block . getScope ( ) ;
if ( ! scope . equals ( ScopeRef . ROOT ) ) {
procedure = getScope ( ) . getProcedure ( ( ProcedureRef ) scope ) ;
}
if ( procedure = = null | | procedure . getInterruptType ( ) = = null ) {
asm . addInstruction ( " rts " , CpuAddressingMode . NON , null , false ) ;
} else {
generateInterruptExit ( asm , procedure ) ;
}
} else if ( statement instanceof StatementAsm ) {
StatementAsm statementAsm = ( StatementAsm ) statement ;
HashMap < String , Value > bindings = new HashMap < > ( ) ;
AsmFragmentInstance asmFragmentInstance = new AsmFragmentInstance ( program , " inline " , block . getScope ( ) , new AsmFragmentTemplate ( statementAsm . getAsmLines ( ) , program . getTargetCpu ( ) ) , bindings ) ;
asmFragmentInstance . generate ( asm ) ;
AsmChunk currentChunk = asm . getCurrentChunk ( ) ;
if ( statementAsm . getDeclaredClobber ( ) ! = null ) {
currentChunk . setClobberOverwrite ( statementAsm . getDeclaredClobber ( ) ) ;
} else {
for ( AsmLine asmLine : currentChunk . getLines ( ) ) {
if ( asmLine instanceof AsmInstruction ) {
AsmInstruction asmInstruction = ( AsmInstruction ) asmLine ;
if ( asmInstruction . getCpuOpcode ( ) . getMnemonic ( ) . equals ( " jsr " ) ) {
currentChunk . setClobberOverwrite ( CpuClobber . CLOBBER_ALL ) ;
}
}
}
}
2023-10-27 13:31:51 +00:00
/ * 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 ) ;
}
}
}
}
}
}
2021-12-27 00:16:04 +00:00
} else if ( statement instanceof StatementKickAsm ) {
StatementKickAsm statementKasm = ( StatementKickAsm ) statement ;
addKickAsm ( asm , statementKasm ) ;
2023-10-27 13:31:51 +00:00
AsmChunk currentChunk = asm . getCurrentChunk ( ) ;
2021-12-27 00:16:04 +00:00
if ( statementKasm . getDeclaredClobber ( ) ! = null ) {
2023-10-27 13:31:51 +00:00
currentChunk . setClobberOverwrite ( statementKasm . getDeclaredClobber ( ) ) ;
2021-12-27 00:16:04 +00:00
}
} else if ( statement instanceof StatementCallPointer ) {
throw new InternalError ( " Statement not supported " + statement ) ;
2018-08-05 11:33:23 +00:00
}
2021-12-27 00:16:04 +00:00
}
}
/ * *
* Generate exit - code for entering an interrupt procedure based on the interrupt type
*
* @param asm The assembler to generate code into
* @param procedure The interrupt procedure
* /
private void generateInterruptEntry ( AsmProgram asm , Procedure procedure ) {
final String interruptType = procedure . getInterruptType ( ) . toLowerCase ( ) ;
AsmFragmentInstanceSpec entryFragment ;
String entryName ;
if ( interruptType . contains ( " clobber " ) ) {
entryFragment = AsmFragmentInstanceSpecBuilder . interruptEntry ( interruptType . replace ( " clobber " , " all " ) , program ) ;
entryName = entryFragment . getSignature ( ) . replace ( " all " , " clobber " ) ;
} else {
entryFragment = AsmFragmentInstanceSpecBuilder . interruptEntry ( interruptType , program ) ;
entryName = entryFragment . getSignature ( ) ;
}
try {
asm . startChunk ( procedure . getRef ( ) , null , " interrupt( " + entryName + " ) " ) ;
AsmFragmentCodeGenerator . generateAsm ( asm , entryFragment , program ) ;
} catch ( AsmFragmentTemplateSynthesizer . UnknownFragmentException e ) {
throw new CompileError ( " Interrupt type not supported " + procedure . getInterruptType ( ) + " int " + procedure + " \ n " + e . getMessage ( ) ) ;
}
}
/ * *
* Generate exit - code for ending an interrupt procedure based on the interrupt type
*
* @param asm The assembler to generate code into
* @param procedure The procedure
* /
private void generateInterruptExit ( AsmProgram asm , Procedure procedure ) {
final String interruptType = procedure . getInterruptType ( ) . toLowerCase ( ) ;
AsmFragmentInstanceSpec entryFragment ;
String entryName ;
if ( interruptType . contains ( " clobber " ) ) {
entryFragment = AsmFragmentInstanceSpecBuilder . interruptExit ( interruptType . replace ( " clobber " , " all " ) , program ) ;
entryName = entryFragment . getSignature ( ) . replace ( " all " , " clobber " ) ;
} else {
entryFragment = AsmFragmentInstanceSpecBuilder . interruptExit ( interruptType , program ) ;
entryName = entryFragment . getSignature ( ) ;
}
asm . startChunk ( procedure . getRef ( ) , null , " interrupt( " + entryName + " ) " ) ;
try {
AsmFragmentCodeGenerator . generateAsm ( asm , entryFragment , program ) ;
} catch ( AsmFragmentTemplateSynthesizer . UnknownFragmentException e ) {
throw new CompileError ( " Interrupt type not supported " + procedure . getInterruptType ( ) + " int " + procedure + " \ n " + e . getMessage ( ) ) ;
}
}
private void addKickAsm ( AsmProgram asm , StatementKickAsm statementKasm ) {
Long asmBytes = null ;
if ( statementKasm . getBytes ( ) ! = null ) {
ConstantValue kasmBytes = ( ConstantValue ) statementKasm . getBytes ( ) ;
ConstantLiteral kasmBytesLiteral = kasmBytes . calculateLiteral ( getScope ( ) ) ;
asmBytes = ( ( ConstantInteger ) kasmBytesLiteral ) . getInteger ( ) ;
}
Long asmCycles = null ;
if ( statementKasm . getCycles ( ) ! = null ) {
ConstantValue kasmCycles = ( ConstantValue ) statementKasm . getCycles ( ) ;
ConstantLiteral kasmCyclesLiteral = kasmCycles . calculateLiteral ( getScope ( ) ) ;
asmCycles = ( ( ConstantInteger ) kasmCyclesLiteral ) . getInteger ( ) ;
}
asm . addInlinedKickAsm ( statementKasm . getKickAsmCode ( ) , asmBytes , asmCycles , statementKasm . getDeclaredClobber ( ) ) ;
}
/ * *
* Generate all block entry points ( phi transitions ) which have not already been generated .
*
* @param asm The ASM program to generate into
* @param toBlock The block to generate remaining entry points for .
* /
2023-04-08 20:52:15 +00:00
private void genBlockEntryPoints ( AsmProgram asm , Graph . Block toBlock ) {
2021-12-27 00:16:04 +00:00
PhiTransitions transitions = getTransitions ( toBlock ) ;
2023-04-08 20:52:15 +00:00
for ( var fromBlock : transitions . getFromBlocks ( ) ) {
2021-12-27 00:16:04 +00:00
PhiTransitions . PhiTransition transition = transitions . getTransition ( fromBlock ) ;
if ( ! transitionIsGenerated ( transition ) & & toBlock . getLabel ( ) . equals ( fromBlock . getConditionalSuccessor ( ) ) ) {
genBlockPhiTransition ( asm , fromBlock , toBlock , toBlock . getScope ( ) ) ;
asm . addInstruction ( " JMP " , CpuAddressingMode . ABS , AsmFormat . asmFix ( toBlock . getLabel ( ) . getLocalName ( ) ) , false ) ;
2019-03-18 19:54:08 +00:00
}
2021-12-27 00:16:04 +00:00
}
}
/ * *
* Generate a phi block transition . The transition performs all necessary assignment operations when moving from one block to another .
* The transition can be inserted either at the start of the to - block ( used for conditional jumps )
* or at the end of the from - block ( used at default block transitions and before JMP / JSR )
*
* @param asm The ASP program to generate the transition into .
* @param fromBlock The from - block
* @param toBlock The to - block
* @param scope The scope where the ASM code is being inserted . Used to ensure that labels inserted in the code reference the right variables .
* If the transition code is inserted in the to - block , this is the scope of the to - block .
* If the transition code is inserted in the from - block this is the scope of the from - block .
* /
2023-04-08 20:52:15 +00:00
private void genBlockPhiTransition ( AsmProgram asm , Graph . Block fromBlock , Graph . Block toBlock , ScopeRef scope ) {
2021-12-27 00:16:04 +00:00
PhiTransitions transitions = getTransitions ( toBlock ) ;
PhiTransitions . PhiTransition transition = transitions . getTransition ( fromBlock ) ;
if ( ! transitionIsGenerated ( transition ) ) {
Statement toFirstStatement = toBlock . getStatements ( ) . get ( 0 ) ;
String chunkSrc = " [ " + toFirstStatement . getIndex ( ) + " ] phi from " ;
2023-04-08 20:52:15 +00:00
for ( var fBlock : transition . getFromBlocks ( ) ) {
2021-12-27 00:16:04 +00:00
chunkSrc + = fBlock . getLabel ( ) . getFullName ( ) + " " ;
2019-04-04 15:44:14 +00:00
}
2021-12-27 00:16:04 +00:00
chunkSrc + = " to " + toBlock . getLabel ( ) . getFullName ( ) ;
asm . startChunk ( scope , toFirstStatement . getIndex ( ) , chunkSrc ) ;
2020-03-01 15:00:42 +00:00
asm . getCurrentChunk ( ) . setSubStatementId ( transition . getTransitionId ( ) ) ;
2023-04-08 20:52:15 +00:00
for ( var fBlock : transition . getFromBlocks ( ) ) {
2021-12-27 00:16:04 +00:00
asm . addLabel ( AsmFormat . asmFix ( toBlock . getLabel ( ) . getLocalName ( ) + " _from_ " + fBlock . getLabel ( ) . getLocalName ( ) ) ) ;
2017-10-22 22:48:22 +00:00
}
2021-12-27 00:16:04 +00:00
List < PhiTransitions . PhiTransition . PhiAssignment > assignments = transition . getAssignments ( ) ;
for ( PhiTransitions . PhiTransition . PhiAssignment assignment : assignments ) {
LValue lValue = assignment . getVariable ( ) ;
RValue rValue = assignment . getrValue ( ) ;
Statement statement = assignment . getPhiBlock ( ) ;
// Generate an ASM move fragment
asm . startChunk ( scope , statement . getIndex ( ) , " [ " + statement . getIndex ( ) + " ] phi " + lValue . toString ( program ) + " = " + rValue . toString ( program ) ) ;
asm . getCurrentChunk ( ) . setSubStatementId ( transition . getTransitionId ( ) ) ;
asm . getCurrentChunk ( ) . setSubStatementIdx ( assignment . getAssignmentIdx ( ) ) ;
if ( isRegisterCopy ( lValue , rValue ) ) {
asm . getCurrentChunk ( ) . setFragment ( " register_copy " ) ;
} else {
AsmFragmentCodeGenerator . generateAsm ( asm , AsmFragmentInstanceSpecBuilder . assignment ( lValue , rValue , program , scope ) , program ) ;
}
}
transitionSetGenerated ( transition ) ;
} else {
program . getLog ( ) . append ( " Already generated transition from " + fromBlock . getLabel ( ) + " to " + toBlock . getLabel ( ) + " - not generating it again! " ) ;
}
}
/ * *
* Get phi transitions for a specific to - block .
*
* @param toBlock The block
* @return The transitions into the block
* /
2023-04-08 20:52:15 +00:00
private PhiTransitions getTransitions ( Graph . Block toBlock ) {
2021-12-27 00:16:04 +00:00
return program . getPhiTransitions ( ) . get ( toBlock . getLabel ( ) ) ;
}
private Registers . Register getRegister ( RValue rValue ) {
if ( rValue instanceof VariableRef ) {
VariableRef rValueRef = ( VariableRef ) rValue ;
return program . getSymbolInfos ( ) . getVariable ( rValueRef ) . getAllocation ( ) ;
} else if ( rValue instanceof CastValue ) {
return getRegister ( ( ( CastValue ) rValue ) . getValue ( ) ) ;
} else {
return null ;
}
}
private boolean isRegisterCopy ( LValue lValue , RValue rValue ) {
return getRegister ( lValue ) ! = null & & getRegister ( rValue ) ! = null & & getRegister ( lValue ) . equals ( getRegister ( rValue ) ) ;
}
2018-01-01 20:25:11 +00:00
2017-05-15 15:01:11 +00:00
}