2017-12-06 23:23:30 +00:00
package millfork.env
2019-07-08 07:26:51 +00:00
import millfork.assembly.m6809. { MOpcode , NonExistent }
2018-12-31 12:20:32 +00:00
import millfork.assembly. { BranchingOpcodeMapping , Elidability }
2018-12-14 21:01:52 +00:00
import millfork. { env , _ }
2018-12-31 12:20:32 +00:00
import millfork.assembly.mos. { AddrMode , Opcode }
import millfork.assembly.z80._
2018-12-16 14:43:17 +00:00
import millfork.compiler. { AbstractExpressionCompiler , LabelGenerator }
2018-07-30 16:15:44 +00:00
import millfork.error.Logger
2017-12-06 23:23:30 +00:00
import millfork.node._
2018-08-07 15:31:41 +00:00
import millfork.output._
2018-12-30 17:55:03 +00:00
import org.apache.commons.lang3.StringUtils
2017-12-06 23:23:30 +00:00
2021-06-21 12:20:24 +00:00
import java.nio.file.Files
2017-12-06 23:23:30 +00:00
import scala.collection.mutable
2019-04-14 23:30:47 +00:00
import scala.collection.mutable.ListBuffer
2017-12-06 23:23:30 +00:00
/* *
* @author Karol Stasiak
*/
//noinspection NotImplementedCode
2019-04-16 14:56:23 +00:00
class Environment ( val parent : Option [ Environment ] , val prefix : String , val cpuFamily : CpuFamily . Value , val options : CompilationOptions ) {
2017-12-06 23:23:30 +00:00
2019-04-16 14:56:23 +00:00
@inline
def jobContext : JobContext = options . jobContext
2018-07-31 16:16:36 +00:00
@inline
def log : Logger = jobContext . log
@inline
def nextLabel : LabelGenerator = jobContext . nextLabel
2017-12-06 23:23:30 +00:00
2020-07-30 23:53:58 +00:00
private val constantsThatShouldHaveBeenImportedEarlier = new MutableMultimap [ String , Option [ Position ] ] ( )
2018-07-06 20:45:59 +00:00
private var baseStackOffset : Int = cpuFamily match {
case CpuFamily . M6502 => 0x101
case CpuFamily . I80 => 0
2019-05-31 15:03:35 +00:00
case CpuFamily . I86 => 0
2019-07-28 22:55:24 +00:00
case CpuFamily . M6809 => 0
2018-07-06 20:45:59 +00:00
}
2017-12-06 23:23:30 +00:00
2020-07-30 23:53:58 +00:00
def errorConstant ( msg : String , invalidExpression : Option [ Expression ] , position : Option [ Position ] = None ) : Constant = {
log . error ( msg , position . orElse ( invalidExpression . flatMap ( _ . position ) ) )
2019-07-19 13:47:36 +00:00
log . info ( "Did you forget to import an appropriate module?" )
2020-07-30 23:53:58 +00:00
invalidExpression . foreach ( markAsConstantsThatShouldHaveBeenImportedEarlier )
2018-07-30 16:15:44 +00:00
Constant . Zero
}
2017-12-06 23:23:30 +00:00
def genRelativeVariable ( constant : Constant , typ : Type , zeropage : Boolean ) : RelativeVariable = {
2018-12-16 20:07:04 +00:00
val variable = RelativeVariable ( nextLabel ( "rv" ) , constant , typ , zeropage = zeropage , declaredBank = None /* TODO */ , isVolatile = false )
2017-12-06 23:23:30 +00:00
addThing ( variable , None )
variable
}
def allThings : Environment = {
val allThings : Map [ String , Thing ] = things . values . map {
case m : FunctionInMemory =>
m . environment . getAllPrefixedThings
2018-02-01 21:39:38 +00:00
case m : MacroFunction =>
2017-12-06 23:23:30 +00:00
m . environment . getAllPrefixedThings
case _ => Map [ String , Thing ] ( )
} . fold ( things . toMap ) ( _ ++ _ )
2019-04-16 14:56:23 +00:00
val e = new Environment ( None , "" , cpuFamily , options )
2017-12-06 23:23:30 +00:00
e . things . clear ( )
e . things ++= allThings
e
}
private def getAllPrefixedThings = {
things . toMap . map { case ( n , th ) => ( if ( n . startsWith ( "." ) ) n else prefix + n , th ) }
}
def getAllLocalVariables : List [ Variable ] = things . values . flatMap {
case v : Variable =>
Some ( v )
case _ => None
} . toList
2017-12-19 17:58:33 +00:00
def allPreallocatables : List [ PreallocableThing ] = things . values . flatMap {
2017-12-06 23:23:30 +00:00
case m : NormalFunction => Some ( m )
case m : InitializedArray => Some ( m )
2017-12-19 17:58:33 +00:00
case m : InitializedMemoryVariable => Some ( m )
2017-12-06 23:23:30 +00:00
case _ => None
} . toList
def allConstants : List [ ConstantThing ] = things . values . flatMap {
case m : NormalFunction => m . environment . allConstants
2018-02-01 21:39:38 +00:00
case m : MacroFunction => m . environment . allConstants
2017-12-06 23:23:30 +00:00
case m : ConstantThing => List ( m )
case _ => Nil
} . toList
2018-03-15 22:09:19 +00:00
def getAllFixedAddressObjects : List [ ( String , Int , Int ) ] = {
2018-03-09 15:31:49 +00:00
things . values . flatMap {
2019-04-29 20:57:40 +00:00
case RelativeArray ( _ , NumericConstant ( addr , _ ) , size , declaredBank , _ , _ , _ ) =>
2018-03-15 22:09:19 +00:00
List ( ( declaredBank . getOrElse ( "default" ) , addr . toInt , size ) )
2018-12-16 20:07:04 +00:00
case RelativeVariable ( _ , NumericConstant ( addr , _ ) , typ , _ , declaredBank , _ ) =>
2018-03-15 22:09:19 +00:00
List ( ( declaredBank . getOrElse ( "default" ) , addr . toInt , typ . size ) )
2018-03-09 15:31:49 +00:00
case f : NormalFunction =>
f . environment . getAllFixedAddressObjects
case _ => Nil
} . toList
}
2018-04-14 23:29:47 +00:00
private def isLocalVariableName ( name : String ) : Boolean = name . contains ( '$' ) && ! name . endsWith ( "$" ) && {
val fname = name . substring ( 0 , name . indexOf ( '$' ) )
things . get ( fname ) -> parent . flatMap ( _ . things . get ( fname ) ) match {
case ( Some ( _ : NormalFunction ) , _ ) => true
case ( _ , Some ( _ : NormalFunction ) ) => true
case _ => false
}
}
2018-06-18 15:59:47 +00:00
def allocateVariables ( nf : Option [ NormalFunction ] ,
mem : CompiledMemory ,
callGraph : CallGraph ,
allocators : Map [ String , VariableAllocator ] ,
options : CompilationOptions ,
2021-11-03 20:48:45 +00:00
onEachVariable : ( String , ( String , Int ) ) => Unit ,
2021-09-20 22:09:59 +00:00
onEachVariableEnd : ( String , ( Char , Int ) ) => Unit ,
2018-06-18 15:59:47 +00:00
pass : Int ,
forZpOnly : Boolean ) : Unit = {
2018-07-03 20:34:45 +00:00
if ( forZpOnly && ! options . platform . hasZeroPage ) {
return
}
2019-10-21 23:39:11 +00:00
if ( nf . exists ( _ . name . endsWith ( ".trampoline" ) ) ) {
return
}
if ( log . traceEnabled ) log . trace ( "Allocating variables in " + nf . map ( f => "function " + f . name ) . getOrElse ( "global scope" ) )
2017-12-06 23:23:30 +00:00
val b = get [ Type ] ( "byte" )
val p = get [ Type ] ( "pointer" )
2017-12-23 23:09:22 +00:00
val params = nf . fold ( List [ String ] ( ) ) { f =>
2017-12-06 23:23:30 +00:00
f . params match {
2018-06-23 21:47:18 +00:00
case NormalParamSignature ( List ( MemoryVariable ( _ , typ , _ ) ) ) if typ . size == 1 && options . platform . cpuFamily == CpuFamily . M6502 =>
Nil
2018-07-23 23:43:59 +00:00
case NormalParamSignature ( List ( MemoryVariable ( _ , typ , _ ) ) ) if typ . size == 1 && options . platform . cpuFamily == CpuFamily . I80 =>
Nil
2020-01-03 22:08:54 +00:00
case NormalParamSignature ( List ( MemoryVariable ( _ , typ , _ ) ) ) if typ . size == 1 && options . platform . cpuFamily == CpuFamily . M6809 =>
Nil
2018-07-23 23:43:59 +00:00
case NormalParamSignature ( List ( MemoryVariable ( _ , typ , _ ) ) ) if typ . size == 2 && options . platform . cpuFamily == CpuFamily . I80 =>
Nil
2020-01-03 22:08:54 +00:00
case NormalParamSignature ( List ( MemoryVariable ( _ , typ , _ ) ) ) if typ . size == 2 && options . platform . cpuFamily == CpuFamily . M6809 =>
Nil
2018-07-30 12:33:16 +00:00
case NormalParamSignature ( List ( MemoryVariable ( _ , typ , _ ) ) ) if typ . size == 3 && options . platform . cpuFamily == CpuFamily . I80 =>
Nil
case NormalParamSignature ( List ( MemoryVariable ( _ , typ , _ ) ) ) if typ . size == 4 && options . platform . cpuFamily == CpuFamily . I80 =>
Nil
2017-12-06 23:23:30 +00:00
case NormalParamSignature ( ps ) =>
ps . map ( p => p . name )
case _ =>
Nil
}
} . toSet
2018-07-03 20:34:45 +00:00
def passForAlloc ( m : VariableAllocationMethod . Value ) : Int = if ( options . platform . hasZeroPage ) m match {
case VariableAllocationMethod . Zeropage => 1
2018-06-18 15:59:47 +00:00
case VariableAllocationMethod . Register => 2
case _ => 3
2018-07-03 20:34:45 +00:00
} else 3
2020-03-26 00:38:54 +00:00
def prioritize [ T ] ( list : List [ T ] ) ( hasPriority : T => Boolean ) : List [ T ] = list . filter ( hasPriority ) ++ list . filterNot ( hasPriority )
val toAdd = prioritize ( things . values . toList ) ( th => th . name == "__reg" ) . flatMap {
2018-06-18 15:59:47 +00:00
case m : UninitializedMemory if passForAlloc ( m . alloc ) = = pass && nf . isDefined == isLocalVariableName ( m . name ) && ! m . name . endsWith ( ".addr" ) && maybeGet [ Thing ] ( m . name + ".array" ) . isEmpty =>
2019-10-21 23:39:11 +00:00
if ( log . traceEnabled ) log . trace ( "Allocating " + m . name )
2017-12-06 23:23:30 +00:00
val vertex = if ( options . flag ( CompilationFlag . VariableOverlap ) ) {
nf . fold [ VariableVertex ] ( GlobalVertex ) { f =>
if ( m . alloc == VariableAllocationMethod . Static ) {
GlobalVertex
} else if ( params ( m . name ) ) {
ParamVertex ( f . name )
} else {
LocalVertex ( f . name )
}
}
} else GlobalVertex
2018-03-15 22:09:19 +00:00
val bank = m . bank ( options )
2019-06-14 09:39:11 +00:00
val bank0 = mem . banks ( bank )
2017-12-06 23:23:30 +00:00
m . alloc match {
case VariableAllocationMethod . None =>
Nil
case VariableAllocationMethod . Zeropage =>
2018-07-03 20:34:45 +00:00
if ( forZpOnly || ! options . platform . hasZeroPage ) {
2018-06-18 15:59:47 +00:00
val addr =
2019-06-14 09:39:11 +00:00
allocators ( bank ) . allocateBytes ( bank0 , callGraph , vertex , options , m . sizeInBytes , initialized = false , writeable = true , location = AllocationLocation . Zeropage , alignment = m . alignment )
2020-01-03 22:08:54 +00:00
if ( log . traceEnabled ) log . trace ( "addr $" + addr . toHexString )
2021-11-03 20:48:45 +00:00
onEachVariable ( m . name , bank -> addr )
2021-09-20 22:09:59 +00:00
onEachVariableEnd ( m . name , ( if ( m . isInstanceOf [ MfArray ] ) 'a' else 'v' ) -> ( addr + m . sizeInBytes - 1 ) )
2018-06-18 15:59:47 +00:00
List (
ConstantThing ( m . name . stripPrefix ( prefix ) + "`" , NumericConstant ( addr , 2 ) , p )
)
} else Nil
2018-02-01 21:39:38 +00:00
case VariableAllocationMethod . Auto | VariableAllocationMethod . Register | VariableAllocationMethod . Static =>
2020-03-17 20:08:43 +00:00
if ( m . alloc == VariableAllocationMethod . Register && options . flag ( CompilationFlag . FallbackValueUseWarning ) ) {
2020-03-26 17:52:46 +00:00
options . platform . cpuFamily match {
case CpuFamily . M6502 if m . sizeInBytes == 1 =>
log . warn ( s" Failed to inline variable ` ${ m . name } ` into a register " , None )
case CpuFamily . I80 | CpuFamily . I80 | CpuFamily . I86 | CpuFamily . M6809 if m . sizeInBytes <= 2 =>
log . warn ( s" Failed to inline variable ` ${ m . name } ` into a register " , None )
case _ =>
}
2018-03-14 17:50:58 +00:00
}
2018-06-18 15:59:47 +00:00
if ( m . sizeInBytes == 0 ) Nil else {
val graveName = m . name . stripPrefix ( prefix ) + "`"
if ( forZpOnly ) {
if ( bank == "default" ) {
2019-06-14 09:39:11 +00:00
allocators ( bank ) . tryAllocateZeropageBytes ( bank0 , callGraph , vertex , options , m . sizeInBytes , alignment = m . alignment ) match {
2018-06-18 15:59:47 +00:00
case None => Nil
case Some ( addr ) =>
2020-01-03 22:08:54 +00:00
if ( log . traceEnabled ) log . trace ( "addr $" + addr . toHexString )
2021-11-03 20:48:45 +00:00
onEachVariable ( m . name , bank -> addr )
2021-09-20 22:09:59 +00:00
onEachVariableEnd ( m . name , ( if ( m . isInstanceOf [ MfArray ] ) 'a' else 'v' ) -> ( addr + m . sizeInBytes - 1 ) )
2018-06-18 15:59:47 +00:00
List (
ConstantThing ( m . name . stripPrefix ( prefix ) + "`" , NumericConstant ( addr , 2 ) , p )
)
}
} else Nil
} else if ( things . contains ( graveName ) ) {
Nil
} else {
2019-06-14 09:39:11 +00:00
val addr = allocators ( bank ) . allocateBytes ( bank0 , callGraph , vertex , options , m . sizeInBytes , initialized = false , writeable = true , location = AllocationLocation . Either , alignment = m . alignment )
2020-01-03 22:08:54 +00:00
if ( log . traceEnabled ) log . trace ( "addr $" + addr . toHexString )
2021-11-03 20:48:45 +00:00
onEachVariable ( m . name , bank -> addr )
2021-09-20 22:09:59 +00:00
onEachVariableEnd ( m . name , ( if ( m . isInstanceOf [ MfArray ] ) 'a' else 'v' ) -> ( addr + m . sizeInBytes - 1 ) )
2017-12-06 23:23:30 +00:00
List (
2018-06-18 15:59:47 +00:00
ConstantThing ( graveName , NumericConstant ( addr , 2 ) , p )
2017-12-06 23:23:30 +00:00
)
2018-06-18 15:59:47 +00:00
}
2017-12-06 23:23:30 +00:00
}
}
case f : NormalFunction =>
2021-09-20 22:09:59 +00:00
f . environment . allocateVariables ( Some ( f ) , mem , callGraph , allocators , options , onEachVariable , onEachVariableEnd , pass , forZpOnly )
2017-12-06 23:23:30 +00:00
Nil
case _ => Nil
} . toList
val tagged : List [ ( String , Thing ) ] = toAdd . map ( x => x . name -> x )
things ++= tagged
}
2018-12-21 21:35:16 +00:00
var builtinsAdded : Boolean = false
2017-12-06 23:23:30 +00:00
val things : mutable.Map [ String , Thing ] = mutable . Map ( )
2018-12-21 21:35:16 +00:00
val pointiesUsed : mutable.Map [ String , Set [ String ] ] = mutable . Map ( )
2018-03-18 22:54:32 +00:00
val removedThings : mutable.Set [ String ] = mutable . Set ( )
2019-07-15 00:06:23 +00:00
val knownLocalLabels : mutable.Set [ ( String , Option [ Position ] ) ] = mutable . Set ( )
2017-12-06 23:23:30 +00:00
private def addThing ( t : Thing , position : Option [ Position ] ) : Unit = {
2018-12-21 21:35:16 +00:00
if ( assertNotDefined ( t . name , position ) ) {
2018-12-21 21:36:05 +00:00
things ( t . name . stripPrefix ( prefix ) ) = t
}
2018-12-21 21:35:16 +00:00
}
2017-12-06 23:23:30 +00:00
2019-05-02 11:24:21 +00:00
private def addThing ( localName : String , t : Thing , position : Option [ Position ] ) : Unit = {
if ( assertNotDefined ( t . name , position ) ) {
things ( localName ) = t
}
}
2019-04-16 14:34:17 +00:00
def getReturnedVariables ( statements : Seq [ Statement ] ) : Set [ String ] = {
statements . flatMap {
case ReturnStatement ( Some ( VariableExpression ( v ) ) ) => Set ( v )
case ReturnStatement ( _ ) => Set ( "/none/" , "|none|" )
case x : CompoundStatement => getReturnedVariables ( x . getChildStatements )
case _ => Set . empty [ String ]
} . toSet
}
def coerceLocalVariableIntoGlobalVariable ( localVarToRelativize : String , concreteGlobalTarget : String ) : Unit = {
log . trace ( s" Coercing $localVarToRelativize to $concreteGlobalTarget " )
def removeVariableImpl2 ( e : Environment , str : String ) : Unit = {
2019-07-09 20:39:20 +00:00
e . removeVariable ( str )
e . removeVariable ( str . stripPrefix ( prefix ) )
2019-04-17 21:04:46 +00:00
e . parent . foreach ( x => removeVariableImpl2 ( x , str ) )
2019-04-16 14:34:17 +00:00
}
removeVariableImpl2 ( this , prefix + localVarToRelativize )
val namePrefix = concreteGlobalTarget + '.'
root . things . filter { entry =>
entry . _1 == concreteGlobalTarget || entry . _1 . startsWith ( namePrefix )
} . foreach { entry =>
val name = entry . _1
val thing = entry . _2
val newName = if ( name == concreteGlobalTarget ) localVarToRelativize else localVarToRelativize + '.' + name . stripPrefix ( namePrefix )
val newThing = thing match {
case t : VariableInMemory => RelativeVariable ( prefix + newName , t . toAddress , t . typ , t . zeropage , t . declaredBank , t . isVolatile )
case t : ConstantThing => t . copy ( name = prefix + newName )
case t => println ( t ) ; ???
}
addThing ( newThing , None )
}
}
2017-12-06 23:23:30 +00:00
def removeVariable ( str : String ) : Unit = {
2018-07-30 16:15:44 +00:00
log . trace ( "Removing variable: " + str )
2018-03-18 22:54:32 +00:00
removeVariableImpl ( str )
}
private def removeVariableImpl ( str : String ) : Unit = {
2019-07-09 20:39:20 +00:00
def extractThingName ( fullName : String ) : String = {
2023-02-03 13:45:47 +00:00
val ix = fullName . indexOf ( '.' )
if ( ix < 0 ) return fullName
var result = fullName . substring ( 0 , ix )
val suffix = fullName . substring ( ix )
2019-07-09 20:39:20 +00:00
if ( suffix == ".return" || suffix . startsWith ( ".return." ) ) {
result += ".return"
}
result
}
2023-02-03 13:45:47 +00:00
val strWithoutPrefix = str . stripPrefix ( prefix )
2019-07-09 20:39:20 +00:00
val toRemove = things . keys . filter { n =>
val baseName = extractThingName ( n )
2023-02-03 13:45:47 +00:00
baseName == str || baseName == strWithoutPrefix
2019-07-09 20:39:20 +00:00
} . toSet
removedThings ++= toRemove . map ( _ . stripPrefix ( prefix ) )
things --= toRemove
2018-03-18 22:54:32 +00:00
parent . foreach ( _ removeVariableImpl str )
2017-12-06 23:23:30 +00:00
}
def get [ T <: Thing : Manifest ] ( name : String , position : Option [ Position ] = None ) : T = {
2019-07-26 22:58:10 +00:00
if ( name . startsWith ( "function." ) && implicitly [ Manifest [ T ] ] . runtimeClass . isAssignableFrom ( classOf [ PointerType ] ) ) {
val tokens = name . stripPrefix ( "function." ) . split ( "\\.to\\." , 2 )
if ( tokens . length == 2 ) {
return FunctionPointerType ( name , tokens ( 0 ) , tokens ( 1 ) , maybeGet [ Type ] ( tokens ( 0 ) ) , maybeGet [ Type ] ( tokens ( 1 ) ) ) . asInstanceOf [ T ]
}
}
2019-04-15 17:45:26 +00:00
if ( name . startsWith ( "pointer." ) && implicitly [ Manifest [ T ] ] . runtimeClass . isAssignableFrom ( classOf [ PointerType ] ) ) {
val targetName = name . stripPrefix ( "pointer." )
2020-11-10 23:28:21 +00:00
targetName match {
case "interrupt" => return InterruptPointerType . asInstanceOf [ T ]
case "kernal_interrupt" => return KernalInterruptPointerType . asInstanceOf [ T ]
case _ =>
val target = maybeGet [ VariableType ] ( targetName )
return PointerType ( name , targetName , target ) . asInstanceOf [ T ]
}
2019-04-15 17:45:26 +00:00
}
2017-12-06 23:23:30 +00:00
val clazz = implicitly [ Manifest [ T ] ] . runtimeClass
if ( things . contains ( name ) ) {
val t : Thing = things ( name )
2020-03-26 00:36:15 +00:00
if ( ( t ne null ) && clazz . isInstance ( t ) && ! t . isInstanceOf [ Alias ] ) {
2017-12-06 23:23:30 +00:00
t . asInstanceOf [ T ]
} else {
2018-07-11 23:23:38 +00:00
t match {
2021-11-12 01:10:07 +00:00
case Alias ( _ , target , deprectated , local ) =>
2020-03-17 20:08:43 +00:00
if ( deprectated && options . flag ( CompilationFlag . DeprecationWarning ) ) {
2018-07-30 16:15:44 +00:00
log . warn ( s" Alias ` $name ` is deprecated, use ` $target ` instead " , position )
2018-07-30 12:33:16 +00:00
}
2021-11-12 01:10:07 +00:00
if ( local ) get [ T ] ( target ) else root . get [ T ] ( target )
2019-10-31 11:14:52 +00:00
case _ => throw IdentifierHasWrongTypeOfThingException ( clazz , name , position )
2018-07-11 23:23:38 +00:00
}
2017-12-06 23:23:30 +00:00
}
} else parent . fold {
2018-12-30 17:55:03 +00:00
hintTypo ( name )
2019-10-31 11:14:52 +00:00
throw UndefinedIdentifierException ( clazz , name , position )
2017-12-06 23:23:30 +00:00
} {
_ . get [ T ] ( name , position )
}
}
2018-07-11 23:23:38 +00:00
2018-07-16 21:00:26 +00:00
def root : Environment = parent . fold ( this ) ( _ . root )
2017-12-06 23:23:30 +00:00
def maybeGet [ T <: Thing : Manifest ] ( name : String ) : Option [ T ] = {
2019-04-15 17:45:26 +00:00
if ( name . startsWith ( "pointer." ) && implicitly [ Manifest [ T ] ] . runtimeClass . isAssignableFrom ( classOf [ PointerType ] ) ) {
val targetName = name . stripPrefix ( "pointer." )
val target = maybeGet [ VariableType ] ( targetName )
return Some ( PointerType ( name , targetName , target ) ) . asInstanceOf [ Option [ T ] ]
}
2017-12-06 23:23:30 +00:00
if ( things . contains ( name ) ) {
val t : Thing = things ( name )
val clazz = implicitly [ Manifest [ T ] ] . runtimeClass
2018-12-16 14:43:17 +00:00
t match {
2021-11-12 01:10:07 +00:00
case Alias ( _ , target , deprectated , local ) =>
2020-03-17 20:08:43 +00:00
if ( deprectated && options . flag ( CompilationFlag . DeprecationWarning ) ) {
2018-12-16 14:43:17 +00:00
log . warn ( s" Alias ` $name ` is deprecated, use ` $target ` instead " )
}
2021-11-12 01:10:07 +00:00
if ( local ) maybeGet [ T ] ( target ) else root . maybeGet [ T ] ( target )
2018-12-16 14:43:17 +00:00
case _ =>
if ( ( t ne null ) && clazz . isInstance ( t ) ) {
Some ( t . asInstanceOf [ T ] )
} else {
None
}
2017-12-06 23:23:30 +00:00
}
} else parent . flatMap {
_ . maybeGet [ T ] ( name )
}
}
def getArrayOrPointer ( arrayName : String ) : Thing = {
2019-04-14 22:27:34 +00:00
maybeGet [ StackVariable ] ( arrayName ) .
orElse ( maybeGet [ ThingInMemory ] ( arrayName ) ) .
2017-12-06 23:23:30 +00:00
orElse ( maybeGet [ ThingInMemory ] ( arrayName + ".array" ) ) .
orElse ( maybeGet [ ConstantThing ] ( arrayName ) ) .
2019-06-24 20:32:29 +00:00
getOrElse {
log . error ( s" ` $arrayName ` is not an array or a pointer " )
get [ Thing ] ( "nullptr" )
}
2017-12-06 23:23:30 +00:00
}
2023-01-27 17:14:50 +00:00
@inline
final def identityPage : Constant = maybeGet [ MfArray ] ( "identity$" ) . fold ( Constant . Zero ) ( _ . toAddress )
2018-02-11 17:55:21 +00:00
def getPointy ( name : String ) : Pointy = {
InitializedMemoryVariable
UninitializedMemoryVariable
getArrayOrPointer ( name ) match {
2020-11-18 09:08:58 +00:00
case th @InitializedArray ( _ , _ , cs , _ , i , e , ro , _ , _ ) => ConstantPointy ( th . toAddress , Some ( name ) , Some ( e . alignedSize * cs . length ) , Some ( cs . length ) , i , e , th . alignment , readOnly = ro )
case th @UninitializedArray ( _ , elementCount , _ , i , e , ro , _ , _ ) => ConstantPointy ( th . toAddress , Some ( name ) , Some ( elementCount * e . alignedSize ) , Some ( elementCount / e . size ) , i , e , th . alignment , readOnly = ro )
2020-07-17 23:14:43 +00:00
case th @RelativeArray ( _ , _ , elementCount , _ , i , e , ro ) => ConstantPointy ( th . toAddress , Some ( name ) , Some ( elementCount * e . alignedSize ) , Some ( elementCount / e . size ) , i , e , NoAlignment , readOnly = ro )
2018-08-07 20:15:50 +00:00
case ConstantThing ( _ , value , typ ) if typ . size <= 2 && typ . isPointy =>
2019-04-15 17:45:26 +00:00
val e = get [ VariableType ] ( typ . pointerTargetName )
2018-07-20 20:46:53 +00:00
val w = get [ VariableType ] ( "word" )
2019-07-10 14:51:12 +00:00
ConstantPointy ( value , None , None , None , w , e , NoAlignment , readOnly = false )
2018-08-07 20:15:50 +00:00
case th : VariableInMemory if th . typ . isPointy =>
2019-04-15 17:45:26 +00:00
val e = get [ VariableType ] ( th . typ . pointerTargetName )
2018-07-20 20:46:53 +00:00
val w = get [ VariableType ] ( "word" )
2020-08-14 20:28:31 +00:00
VariablePointy ( th . toAddress , w , e , th . zeropage , th . isVolatile )
2019-04-14 22:27:34 +00:00
case th : StackVariable if th . typ . isPointy =>
2019-04-15 17:45:26 +00:00
val e = get [ VariableType ] ( th . typ . pointerTargetName )
2019-04-14 22:27:34 +00:00
val w = get [ VariableType ] ( "word" )
2019-04-15 17:45:26 +00:00
StackVariablePointy ( th . baseOffset , w , e )
2018-02-11 17:55:21 +00:00
case _ =>
2018-07-30 16:15:44 +00:00
log . error ( s" $name is not a valid pointer or array " )
2018-07-20 20:46:53 +00:00
val b = get [ VariableType ] ( "byte" )
val w = get [ VariableType ] ( "word" )
2019-07-10 14:51:12 +00:00
ConstantPointy ( Constant . Zero , None , None , None , w , b , NoAlignment , readOnly = false )
2018-02-11 17:55:21 +00:00
}
}
2017-12-06 23:23:30 +00:00
if ( parent . isEmpty ) {
addThing ( VoidType , None )
2019-04-16 14:56:23 +00:00
addThing ( NullType , None )
2017-12-06 23:23:30 +00:00
addThing ( BuiltInBooleanType , None )
2018-06-08 22:18:21 +00:00
val b = BasicPlainType ( "byte" , 1 )
val w = BasicPlainType ( "word" , 2 )
addThing ( b , None )
addThing ( w , None )
2018-07-30 12:33:16 +00:00
addThing ( Alias ( "int8" , "byte" ) , None )
addThing ( Alias ( "int16" , "word" ) , None )
addThing ( BasicPlainType ( "int24" , 3 ) , None )
addThing ( Alias ( "farword" , "int24" , deprecated = true ) , None )
addThing ( BasicPlainType ( "int32" , 4 ) , None )
addThing ( Alias ( "long" , "int32" ) , None )
addThing ( BasicPlainType ( "int40" , 5 ) , None )
addThing ( BasicPlainType ( "int48" , 6 ) , None )
addThing ( BasicPlainType ( "int56" , 7 ) , None )
addThing ( BasicPlainType ( "int64" , 8 ) , None )
addThing ( BasicPlainType ( "int72" , 9 ) , None )
addThing ( BasicPlainType ( "int80" , 10 ) , None )
addThing ( BasicPlainType ( "int88" , 11 ) , None )
addThing ( BasicPlainType ( "int96" , 12 ) , None )
addThing ( BasicPlainType ( "int104" , 13 ) , None )
addThing ( BasicPlainType ( "int112" , 14 ) , None )
addThing ( BasicPlainType ( "int120" , 15 ) , None )
addThing ( BasicPlainType ( "int128" , 16 ) , None )
2018-08-07 20:15:50 +00:00
val p = DerivedPlainType ( "pointer" , w , isSigned = false , isPointy = true )
2018-08-07 15:37:09 +00:00
addThing ( p , None )
2018-08-07 20:15:50 +00:00
addThing ( DerivedPlainType ( "ubyte" , b , isSigned = false , isPointy = false ) , None )
addThing ( DerivedPlainType ( "sbyte" , b , isSigned = true , isPointy = false ) , None )
2018-07-30 12:33:16 +00:00
addThing ( Alias ( "unsigned8" , "ubyte" ) , None )
addThing ( Alias ( "signed8" , "sbyte" ) , None )
2019-11-03 22:33:41 +00:00
addThing ( DerivedPlainType ( "unsigned16" , w , isSigned = false , isPointy = false ) , None )
2020-03-25 22:53:26 +00:00
addThing ( DerivedPlainType ( "signed16" , w , isSigned = true , isPointy = false ) , None )
2020-11-10 23:28:21 +00:00
addThing ( InterruptPointerType , None )
addThing ( KernalInterruptPointerType , None )
2019-11-03 22:33:41 +00:00
for ( bits <- Seq ( 24 , 32 , 40 , 48 , 56 , 64 , 72 , 80 , 88 , 96 , 104 , 112 , 120 , 128 ) ) {
addThing ( DerivedPlainType ( "unsigned" + bits , get [ BasicPlainType ] ( "int" + bits ) , isSigned = false , isPointy = false ) , None )
}
if ( options . flag ( CompilationFlag . EnableInternalTestSyntax ) ) {
2020-03-25 22:53:26 +00:00
for ( bits <- Seq ( 24 , 32 , 40 , 48 , 56 , 64 , 72 , 80 , 88 , 96 , 104 , 112 , 120 , 128 ) ) {
addThing ( DerivedPlainType ( "signed" + bits , get [ BasicPlainType ] ( "int" + bits ) , isSigned = true , isPointy = false ) , None )
2019-11-03 22:33:41 +00:00
}
}
2017-12-06 23:23:30 +00:00
val trueType = ConstantBooleanType ( "true$" , value = true )
val falseType = ConstantBooleanType ( "false$" , value = false )
addThing ( trueType , None )
addThing ( falseType , None )
2018-12-19 00:14:53 +00:00
addThing ( ConstantThing ( "true" , NumericConstant ( 1 , 0 ) , trueType ) , None )
2017-12-06 23:23:30 +00:00
addThing ( ConstantThing ( "false" , NumericConstant ( 0 , 0 ) , falseType ) , None )
2019-07-26 17:02:32 +00:00
addThing ( FatBooleanType , None )
2019-04-16 14:56:23 +00:00
val nullptrValue = options . features . getOrElse ( "NULLPTR" , 0L )
val nullptrConstant = NumericConstant ( nullptrValue , 2 )
addThing ( ConstantThing ( "nullptr" , nullptrConstant , NullType ) , None )
addThing ( ConstantThing ( "nullptr.hi" , nullptrConstant . hiByte . quickSimplify , b ) , None )
addThing ( ConstantThing ( "nullptr.lo" , nullptrConstant . loByte . quickSimplify , b ) , None )
addThing ( ConstantThing ( "nullptr.raw" , nullptrConstant , p ) , None )
addThing ( ConstantThing ( "nullptr.raw.hi" , nullptrConstant . hiByte . quickSimplify , b ) , None )
addThing ( ConstantThing ( "nullptr.raw.lo" , nullptrConstant . loByte . quickSimplify , b ) , None )
2019-10-17 21:23:57 +00:00
val nullcharValue = options . features . getOrElse ( "NULLCHAR" , options . platform . defaultCodec . stringTerminator . head . toLong )
2019-10-31 11:20:20 +00:00
val nullcharScrValue = options . features . getOrElse ( "NULLCHAR_SCR" , options . platform . screenCodec . stringTerminator . head . toLong )
2019-08-15 22:46:11 +00:00
val nullcharConstant = NumericConstant ( nullcharValue , 1 )
2019-10-31 11:20:20 +00:00
val nullcharScrConstant = NumericConstant ( nullcharScrValue , 1 )
2019-08-15 22:46:11 +00:00
addThing ( ConstantThing ( "nullchar" , nullcharConstant , b ) , None )
2019-10-31 11:20:20 +00:00
addThing ( ConstantThing ( "nullchar_scr" , nullcharScrConstant , b ) , None )
2019-04-16 14:56:23 +00:00
val __zeropage_usage = UnexpandedConstant ( "__zeropage_usage" , 1 )
addThing ( ConstantThing ( "__zeropage_usage" , __zeropage_usage , b ) , None )
2019-06-28 14:28:49 +00:00
def addUnexpandedWordConstant ( name : String ) : Unit = {
2019-09-02 21:22:07 +00:00
val c = UnexpandedConstant ( name , 2 )
addThing ( ConstantThing ( name , c , w ) , None )
addThing ( ConstantThing ( name + ".hi" , c . hiByte , b ) , None )
addThing ( ConstantThing ( name + ".lo" , c . loByte , b ) , None )
}
def addUnexpandedByteConstant ( name : String ) : Unit = {
val c = UnexpandedConstant ( name , 1 )
addThing ( ConstantThing ( name , c , b ) , None )
}
def addUnexpandedPointerConstant ( name : String ) : Unit = {
2019-06-28 14:28:49 +00:00
val c = UnexpandedConstant ( name , 2 )
addThing ( ConstantThing ( name , c , p ) , None )
addThing ( ConstantThing ( name + ".hi" , c . hiByte , b ) , None )
addThing ( ConstantThing ( name + ".lo" , c . loByte , b ) , None )
}
2019-09-02 21:22:07 +00:00
addUnexpandedPointerConstant ( "__rwdata_start" )
addUnexpandedPointerConstant ( "__rwdata_end" )
2020-11-18 22:08:45 +00:00
addUnexpandedPointerConstant ( "__heap_start" )
2019-06-28 14:28:49 +00:00
if ( options . platform . ramInitialValuesBank . isDefined ) {
2019-09-02 21:22:07 +00:00
addUnexpandedPointerConstant ( "__rwdata_init_start" )
addUnexpandedPointerConstant ( "__rwdata_init_end" )
2019-06-28 14:28:49 +00:00
addUnexpandedWordConstant ( "__rwdata_size" )
}
2019-09-02 21:22:07 +00:00
for ( segment <- options . platform . bankNumbers . keys ) {
addUnexpandedPointerConstant ( s" segment. $segment .start " )
2021-01-13 18:55:11 +00:00
addUnexpandedPointerConstant ( s" segment. $segment .codeend " )
addUnexpandedPointerConstant ( s" segment. $segment .datastart " )
2019-09-02 21:22:07 +00:00
addUnexpandedPointerConstant ( s" segment. $segment .heapstart " )
addUnexpandedPointerConstant ( s" segment. $segment .end " )
addUnexpandedWordConstant ( s" segment. $segment .length " )
addUnexpandedByteConstant ( s" segment. $segment .bank " )
2021-01-13 18:35:11 +00:00
addUnexpandedByteConstant ( s" segment. $segment .fill " )
2019-09-02 21:22:07 +00:00
}
2019-10-23 09:03:55 +00:00
addThing ( ConstantThing ( "$0000" , Constant . WordZero , p ) , None )
2018-06-17 00:01:35 +00:00
addThing ( FlagBooleanType ( "set_carry" ,
2019-07-28 22:55:24 +00:00
BranchingOpcodeMapping ( Opcode . BCS , IfFlagSet ( ZFlag . C ) , MOpcode . BCS ) ,
BranchingOpcodeMapping ( Opcode . BCC , IfFlagClear ( ZFlag . C ) , MOpcode . BCC ) ) ,
2018-06-17 00:01:35 +00:00
None )
addThing ( FlagBooleanType ( "clear_carry" ,
2019-07-28 22:55:24 +00:00
BranchingOpcodeMapping ( Opcode . BCC , IfFlagClear ( ZFlag . C ) , MOpcode . BCC ) ,
BranchingOpcodeMapping ( Opcode . BCS , IfFlagSet ( ZFlag . C ) , MOpcode . BCS ) ) ,
2018-06-17 00:01:35 +00:00
None )
addThing ( FlagBooleanType ( "set_overflow" ,
2019-07-28 22:55:24 +00:00
BranchingOpcodeMapping ( Opcode . BVS , IfFlagSet ( ZFlag . P ) , MOpcode . BVS ) ,
BranchingOpcodeMapping ( Opcode . BVC , IfFlagClear ( ZFlag . P ) , MOpcode . BVC ) ) ,
2018-06-17 00:01:35 +00:00
None )
addThing ( FlagBooleanType ( "clear_overflow" ,
2019-07-28 22:55:24 +00:00
BranchingOpcodeMapping ( Opcode . BVC , IfFlagClear ( ZFlag . P ) , MOpcode . BVC ) ,
BranchingOpcodeMapping ( Opcode . BVS , IfFlagSet ( ZFlag . P ) , MOpcode . BVS ) ) ,
2018-06-17 00:01:35 +00:00
None )
addThing ( FlagBooleanType ( "set_zero" ,
2019-07-28 22:55:24 +00:00
BranchingOpcodeMapping ( Opcode . BEQ , IfFlagSet ( ZFlag . Z ) , MOpcode . BEQ ) ,
BranchingOpcodeMapping ( Opcode . BNE , IfFlagClear ( ZFlag . Z ) , MOpcode . BNE ) ) ,
2018-06-17 00:01:35 +00:00
None )
addThing ( FlagBooleanType ( "clear_zero" ,
2019-07-28 22:55:24 +00:00
BranchingOpcodeMapping ( Opcode . BNE , IfFlagClear ( ZFlag . Z ) , MOpcode . BNE ) ,
BranchingOpcodeMapping ( Opcode . BEQ , IfFlagSet ( ZFlag . Z ) , MOpcode . BEQ ) ) ,
2018-06-17 00:01:35 +00:00
None )
addThing ( FlagBooleanType ( "set_negative" ,
2019-07-28 22:55:24 +00:00
BranchingOpcodeMapping ( Opcode . BMI , IfFlagSet ( ZFlag . S ) , MOpcode . BMI ) ,
BranchingOpcodeMapping ( Opcode . BPL , IfFlagClear ( ZFlag . S ) , MOpcode . BPL ) ) ,
2018-06-17 00:01:35 +00:00
None )
addThing ( FlagBooleanType ( "clear_negative" ,
2019-07-28 22:55:24 +00:00
BranchingOpcodeMapping ( Opcode . BPL , IfFlagClear ( ZFlag . S ) , MOpcode . BPL ) ,
BranchingOpcodeMapping ( Opcode . BMI , IfFlagSet ( ZFlag . S ) , MOpcode . BMI ) ) ,
2018-06-17 00:01:35 +00:00
None )
2021-06-21 12:20:24 +00:00
val byte_and_pointer$ = StructType ( "byte_and_pointer$" , List ( FieldDesc ( "byte" , "zp" , false , None ) , FieldDesc ( "pointer" , "branch" , false , None ) ) , NoAlignment )
val hudson_transfer$ = StructType ( "hudson_transfer$" , List ( FieldDesc ( "word" , "a" , false , None ) , FieldDesc ( "word" , "b" , false , None ) , FieldDesc ( "word" , "c" , false , None ) ) , NoAlignment )
2019-11-24 00:30:30 +00:00
addThing ( byte_and_pointer$ , None )
addThing ( hudson_transfer$ , None )
2020-03-19 18:43:24 +00:00
Environment . constOnlyBuiltinFunction . foreach ( n => addThing ( ConstOnlyCallable ( n ) , None ) )
2018-12-21 21:35:16 +00:00
builtinsAdded = true
2017-12-06 23:23:30 +00:00
}
2018-12-21 21:35:16 +00:00
def assertNotDefined ( name : String , position : Option [ Position ] ) : Boolean = {
2019-10-07 23:33:55 +00:00
if ( builtinsAdded && Environment . invalidNewIdentifiers ( name ) ) {
if ( Environment . predefinedFunctions ( name ) ) {
log . error ( s" Cannot redefine a builtin predefined function ` $name ` " , position )
} else if ( Environment . neverIdentifiers ( name ) ) {
log . error ( s" Cannot the keyword ` $name ` as a name " , position )
} else if ( Environment . invalidNewIdentifiers ( name ) ) {
log . error ( s" Cannot redefine a builtin predefined identifier ` $name ` " , position )
}
2018-12-21 21:35:16 +00:00
false
2019-07-08 17:10:21 +00:00
} else if ( things . contains ( name ) || parent . exists ( _ . things . contains ( name ) ) || things . contains ( name . stripPrefix ( prefix ) ) ) {
if ( ! name . contains ( '.' ) ) {
if ( parent . isDefined ) {
log . error ( s" ` ${ name . stripPrefix ( prefix ) } ` is already defined in this function " , position )
} else {
log . error ( s" ` $name ` is already defined " , position )
}
}
2018-12-21 21:35:16 +00:00
false
} else {
true
}
2017-12-06 23:23:30 +00:00
}
def registerType ( stmt : TypeDefinitionStatement ) : Unit = {
// addThing(DerivedPlainType(stmt.name, get(stmt.parent)))
???
}
def sequence [ A ] ( a : List [ Option [ A ] ] ) : Option [ List [ A ] ] = a match {
case Nil => Some ( Nil )
case None : : _ => None
case Some ( r ) : : t => sequence ( t ) map ( r : : _ )
}
def evalVariableAndConstantSubParts ( e : Expression ) : ( Option [ Expression ] , Constant ) =
2018-03-07 11:36:21 +00:00
// TODO: prevent accidental negative indexing more robustly
2017-12-06 23:23:30 +00:00
e match {
case SumExpression ( params , false ) =>
val ( constants , variables ) = params . map { case ( sign , expr ) => ( sign , expr , eval ( expr ) ) } . partition ( _ . _3 . isDefined )
val constant = eval ( SumExpression ( constants . map ( x => ( x . _1 , x . _2 ) ) , decimal = false ) ) . get
val variable = variables match {
case Nil => None
case List ( ( false , x , _ ) ) => Some ( x )
case _ => Some ( SumExpression ( variables . map ( x => ( x . _1 , x . _2 ) ) , decimal = false ) )
}
2018-03-07 11:36:21 +00:00
variable match {
case None => variable -> constant
case Some ( x @VariableExpression ( v ) ) =>
if ( get [ Variable ] ( v ) . typ . isSigned ) Some ( FunctionCallExpression ( "^" , List ( x , LiteralExpression ( 0x80 , 1 ) ) ) ) -> ( constant - 128 ) . quickSimplify
else variable -> constant
case Some ( IndexedExpression ( _ , _ ) ) => variable -> constant
case Some ( LiteralExpression ( _ , _ ) ) => variable -> constant
2018-07-21 21:59:16 +00:00
case Some ( GeneratedConstantExpression ( _ , _ ) ) => variable -> constant
2018-03-07 11:36:21 +00:00
case Some ( SumExpression ( List ( negative @ ( true , _ ) ) , false ) ) =>
Some ( SumExpression ( List ( false -> LiteralExpression ( 0xff , 1 ) , negative ) , decimal = false ) ) -> ( constant - 255 ) . quickSimplify
case Some ( FunctionCallExpression (
"<<" | ">>" |
"<<'" | ">>'" |
"&" | "|" | "^" |
2018-03-15 23:34:24 +00:00
">>>>" |
2018-03-07 11:36:21 +00:00
"*" | "*'" , _ ) ) => variable -> constant
case Some ( FunctionCallExpression ( fname , _ ) ) =>
maybeGet [ Thing ] ( fname ) match {
case Some ( ff : MangledFunction ) =>
if ( ff . returnType . isSigned ) Some ( e ) -> Constant . Zero
else variable -> constant
2019-06-24 22:45:49 +00:00
case Some ( t : StructType ) =>
// dunno what to do
variable -> constant
case Some ( t : UnionType ) =>
// dunno what to do
None -> Constant . Zero
2018-03-07 11:36:21 +00:00
case Some ( t : Type ) =>
if ( t . isSigned ) Some ( e ) -> Constant . Zero
2019-09-20 16:33:41 +00:00
else variable -> constant . fitInto ( t )
2018-03-07 11:36:21 +00:00
case _ =>
// dunno what to do
Some ( e ) -> Constant . Zero
}
case _ =>
// dunno what to do
Some ( e ) -> Constant . Zero
}
2017-12-06 23:23:30 +00:00
case _ => eval ( e ) match {
case Some ( c ) => None -> c
case None => Some ( e ) -> Constant . Zero
}
}
2018-12-16 14:43:17 +00:00
def evalSizeof ( expr : Expression ) : Constant = {
val size : Int = expr match {
case VariableExpression ( name ) =>
maybeGet [ Thing ] ( name ) match {
case None =>
2019-10-31 11:14:52 +00:00
log . error ( s" ` $name ` is not defined " , expr . position )
2018-12-30 17:55:03 +00:00
hintTypo ( name )
2018-12-16 14:43:17 +00:00
1
case Some ( thing ) => thing match {
2020-07-17 23:14:43 +00:00
case t : Type => t . alignedSize
case v : Variable => v . typ . alignedSize
2019-09-14 01:34:32 +00:00
case a : MfArray => a . sizeInBytes
case ConstantThing ( _ , MemoryAddressConstant ( a : MfArray ) , _ ) => a . sizeInBytes
2018-12-16 14:43:17 +00:00
case x =>
log . error ( "Invalid parameter for expr: " + name )
1
}
}
case _ =>
2020-07-17 23:14:43 +00:00
AbstractExpressionCompiler . getExpressionType ( this , log , expr ) . alignedSize
2018-12-16 14:43:17 +00:00
}
NumericConstant ( size , Constant . minimumSize ( size ) )
}
2021-02-17 23:38:30 +00:00
def evalTypeof ( expr : Expression ) : Constant = {
2021-02-18 00:29:27 +00:00
val typeof : Int = expr match {
2021-02-17 23:38:30 +00:00
case VariableExpression ( name ) =>
maybeGet [ Thing ] ( name ) match {
case None =>
log . error ( s" ` $name ` is not defined " , expr . position )
hintTypo ( name )
1
case Some ( thing ) => thing match {
case t : Type => t . name . hashCode & 0xffff
case v : Variable => v . typ . name . hashCode & 0xffff
case a : MfArray => a . elementType . name . hashCode & 0xffff
case ConstantThing ( _ , MemoryAddressConstant ( a : MfArray ) , _ ) => a . elementType . name . hashCode & 0xffff
case x =>
log . error ( "Invalid parameter for expr: " + name )
1
}
}
case _ =>
2021-02-18 00:29:27 +00:00
AbstractExpressionCompiler . getExpressionType ( this , log , expr ) . name . hashCode & 0xffff
2021-02-17 23:38:30 +00:00
}
2021-02-18 00:29:27 +00:00
NumericConstant ( typeof , 2 )
2021-02-17 23:38:30 +00:00
}
2018-07-21 21:59:16 +00:00
def eval ( e : Expression , vars : Map [ String , Constant ] ) : Option [ Constant ] = evalImpl ( e , Some ( vars ) )
2020-09-20 22:14:39 +00:00
def eval ( e : Expression ) : Option [ Constant ] = {
if ( e . constantValueCache ne null ) return e . constantValueCache
val cv = evalImpl ( e , None )
e . constantValueCache = cv
cv
}
2018-07-21 21:59:16 +00:00
2018-06-18 00:52:14 +00:00
//noinspection ScalaUnnecessaryParentheses,ZeroIndexToHead
2019-10-31 11:14:52 +00:00
private def evalImpl ( e : Expression , vv : Option [ Map [ String , Constant ] ] ) : Option [ Constant ] = try { {
2017-12-06 23:23:30 +00:00
e match {
case LiteralExpression ( value , size ) => Some ( NumericConstant ( value , size ) )
2019-07-26 23:38:06 +00:00
case tl : TextLiteralExpression => Some ( getPointy ( getTextLiteralArrayName ( tl ) ) . asInstanceOf [ ConstantPointy ] . value )
2018-06-09 23:56:04 +00:00
case ConstantArrayElementExpression ( c ) => Some ( c )
2018-07-21 21:59:16 +00:00
case GeneratedConstantExpression ( c , t ) => Some ( c )
2017-12-06 23:23:30 +00:00
case VariableExpression ( name ) =>
2020-09-01 20:00:07 +00:00
if ( name . startsWith ( "." ) ) return Some ( MemoryAddressConstant ( Label ( prefix + name ) ) )
2018-07-21 21:59:16 +00:00
vv match {
case Some ( m ) if m . contains ( name ) => Some ( m ( name ) )
2021-11-12 01:10:07 +00:00
case _ => maybeGet [ ConstantLikeThing ] ( name ) . map {
case x : ConstantThing => x . value
case x : FunctionInMemory => x . toAddress
}
2018-07-21 21:59:16 +00:00
}
2019-04-29 20:57:40 +00:00
case IndexedExpression ( arrName , index ) =>
getPointy ( arrName ) match {
2020-07-17 23:14:43 +00:00
case ConstantPointy ( MemoryAddressConstant ( arr : InitializedArray ) , _ , _ , _ , _ , _ , _ , _ ) if arr . readOnly && arr . elementType . alignedSize == 1 =>
2020-03-19 22:53:16 +00:00
evalImpl ( index , vv ) . flatMap {
2019-04-29 20:57:40 +00:00
case NumericConstant ( constIndex , _ ) =>
if ( constIndex >= 0 && constIndex < arr . sizeInBytes ) {
2020-03-19 22:53:16 +00:00
evalImpl ( arr . contents ( constIndex . toInt ) , vv )
2019-04-29 20:57:40 +00:00
} else None
case _ => None
}
case _ => None
}
2019-04-15 17:45:26 +00:00
case _ : DerefExpression => None
case _ : IndirectFieldExpression => None
case _ : DerefDebuggingExpression => None
2019-08-16 15:21:51 +00:00
case HalfWordExpression ( param , hi ) => evalImpl ( param , vv ) . map ( c => if ( hi ) c . hiByte else c . loByte )
2017-12-06 23:23:30 +00:00
case SumExpression ( params , decimal ) =>
params . map {
2018-07-21 21:59:16 +00:00
case ( minus , param ) => ( minus , evalImpl ( param , vv ) )
2017-12-06 23:23:30 +00:00
} . foldLeft ( Some ( Constant . Zero ) . asInstanceOf [ Option [ Constant ] ] ) { ( oc , pair ) =>
oc . flatMap { c =>
pair match {
case ( _ , None ) => None
case ( minus , Some ( addend ) ) =>
val op = if ( decimal ) {
if ( minus ) MathOperator . DecimalMinus else MathOperator . DecimalPlus
} else {
if ( minus ) MathOperator . Minus else MathOperator . Plus
}
Some ( CompoundConstant ( op , c , addend ) )
}
}
}
case SeparateBytesExpression ( h , l ) => for {
2018-07-21 21:59:16 +00:00
lc <- evalImpl ( l , vv )
hc <- evalImpl ( h , vv )
2017-12-06 23:23:30 +00:00
} yield hc . asl ( 8 ) + lc
2020-03-19 18:43:24 +00:00
case fce @FunctionCallExpression ( name , params ) =>
2017-12-06 23:23:30 +00:00
name match {
2018-12-16 14:43:17 +00:00
case "sizeof" =>
if ( params . size == 1 ) {
Some ( evalSizeof ( params . head ) )
} else {
log . error ( "Invalid number of parameters for `sizeof`" , e . position )
Some ( Constant . One )
}
2021-02-17 23:38:30 +00:00
case "typeof" =>
if ( params . size == 1 ) {
Some ( evalTypeof ( params . head ) )
} else {
log . error ( "Invalid number of parameters for `typeof`" , e . position )
Some ( Constant . Zero )
}
2018-03-19 20:58:51 +00:00
case "hi" =>
if ( params . size == 1 ) {
2020-03-19 22:53:16 +00:00
evalImpl ( params . head , vv ) . map ( _ . hiByte . quickSimplify )
2018-03-19 20:58:51 +00:00
} else {
2018-07-30 16:15:44 +00:00
log . error ( "Invalid number of parameters for `hi`" , e . position )
2018-03-19 20:58:51 +00:00
None
}
case "lo" =>
if ( params . size == 1 ) {
2020-03-19 22:53:16 +00:00
evalImpl ( params . head , vv ) . map ( _ . loByte . quickSimplify )
2018-03-19 20:58:51 +00:00
} else {
2018-07-30 16:15:44 +00:00
log . error ( "Invalid number of parameters for `lo`" , e . position )
2018-03-19 20:58:51 +00:00
None
}
2018-06-18 00:52:14 +00:00
case "sin" =>
if ( params . size == 2 ) {
2020-03-19 22:53:16 +00:00
( evalImpl ( params ( 0 ) , vv ) -> evalImpl ( params ( 1 ) , vv ) ) match {
2018-06-18 00:52:14 +00:00
case ( Some ( NumericConstant ( angle , _ ) ) , Some ( NumericConstant ( scale , _ ) ) ) =>
2018-08-08 21:52:47 +00:00
val value = ( scale * math . sin ( angle * math . Pi / 128 ) ) . round . toInt
2018-06-18 00:52:14 +00:00
Some ( Constant ( value ) )
case _ => None
}
} else {
2018-07-30 16:15:44 +00:00
log . error ( "Invalid number of parameters for `sin`" , e . position )
2018-06-18 00:52:14 +00:00
None
}
case "cos" =>
if ( params . size == 2 ) {
2020-03-19 22:53:16 +00:00
( evalImpl ( params ( 0 ) , vv ) -> evalImpl ( params ( 1 ) , vv ) ) match {
2018-06-18 00:52:14 +00:00
case ( Some ( NumericConstant ( angle , _ ) ) , Some ( NumericConstant ( scale , _ ) ) ) =>
2018-08-08 21:52:47 +00:00
val value = ( scale * math . cos ( angle * math . Pi / 128 ) ) . round . toInt
2018-06-18 00:52:14 +00:00
Some ( Constant ( value ) )
case _ => None
}
} else {
2018-07-30 16:15:44 +00:00
log . error ( "Invalid number of parameters for `cos`" , e . position )
2018-06-18 00:52:14 +00:00
None
}
2018-08-08 21:52:47 +00:00
case "tan" =>
if ( params . size == 2 ) {
2020-03-19 22:53:16 +00:00
( evalImpl ( params ( 0 ) , vv ) -> evalImpl ( params ( 1 ) , vv ) ) match {
2018-08-08 21:52:47 +00:00
case ( Some ( NumericConstant ( angle , _ ) ) , Some ( NumericConstant ( scale , _ ) ) ) =>
val value = ( scale * math . tan ( angle * math . Pi / 128 ) ) . round . toInt
Some ( Constant ( value ) )
case _ => None
}
} else {
log . error ( "Invalid number of parameters for `tan`" , e . position )
None
}
2020-03-19 18:43:24 +00:00
case "min" =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . Minimum , fce , vv )
2020-03-19 18:43:24 +00:00
case "max" =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . Maximum , fce , vv )
2020-03-19 18:43:24 +00:00
case "if" =>
if ( params . size == 3 ) {
eval ( params ( 0 ) ) . map ( _ . quickSimplify ) match {
case Some ( NumericConstant ( cond , _ ) ) =>
2020-03-19 22:53:16 +00:00
evalImpl ( params ( if ( cond != 0 ) 1 else 2 ) , vv )
2020-03-19 18:43:24 +00:00
case Some ( c ) =>
2020-03-19 22:53:16 +00:00
if ( c . isProvablyGreaterOrEqualThan ( 1 ) ) evalImpl ( params ( 1 ) , vv )
else if ( c . isProvablyZero ) evalImpl ( params ( 2 ) , vv )
2021-11-12 01:44:20 +00:00
else ( evalImpl ( params ( 1 ) , vv ) , evalImpl ( params ( 2 ) , vv ) ) match {
case ( Some ( t ) , Some ( f ) ) => Some ( IfConstant ( c , t , f ) )
case _ => None
}
2020-03-19 18:43:24 +00:00
case _ => None
}
} else {
log . error ( "Invalid number of parameters for `if`" , e . position )
None
}
2018-03-15 23:34:24 +00:00
case "nonet" =>
params match {
case List ( FunctionCallExpression ( "<<" , ps @List ( _ , _ ) ) ) =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . Shl9 , ps , vv )
2018-03-15 23:34:24 +00:00
case List ( FunctionCallExpression ( "<<'" , ps @List ( _ , _ ) ) ) =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . DecimalShl9 , ps , vv )
2018-12-14 14:43:12 +00:00
case List ( SumExpression ( ps @List ( ( false , _ ) , ( false , _ ) ) , false ) ) =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . Plus9 , ps . map ( _ . _2 ) , vv )
2018-12-14 14:43:12 +00:00
case List ( SumExpression ( ps @List ( ( false , _ ) , ( false , _ ) ) , true ) ) =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . DecimalPlus9 , ps . map ( _ . _2 ) , vv )
2018-03-19 20:58:51 +00:00
case List ( _ ) =>
None
2018-03-15 23:34:24 +00:00
case _ =>
2018-07-30 16:15:44 +00:00
log . error ( "Invalid number of parameters for `nonet`" , e . position )
2018-03-15 23:34:24 +00:00
None
}
2018-01-18 21:38:17 +00:00
case ">>'" =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . DecimalShr , params , vv )
2018-01-18 21:38:17 +00:00
case "<<'" =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . DecimalShl , params , vv )
2018-01-18 21:38:17 +00:00
case ">>" =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . Shr , params , vv )
2018-01-18 21:38:17 +00:00
case "<<" =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . Shl , params , vv )
2018-02-01 21:39:38 +00:00
case ">>>>" =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . Shr9 , params , vv )
2018-01-18 21:38:17 +00:00
case "*'" =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . DecimalTimes , params , vv )
2017-12-06 23:23:30 +00:00
case "*" =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . Times , params , vv )
2019-06-05 16:36:39 +00:00
case "/" =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . Divide , params , vv )
2019-06-05 16:36:39 +00:00
case "%%" =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . Modulo , params , vv )
2017-12-06 23:23:30 +00:00
case "&&" | "&" =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . And , params , vv )
2017-12-06 23:23:30 +00:00
case "^" =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . Exor , params , vv )
2017-12-06 23:23:30 +00:00
case "||" | "|" =>
2020-03-19 22:53:16 +00:00
constantOperation ( MathOperator . Or , params , vv )
2021-11-12 01:44:20 +00:00
case ">" => evalComparisons ( params , vv , MathOperator . Greater , _ > _ )
case "<" => evalComparisons ( params , vv , MathOperator . Less , _ < _ )
case ">=" => evalComparisons ( params , vv , MathOperator . GreaterEqual , _ >= _ )
case "<=" => evalComparisons ( params , vv , MathOperator . LessEqual , _ <= _ )
case "==" => evalComparisons ( params , vv , MathOperator . Equal , _ == _ )
2020-03-19 22:53:16 +00:00
case "!=" =>
sequence ( params . map ( p => evalImpl ( p , vv ) ) ) match {
case Some ( List ( NumericConstant ( n1 , _ ) , NumericConstant ( n2 , _ ) ) ) =>
Some ( if ( n1 != n2 ) Constant . One else Constant . Zero )
2021-11-12 01:44:20 +00:00
case Some ( List ( c1 , c2 ) ) =>
Some ( CompoundConstant ( MathOperator . NotEqual , c1 , c2 ) )
2020-03-19 22:53:16 +00:00
case _ => None
}
2017-12-06 23:23:30 +00:00
case _ =>
2020-03-19 22:53:16 +00:00
maybeGet [ Thing ] ( name ) match {
2019-06-24 22:45:49 +00:00
case Some ( t : StructType ) =>
if ( params . size == t . fields . size ) {
2020-03-19 22:53:16 +00:00
sequence ( params . map ( p => evalImpl ( p , vv ) ) ) . map ( fields => StructureConstant ( t , fields . zip ( t . fields ) . map {
2019-09-20 16:33:41 +00:00
case ( fieldConst , fieldDesc ) =>
2021-02-22 22:23:00 +00:00
if ( fieldDesc . arraySize . isDefined ) {
log . error ( s" Cannot define a struct literal for a struct type ${ t . name } with array fields " , fce . position )
}
2019-09-20 16:33:41 +00:00
fieldConst . fitInto ( get [ Type ] ( fieldDesc . typeName ) )
} ) )
2019-06-24 22:45:49 +00:00
} else None
2020-03-19 22:53:16 +00:00
case Some ( n : NormalFunction ) if n . isConstPure =>
if ( params . size == n . params . length ) {
sequence ( params . map ( p => evalImpl ( p , vv ) ) ) match {
case Some ( args ) => ConstPureFunctions . eval ( this , n , args )
case _ => None
}
} else None
2019-06-24 22:45:49 +00:00
case Some ( _ : UnionType ) =>
None
2020-03-19 22:53:16 +00:00
case Some ( t : Type ) =>
2019-06-24 22:45:49 +00:00
if ( params . size == 1 ) {
2019-08-04 15:16:01 +00:00
eval ( params . head ) . map { c =>
2019-09-20 16:33:41 +00:00
c . fitInto ( t )
2019-08-04 15:16:01 +00:00
}
2019-06-24 22:45:49 +00:00
} else None
case _ => None
2018-07-20 20:46:53 +00:00
}
2017-12-06 23:23:30 +00:00
}
}
2019-10-31 11:14:52 +00:00
} . map ( _ . quickSimplify ) } catch {
case ez : NonFatalCompilationException =>
log . error ( ez . getMessage , e . position )
None
}
2017-12-06 23:23:30 +00:00
2019-09-24 23:17:23 +00:00
def debugConstness ( item : Expression ) : Unit = {
if ( ! log . debugEnabled ) return
if ( eval ( item ) . isEmpty ) {
log . debug ( s" $item is not const! " )
item match {
case FunctionCallExpression ( _ , expressions ) => expressions . foreach ( debugConstness )
case SumExpression ( expressions , _ ) => expressions . map ( _ . _2 ) . foreach ( debugConstness )
case SeparateBytesExpression ( b1 , b2 ) =>
debugConstness ( b1 )
debugConstness ( b2 )
case _ =>
}
}
}
2021-11-12 01:44:20 +00:00
private def evalComparisons ( params : List [ Expression ] , vv : Option [ Map [ String , Constant ] ] , operator : MathOperator . Value , cond : ( Long , Long ) => Boolean ) : Option [ Constant ] = {
2020-03-19 22:53:16 +00:00
if ( params . size < 2 ) return None
2021-11-12 01:44:20 +00:00
val paramsEvaluated = params . map { e =>
evalImpl ( e , vv )
}
val numbers = sequence ( paramsEvaluated . map {
case Some ( NumericConstant ( n , _ ) ) => Some ( n )
case _ => None
2020-03-19 22:53:16 +00:00
} )
2021-11-12 01:44:20 +00:00
if ( numbers . isEmpty ) {
paramsEvaluated match {
case List ( Some ( c1 ) , Some ( c2 ) ) =>
return Some ( CompoundConstant ( operator , c1 , c2 ) )
case _ =>
}
}
2020-03-19 22:53:16 +00:00
numbers . map { ns =>
if ( ns . init . zip ( ns . tail ) . forall ( cond . tupled ) ) Constant . One else Constant . Zero
}
}
private def constantOperation ( op : MathOperator . Value , fce : FunctionCallExpression , vv : Option [ Map [ String , Constant ] ] ) : Option [ Constant ] = {
2020-03-19 18:43:24 +00:00
val params = fce . expressions
if ( params . isEmpty ) {
log . error ( s" Invalid number of parameters for ` ${ fce . functionName } ` " , fce . position )
None
}
2020-03-19 22:53:16 +00:00
constantOperation ( op , fce . expressions , vv )
2020-03-19 18:43:24 +00:00
}
2020-03-19 22:53:16 +00:00
private def constantOperation ( op : MathOperator . Value , params : List [ Expression ] , vv : Option [ Map [ String , Constant ] ] ) : Option [ Constant ] = {
params . map ( p => evalImpl ( p , vv ) ) . reduceLeft [ Option [ Constant ] ] { ( oc , om ) =>
2018-01-01 21:13:05 +00:00
for {
c <- oc
m <- om
} yield CompoundConstant ( op , c , m )
}
}
2021-02-24 01:32:00 +00:00
def evalForAsm ( e : Expression , silent : Boolean = false ) : Option [ Constant ] = {
2020-03-15 22:48:27 +00:00
e match {
2021-02-24 01:32:00 +00:00
case FunctionCallExpression ( "label" , List ( VariableExpression ( name ) ) ) if ( ! name . contains ( "." ) ) =>
return Some ( Label ( name ) . toAddress )
case FunctionCallExpression ( "label" , List ( VariableExpression ( name ) ) ) if ( name . startsWith ( "." ) ) =>
return Some ( Label ( prefix + name ) . toAddress )
case FunctionCallExpression ( "label" , _ ) =>
log . error ( "Invalid label reference" , e . position )
return Some ( Constant . Zero )
2020-03-15 22:48:27 +00:00
case _ =>
}
2018-01-01 21:13:05 +00:00
e match {
case LiteralExpression ( value , size ) => Some ( NumericConstant ( value , size ) )
2018-06-09 23:56:04 +00:00
case ConstantArrayElementExpression ( c ) => Some ( c )
2018-01-01 21:13:05 +00:00
case VariableExpression ( name ) =>
2019-08-16 15:21:51 +00:00
val result = maybeGet [ ConstantThing ] ( name ) . map ( _ . value ) . orElse ( maybeGet [ ThingInMemory ] ( name ) . map ( _ . toAddress ) )
2020-07-31 11:29:48 +00:00
result match {
case Some ( x ) => result
case None =>
2020-09-01 20:00:07 +00:00
if ( name . startsWith ( "." ) ) Some ( Label ( prefix + name ) . toAddress )
2020-07-31 11:29:48 +00:00
else {
2021-02-24 01:32:00 +00:00
if ( ! silent ) log . warn ( s" $name is not known. If it is a label, consider wrapping it in label(...). " , e . position )
2020-07-31 11:29:48 +00:00
None
}
}
2018-01-01 21:13:05 +00:00
case IndexedExpression ( name , index ) => ( evalForAsm ( VariableExpression ( name ) ) , evalForAsm ( index ) ) match {
case ( Some ( a ) , Some ( b ) ) => Some ( CompoundConstant ( MathOperator . Plus , a , b ) . quickSimplify )
}
2019-08-16 15:21:51 +00:00
case HalfWordExpression ( param , hi ) => evalForAsm ( param ) . map ( c => if ( hi ) c . hiByte else c . loByte )
2018-01-01 21:13:05 +00:00
case SumExpression ( params , decimal ) =>
params . map {
case ( minus , param ) => ( minus , evalForAsm ( param ) )
} . foldLeft ( Some ( Constant . Zero ) . asInstanceOf [ Option [ Constant ] ] ) { ( oc , pair ) =>
oc . flatMap { c =>
pair match {
case ( _ , None ) => None
case ( minus , Some ( addend ) ) =>
val op = if ( decimal ) {
if ( minus ) MathOperator . DecimalMinus else MathOperator . DecimalPlus
} else {
if ( minus ) MathOperator . Minus else MathOperator . Plus
}
2018-08-03 11:06:23 +00:00
Some ( CompoundConstant ( op , c , addend ) . quickSimplify )
2018-01-01 21:13:05 +00:00
}
}
}
case SeparateBytesExpression ( h , l ) => for {
lc <- evalForAsm ( l )
hc <- evalForAsm ( h )
} yield hc . asl ( 8 ) + lc
case FunctionCallExpression ( name , params ) =>
name match {
case "*" =>
constantOperationForAsm ( MathOperator . Times , params )
case "&&" | "&" =>
constantOperationForAsm ( MathOperator . And , params )
case "^" =>
constantOperationForAsm ( MathOperator . Exor , params )
case "||" | "|" =>
constantOperationForAsm ( MathOperator . Or , params )
2020-03-17 01:03:44 +00:00
case "/" =>
constantBinaryOperationForAsm ( "/" , MathOperator . Divide , params )
case "%%" =>
constantBinaryOperationForAsm ( "%%" , MathOperator . Modulo , params )
case ">>" =>
constantBinaryOperationForAsm ( ">>" , MathOperator . Shr , params )
case "<<" =>
constantBinaryOperationForAsm ( "<<" , MathOperator . Shl , params )
2018-12-27 13:17:48 +00:00
case "hi" =>
oneArgFunctionForAsm ( _ . hiByte , params )
case "lo" =>
oneArgFunctionForAsm ( _ . loByte , params )
2020-03-17 01:03:44 +00:00
case ">>>>" | ">>'" | "<<'" | ">" | "<" =>
2020-07-30 23:53:58 +00:00
if ( ! silent ) log . error ( s" Operator ` $name ` not supported in inline assembly " , e . position )
2020-03-17 01:03:44 +00:00
None
case _ if name . endsWith ( "=" ) =>
2020-07-30 23:53:58 +00:00
if ( ! silent ) log . error ( s" Operator ` $name ` not supported in inline assembly " , e . position )
2020-03-17 01:03:44 +00:00
None
2018-12-27 13:17:48 +00:00
case "nonet" | "sin" | "cos" | "tan" =>
2020-07-30 23:53:58 +00:00
if ( ! silent ) log . error ( "Function not supported in inline assembly" , e . position )
2018-12-27 13:17:48 +00:00
None
2020-05-01 13:18:48 +00:00
case "sizeof" =>
2021-02-17 23:38:30 +00:00
if ( params . size == 1 ) {
Some ( evalSizeof ( params . head ) )
} else {
log . error ( "Invalid number of parameters for `sizeof`" , e . position )
Some ( Constant . One )
}
case "typeof" =>
if ( params . size == 1 ) {
Some ( evalTypeof ( params . head ) )
} else {
log . error ( "Invalid number of parameters for `typeof`" , e . position )
Some ( Constant . Zero )
}
2018-01-01 21:13:05 +00:00
case _ =>
None
}
}
}
2020-03-17 01:03:44 +00:00
private def constantBinaryOperationForAsm ( symbol : String , op : MathOperator . Value , params : List [ Expression ] ) = {
if ( params . length != 2 ) {
log . error ( s" Too many operands for the $symbol operator " , params . head . position )
}
params . map ( e => evalForAsm ( e ) ) . reduceLeft [ Option [ Constant ] ] { ( oc , om ) =>
for {
c <- oc
m <- om
} yield CompoundConstant ( op , c , m )
}
}
2018-01-01 21:13:05 +00:00
private def constantOperationForAsm ( op : MathOperator . Value , params : List [ Expression ] ) = {
2020-03-15 22:48:27 +00:00
params . map ( e => evalForAsm ( e ) ) . reduceLeft [ Option [ Constant ] ] { ( oc , om ) =>
2017-12-06 23:23:30 +00:00
for {
c <- oc
m <- om
} yield CompoundConstant ( op , c , m )
}
}
2018-12-27 13:17:48 +00:00
private def oneArgFunctionForAsm ( f : Constant => Constant , params : List [ Expression ] ) : Option [ Constant ] = {
if ( params . size != 1 ) {
log . error ( "Too many arguments" , params . headOption . flatMap ( _ . position ) )
return None
}
evalForAsm ( params . head ) . map ( f ) . map ( _ . quickSimplify )
}
2018-07-11 23:23:38 +00:00
def registerAlias ( stmt : AliasDefinitionStatement ) : Unit = {
addThing ( Alias ( stmt . name , stmt . target ) , stmt . position )
}
2018-07-20 20:46:53 +00:00
def registerEnum ( stmt : EnumDefinitionStatement ) : Unit = {
val count = if ( stmt . variants . nonEmpty && stmt . variants . forall ( _ . _2 . isEmpty ) ) {
val size = stmt . variants . size
addThing ( ConstantThing ( stmt . name + ".count" , NumericConstant ( size , 1 ) , get [ Type ] ( "byte" ) ) , stmt . position )
Some ( size )
} else None
2018-12-19 00:14:02 +00:00
if ( count . exists ( _ > 256 ) ) {
log . error ( s" Enum ` ${ stmt . name } has more than 256 constants. " , stmt . position )
}
2018-07-20 20:46:53 +00:00
val t = EnumType ( stmt . name , count )
addThing ( t , stmt . position )
var value = Constant . Zero
for ( ( name , optValue ) <- stmt . variants ) {
optValue match {
case Some ( v ) =>
2020-07-30 23:53:58 +00:00
value = eval ( v ) . getOrElse ( errorConstant ( s" Enum constant ` ${ stmt . name } . $name ` is not a constant " , Some ( v ) , stmt . position ) )
2018-07-20 20:46:53 +00:00
case _ =>
}
2019-09-20 16:33:41 +00:00
addThing ( ConstantThing ( name , value . fitInto ( t ) , t ) , stmt . position )
2018-07-20 20:46:53 +00:00
value += 1
}
}
2019-04-14 23:30:47 +00:00
def registerStruct ( stmt : StructDefinitionStatement ) : Unit = {
stmt . fields . foreach { f =>
2019-09-20 16:33:41 +00:00
if ( Environment . invalidFieldNames . contains ( f . fieldName ) ) {
log . error ( s" Invalid field name: ` ${ f . fieldName } ` " , stmt . position )
2019-04-14 23:30:47 +00:00
}
}
2020-07-17 23:14:43 +00:00
addThing ( StructType ( stmt . name , stmt . fields , stmt . alignment . getOrElse ( NoAlignment ) ) , stmt . position )
2019-04-14 23:30:47 +00:00
}
2019-04-15 17:45:26 +00:00
def registerUnion ( stmt : UnionDefinitionStatement ) : Unit = {
stmt . fields . foreach { f =>
2019-09-20 16:33:41 +00:00
if ( Environment . invalidFieldNames . contains ( f . fieldName ) ) {
log . error ( s" Invalid field name: ` ${ f . fieldName } ` " , stmt . position )
2019-04-15 17:45:26 +00:00
}
}
2020-07-17 23:14:43 +00:00
addThing ( UnionType ( stmt . name , stmt . fields , stmt . alignment . getOrElse ( NoAlignment ) ) , stmt . position )
2019-04-15 17:45:26 +00:00
}
2020-09-01 22:44:24 +00:00
def getTypeAlignment ( t : VariableType , path : Set [ String ] ) : MemoryAlignment = {
val name = t . name
if ( path . contains ( name ) ) return null
t match {
case s : CompoundVariableType =>
if ( s . mutableAlignment ne null ) return s . mutableAlignment
var alignment = s . baseAlignment
2021-06-21 12:20:24 +00:00
for ( ResolvedFieldDesc ( fieldType , _ , _ , _ ) <- s . mutableFieldsWithTypes ) {
2020-09-01 22:44:24 +00:00
val a = getTypeAlignment ( fieldType , path + name )
if ( a eq null ) return null
alignment = alignment & a
}
s . mutableAlignment = alignment
alignment
case _ => t . alignment
}
}
2019-12-30 10:50:18 +00:00
def getTypeSize ( t : VariableType , path : Set [ String ] ) : Int = {
val name = t . name
2019-04-14 23:30:47 +00:00
if ( path . contains ( name ) ) return - 1
t match {
case s : StructType =>
if ( s . mutableSize >= 0 ) s . mutableSize
else {
val newPath = path + name
var sum = 0
2021-06-21 12:20:24 +00:00
for ( ResolvedFieldDesc ( fieldType , _ , _ , indexTypeAndCount ) <- s . mutableFieldsWithTypes ) {
2021-02-22 22:23:00 +00:00
val fieldSize = getTypeSize ( fieldType , newPath ) * indexTypeAndCount . fold ( 1 ) ( _ . _2 )
2019-04-14 23:30:47 +00:00
if ( fieldSize < 0 ) return - 1
2020-09-01 22:44:24 +00:00
sum = fieldType . alignment . roundSizeUp ( sum )
2019-04-14 23:30:47 +00:00
sum += fieldSize
2020-09-01 22:44:24 +00:00
sum = fieldType . alignment . roundSizeUp ( sum )
2019-04-14 23:30:47 +00:00
}
s . mutableSize = sum
if ( sum > 0xff ) {
log . error ( s" Struct ` $name ` is larger than 255 bytes " )
}
val b = get [ Type ] ( "byte" )
var offset = 0
2021-06-21 12:20:24 +00:00
for ( ResolvedFieldDesc ( fieldType , fieldName , _ , indexTypeAndCount ) <- s . mutableFieldsWithTypes ) {
2020-09-01 22:44:24 +00:00
offset = fieldType . alignment . roundSizeUp ( offset )
2019-04-14 23:30:47 +00:00
addThing ( ConstantThing ( s" $name . $fieldName .offset " , NumericConstant ( offset , 1 ) , b ) , None )
2021-02-22 22:23:00 +00:00
offset += getTypeSize ( fieldType , newPath ) * indexTypeAndCount . fold ( 1 ) ( _ . _2 )
2020-09-01 22:44:24 +00:00
offset = fieldType . alignment . roundSizeUp ( offset )
2019-04-14 23:30:47 +00:00
}
sum
}
2019-04-15 17:45:26 +00:00
case s : UnionType =>
if ( s . mutableSize >= 0 ) s . mutableSize
else {
val newPath = path + name
var max = 0
2021-06-21 12:20:24 +00:00
for ( ResolvedFieldDesc ( fieldType , _ , _ , indexTypeAndCount ) <- s . mutableFieldsWithTypes ) {
2021-02-22 22:23:00 +00:00
val fieldSize = getTypeSize ( fieldType , newPath ) * indexTypeAndCount . fold ( 1 ) ( _ . _2 )
2019-04-15 17:45:26 +00:00
if ( fieldSize < 0 ) return - 1
max = max max fieldSize
}
s . mutableSize = max
if ( max > 0xff ) {
log . error ( s" Union ` $name ` is larger than 255 bytes " )
}
val b = get [ Type ] ( "byte" )
2021-06-21 12:20:24 +00:00
for ( ResolvedFieldDesc ( fieldType , fieldName , _ , _ ) <- s . mutableFieldsWithTypes ) {
2019-04-15 17:45:26 +00:00
addThing ( ConstantThing ( s" $name . $fieldName .offset " , NumericConstant ( 0 , 1 ) , b ) , None )
}
max
}
2019-04-14 23:30:47 +00:00
case _ => t . size
}
}
2018-12-21 21:36:05 +00:00
def collectPointies ( stmts : Seq [ Statement ] ) : Set [ String ] = {
val pointies : mutable.Set [ String ] = new mutable . HashSet ( )
2020-03-19 19:00:28 +00:00
pointies ++= stmts . flatMap ( _ . getAllPointies )
pointies ++= getAliases . filterKeys ( pointies ) . values
2018-12-21 21:36:05 +00:00
log . trace ( "Collected pointies: " + pointies )
pointies . toSet
}
2017-12-06 23:23:30 +00:00
def registerFunction ( stmt : FunctionDeclarationStatement , options : CompilationOptions ) : Unit = {
2018-12-21 21:36:05 +00:00
val pointies = collectPointies ( stmt . statements . getOrElse ( Seq . empty ) )
pointiesUsed ( stmt . name ) = pointies
2017-12-06 23:23:30 +00:00
val w = get [ Type ] ( "word" )
2019-07-15 00:06:23 +00:00
val p = get [ Type ] ( "pointer" )
2017-12-06 23:23:30 +00:00
val name = stmt . name
2020-03-19 18:43:24 +00:00
if ( Environment . constOnlyBuiltinFunction ( name ) ) {
log . error ( s" Cannot redefine a built-in function ` $name ` " , stmt . position )
}
2017-12-06 23:23:30 +00:00
val resultType = get [ Type ] ( stmt . resultType )
2018-12-16 20:07:27 +00:00
if ( stmt . name == "main" ) {
2020-03-17 20:08:43 +00:00
if ( stmt . resultType != "void" && options . flag ( CompilationFlag . UselessCodeWarning ) ) {
2018-12-16 20:07:27 +00:00
log . warn ( "`main` should return `void`." , stmt . position )
}
2020-03-17 20:08:43 +00:00
if ( stmt . params . nonEmpty && options . flag ( CompilationFlag . BuggyCodeWarning ) ) {
2018-12-16 20:07:27 +00:00
log . warn ( "`main` shouldn't have parameters." , stmt . position )
}
}
2017-12-06 23:23:30 +00:00
2018-07-30 16:15:44 +00:00
if ( stmt . reentrant && stmt . interrupt ) log . error ( s" Reentrant function ` $name ` cannot be an interrupt handler " , stmt . position )
if ( stmt . reentrant && stmt . params . nonEmpty ) log . error ( s" Reentrant function ` $name ` cannot have parameters " , stmt . position )
if ( stmt . interrupt && stmt . params . nonEmpty ) log . error ( s" Interrupt function ` $name ` cannot have parameters " , stmt . position )
2018-02-01 21:39:38 +00:00
if ( stmt . isMacro ) {
2017-12-06 23:23:30 +00:00
if ( ! stmt . assembly ) {
2018-07-30 16:15:44 +00:00
if ( resultType != VoidType ) log . error ( s" Macro non-assembly function ` $name ` must return void " , stmt . position )
2017-12-06 23:23:30 +00:00
}
2018-02-01 21:39:38 +00:00
if ( stmt . assembly && stmt . params . exists ( _ . assemblyParamPassingConvention . inNonInlinedOnly ) )
2018-07-30 16:15:44 +00:00
log . error ( s" Macro function ` $name ` cannot have by-variable parameters " , stmt . position )
2017-12-06 23:23:30 +00:00
} else {
if ( ! stmt . assembly ) {
if ( stmt . params . exists ( ! _ . assemblyParamPassingConvention . isInstanceOf [ ByVariable ] ) )
2018-07-30 16:15:44 +00:00
log . error ( s" Non-assembly function ` $name ` cannot have non-variable parameters " , stmt . position )
2017-12-06 23:23:30 +00:00
}
if ( stmt . params . exists ( _ . assemblyParamPassingConvention . inInlinedOnly ) )
2018-07-30 16:15:44 +00:00
log . error ( s" Non-macro function ` $name ` cannot have inlinable parameters " , stmt . position )
2017-12-06 23:23:30 +00:00
}
2019-07-30 20:49:32 +00:00
val isTrampoline = stmt . name . endsWith ( ".trampoline" )
val env = if ( isTrampoline ) {
// let's hope nothing goes wrong with this:
get [ FunctionInMemory ] ( stmt . name . stripSuffix ( ".trampoline" ) ) . environment
} else {
new Environment ( Some ( this ) , name + "$" , cpuFamily , options )
}
2020-03-19 19:00:28 +00:00
stmt . params . foreach ( p => env . registerParameter ( p , options , pointies ) )
2020-03-30 17:23:48 +00:00
def params : ParamSignature = if ( stmt . assembly || stmt . isMacro ) {
AssemblyOrMacroParamSignature ( stmt . params . map {
2017-12-06 23:23:30 +00:00
pd =>
val typ = env . get [ Type ] ( pd . typ )
pd . assemblyParamPassingConvention match {
case ByVariable ( vn ) =>
2020-03-30 17:23:48 +00:00
AssemblyOrMacroParam ( typ , env . get [ MemoryVariable ] ( vn ) , AssemblyParameterPassingBehaviour . Copy )
2018-06-17 00:01:35 +00:00
case ByMosRegister ( reg ) =>
2020-03-30 17:23:48 +00:00
AssemblyOrMacroParam ( typ , RegisterVariable ( reg , typ ) , AssemblyParameterPassingBehaviour . Copy )
2018-07-04 22:49:51 +00:00
case ByZRegister ( reg ) =>
2020-03-30 17:23:48 +00:00
AssemblyOrMacroParam ( typ , ZRegisterVariable ( reg , typ ) , AssemblyParameterPassingBehaviour . Copy )
2019-08-05 09:43:51 +00:00
case ByM6809Register ( reg ) =>
2020-03-30 17:23:48 +00:00
AssemblyOrMacroParam ( typ , M6809RegisterVariable ( reg , typ ) , AssemblyParameterPassingBehaviour . Copy )
2017-12-06 23:23:30 +00:00
case ByConstant ( vn ) =>
2020-03-30 17:23:48 +00:00
AssemblyOrMacroParam ( typ , Placeholder ( vn , typ ) , AssemblyParameterPassingBehaviour . ByConstant )
2017-12-06 23:23:30 +00:00
case ByReference ( vn ) =>
2020-03-30 17:23:48 +00:00
AssemblyOrMacroParam ( typ , Placeholder ( vn , typ ) , AssemblyParameterPassingBehaviour . ByReference )
case ByLazilyEvaluableExpressionVariable ( vn ) =>
AssemblyOrMacroParam ( typ , Placeholder ( vn , typ ) , AssemblyParameterPassingBehaviour . Eval )
2017-12-06 23:23:30 +00:00
}
} )
} else {
NormalParamSignature ( stmt . params . map { pd =>
2019-04-16 14:34:17 +00:00
env . get [ VariableInMemory ] ( pd . assemblyParamPassingConvention . asInstanceOf [ ByVariable ] . name )
2017-12-06 23:23:30 +00:00
} )
}
2019-04-16 14:34:17 +00:00
var hasElidedReturnVariable = false
val hasReturnVariable = resultType . size > Cpu . getMaxSizeReturnableViaRegisters ( options . platform . cpu , options )
if ( hasReturnVariable ) {
2020-11-18 09:08:58 +00:00
registerVariable ( VariableDeclarationStatement ( stmt . name + ".return" , stmt . resultType , None , global = true , stack = false , constant = false , volatile = false , register = false , None , None , Set . empty , None ) , options , isPointy = false )
2018-07-30 12:33:16 +00:00
}
2021-11-12 01:10:07 +00:00
val constants = mutable . MutableList [ VariableDeclarationStatement ] ( )
2017-12-06 23:23:30 +00:00
stmt . statements match {
case None =>
stmt . address match {
case None =>
2018-07-30 16:15:44 +00:00
log . error ( s" Extern function ` ${ stmt . name } `needs an address " , stmt . position )
2017-12-06 23:23:30 +00:00
case Some ( a ) =>
2020-07-30 23:53:58 +00:00
val addr = eval ( a ) . getOrElse ( errorConstant ( s" Address of ` ${ stmt . name } ` is not a constant " , Some ( a ) , stmt . position ) )
2017-12-06 23:23:30 +00:00
val mangled = ExternFunction (
name ,
resultType ,
params ,
addr ,
2018-03-15 22:09:19 +00:00
env ,
2020-11-18 09:08:58 +00:00
prepareFunctionOptimizationHints ( options , stmt ) ,
2018-03-15 22:09:19 +00:00
stmt . bank
2017-12-06 23:23:30 +00:00
)
addThing ( mangled , stmt . position )
2019-04-15 17:45:26 +00:00
registerAddressConstant ( mangled , stmt . position , options , None )
2017-12-06 23:23:30 +00:00
addThing ( ConstantThing ( name + '`' , addr , w ) , stmt . position )
}
case Some ( statements ) =>
2021-11-12 01:10:07 +00:00
if ( stmt . isMacro ) {
statements . foreach {
case v : VariableDeclarationStatement => constants += v
case _ => ( )
}
} else {
statements . foreach {
case v : VariableDeclarationStatement => env . registerVariable ( v , options , pointies ( v . name ) )
case a : ArrayDeclarationStatement => env . registerArray ( a , options )
case _ => ( )
}
2017-12-06 23:23:30 +00:00
}
2019-07-15 00:06:23 +00:00
def scanForLabels ( statement : Statement ) : Unit = statement match {
case c : CompoundStatement => c . getChildStatements . foreach ( scanForLabels )
case LabelStatement ( labelName ) => env . knownLocalLabels += ( labelName -> statement . position )
case _ => ( )
}
statements . foreach ( scanForLabels )
for ( ( knownLabel , position ) <- env . knownLocalLabels ) {
env . addThing ( knownLabel , ConstantThing ( env . prefix + knownLabel , Label ( env . prefix + knownLabel ) . toAddress , p ) , position )
}
// not all in-function gotos are allowed; warn about the provably wrong ones:
def checkLabels ( statements : Seq [ Statement ] ) = {
def getAllSafeLabels ( statements : Seq [ Statement ] ) : Seq [ String ] = statements . flatMap {
case _ : ForEachStatement => Nil
case c : CompoundStatement => getAllSafeLabels ( c . getChildStatements )
case LabelStatement ( labelName ) => Seq ( labelName )
case _ => Nil
}
def getAllSafeGotos ( statements : Seq [ Statement ] ) : Seq [ String ] = statements . flatMap {
case _ : ForEachStatement => Nil
case c : CompoundStatement => getAllSafeGotos ( c . getChildStatements )
case GotoStatement ( VariableExpression ( labelName ) ) if env . knownLocalLabels . exists ( _ . _1 . == ( labelName ) ) => Seq ( labelName )
case _ => Nil
}
def doOnlyCheck ( position : Option [ Position ] , statements : Seq [ Statement ] ) : Unit = {
val l = getAllSafeLabels ( statements ) . toSet
val g = getAllSafeGotos ( statements ) . toSet
val bad = g . & ( env . knownLocalLabels . map ( _ . _1 ) ) . -- ( l )
if ( bad . nonEmpty ) {
2020-03-17 20:08:43 +00:00
if ( options . flag ( CompilationFlag . BuggyCodeWarning ) ) log . warn ( "Detected cross-loop gotos to labels " + bad . mkString ( ", " ) , position )
2019-07-15 00:06:23 +00:00
}
}
def recurse ( statements : Seq [ Statement ] ) : Unit = statements . foreach {
case c : ForEachStatement =>
doOnlyCheck ( c . position , c . body )
recurse ( c . body )
case c : CompoundStatement => recurse ( c . getChildStatements )
case _ => Nil
}
doOnlyCheck ( stmt . position , statements )
recurse ( statements )
}
checkLabels ( statements )
2017-12-06 23:23:30 +00:00
val executableStatements = statements . flatMap {
case e : ExecutableStatement => Some ( e )
case _ => None
}
2019-04-16 14:34:17 +00:00
if ( hasReturnVariable ) {
val set = getReturnedVariables ( executableStatements )
if ( set . size == 1 ) {
env . maybeGet [ Variable ] ( set . head ) match {
case Some ( v : MemoryVariable ) =>
if ( ! v . isVolatile && v . typ == resultType && v . alloc == VariableAllocationMethod . Auto ) {
env . coerceLocalVariableIntoGlobalVariable ( set . head , stmt . name + ".return" )
hasElidedReturnVariable = true
}
case _ =>
}
}
}
2018-12-19 17:14:11 +00:00
val paramForAutomaticReturn : List [ Option [ Expression ] ] = if ( stmt . isMacro || stmt . assembly ) {
Nil
2020-03-19 22:53:16 +00:00
} else if ( executableStatements . isEmpty ) {
2018-12-19 17:14:11 +00:00
List ( None )
} else {
2020-03-19 22:53:16 +00:00
executableStatements . last match {
case s if s . isValidFunctionEnd => Nil
2018-12-19 17:14:11 +00:00
case WhileStatement ( VariableExpression ( tr ) , _ , _ , _ ) =>
if ( resultType . size > 0 && env . getBooleanConstant ( tr ) . contains ( true ) ) {
List ( Some ( LiteralExpression ( 0 , 1 ) ) ) // TODO: what if the loop is breakable?
} else List ( None )
case DoWhileStatement ( _ , _ , VariableExpression ( tr ) , _ ) =>
if ( resultType . size > 0 && env . getBooleanConstant ( tr ) . contains ( true ) ) {
List ( Some ( LiteralExpression ( 0 , 1 ) ) ) // TODO: what if the loop is breakable?
} else List ( None )
case _ =>
// None so the compiler warns
List ( None )
}
}
2018-02-01 21:39:38 +00:00
if ( stmt . isMacro ) {
2018-03-15 22:09:19 +00:00
if ( stmt . bank . isDefined ) {
2018-07-30 16:15:44 +00:00
log . error ( "Macro functions cannot be in a defined segment" , stmt . position )
2018-03-15 22:09:19 +00:00
}
2018-02-01 21:39:38 +00:00
val mangled = MacroFunction (
2017-12-06 23:23:30 +00:00
name ,
resultType ,
2020-03-30 17:23:48 +00:00
params . asInstanceOf [ AssemblyOrMacroParamSignature ] ,
stmt . assembly ,
2017-12-06 23:23:30 +00:00
env ,
2021-11-12 01:10:07 +00:00
constants . toList ,
2018-07-30 12:33:16 +00:00
executableStatements
2017-12-06 23:23:30 +00:00
)
addThing ( mangled , stmt . position )
} else {
2017-12-23 23:09:22 +00:00
val stackVariablesSize = env . things . values . map {
2017-12-06 23:23:30 +00:00
case StackVariable ( n , t , _ ) if ! n . contains ( "." ) => t . size
case _ => 0
} . sum
val mangled = NormalFunction (
name ,
resultType ,
params ,
env ,
stackVariablesSize ,
2020-07-30 23:53:58 +00:00
stmt . address . map ( a => this . eval ( a ) . getOrElse ( errorConstant ( s" Address of ` ${ stmt . name } ` is not a constant " , Some ( a ) , a . position ) ) ) ,
2018-12-19 17:14:11 +00:00
executableStatements ++ paramForAutomaticReturn . map ( param => ReturnStatement ( param ) . pos ( executableStatements . lastOption . fold ( stmt . position ) ( _ . position ) ) ) ,
2019-04-16 14:34:17 +00:00
hasElidedReturnVariable = hasElidedReturnVariable ,
2017-12-06 23:23:30 +00:00
interrupt = stmt . interrupt ,
2018-03-05 11:05:37 +00:00
kernalInterrupt = stmt . kernalInterrupt ,
2020-09-08 23:46:34 +00:00
inAssembly = stmt . assembly ,
2017-12-06 23:23:30 +00:00
reentrant = stmt . reentrant ,
2020-03-19 22:53:16 +00:00
isConstPure = stmt . constPure ,
2018-03-15 22:09:19 +00:00
position = stmt . position ,
2018-08-07 21:55:08 +00:00
declaredBank = stmt . bank ,
2020-11-18 09:08:58 +00:00
optimizationHints = prepareFunctionOptimizationHints ( options , stmt ) ,
2018-10-04 19:33:10 +00:00
alignment = stmt . alignment . getOrElse ( if ( name == "main" ) NoAlignment else defaultFunctionAlignment ( options , hot = true ) ) // TODO: decide actual hotness in a smarter way
2017-12-06 23:23:30 +00:00
)
addThing ( mangled , stmt . position )
2019-04-15 17:45:26 +00:00
registerAddressConstant ( mangled , stmt . position , options , None )
2020-03-19 22:53:16 +00:00
if ( mangled . isConstPure ) {
ConstPureFunctions . checkConstPure ( env , mangled )
}
2017-12-06 23:23:30 +00:00
}
}
2021-11-12 01:10:07 +00:00
if ( ! stmt . isMacro ) {
val alias = Alias ( "this.function" , name , local = true )
env . addThing ( "this.function" , alias , None )
env . expandAlias ( alias )
}
2017-12-06 23:23:30 +00:00
}
2019-07-26 22:58:10 +00:00
private def getFunctionPointerType ( f : FunctionInMemory ) = f . params . types match {
case List ( ) =>
2020-11-10 23:28:21 +00:00
if ( f . returnType == VoidType ) get [ Type ] ( "pointer.kernal_interrupt" )
else get [ Type ] ( "function.void.to." + f . returnType . name )
2019-07-26 22:58:10 +00:00
case p : : _ => // TODO: this only handles one type though!
get [ Type ] ( "function." + p . name + ".to." + f . returnType . name )
}
2019-07-26 23:38:06 +00:00
def getTextLiteralArrayName ( literal : TextLiteralExpression ) : String = {
val name = "textliteral$" ++ literal . characters . flatMap {
case LiteralExpression ( n , _ ) =>
f" $n %02x "
case _ => ???
}
if ( maybeGet [ Thing ] ( name ) . isEmpty ) {
2020-11-18 09:08:58 +00:00
root . registerArray ( ArrayDeclarationStatement ( name , None , None , "byte" , None , const = true , Some ( LiteralContents ( literal . characters ) ) , Set . empty , None , options . isBigEndian ) . pos ( literal . position ) , options )
2019-07-26 23:38:06 +00:00
}
name
}
2019-04-15 17:45:26 +00:00
private def registerAddressConstant ( thing : ThingInMemory , position : Option [ Position ] , options : CompilationOptions , targetType : Option [ Type ] ) : Unit = {
2019-07-26 22:58:10 +00:00
val b = get [ Type ] ( "byte" )
2021-01-13 18:35:11 +00:00
val w = get [ Type ] ( "word" )
val ptr = get [ Type ] ( "pointer" )
val segment = thing . bank ( options )
for ( bankNumber <- options . platform . bankNumbers . get ( segment ) ) {
addThing ( ConstantThing ( thing . name + ".segment" , NumericConstant ( bankNumber , 1 ) , b ) , position )
addThing ( ConstantThing ( thing . name + ".segment.bank" , NumericConstant ( bankNumber , 1 ) , b ) , position )
}
for ( bankFill <- options . platform . bankFill . get ( segment ) ) {
addThing ( ConstantThing ( thing . name + ".segment.fill" , NumericConstant ( bankFill , 1 ) , b ) , position )
}
addThing ( ConstantThing ( thing . name + ".segment.start" , UnexpandedConstant ( s" segment. $segment .start " , 2 ) , ptr ) , position )
2021-01-13 18:55:11 +00:00
addThing ( ConstantThing ( thing . name + ".segment.codeend" , UnexpandedConstant ( s" segment. $segment .codeend " , 2 ) , ptr ) , position )
addThing ( ConstantThing ( thing . name + ".segment.datastart" , UnexpandedConstant ( s" segment. $segment .datastart " , 2 ) , ptr ) , position )
2021-01-13 18:35:11 +00:00
addThing ( ConstantThing ( thing . name + ".segment.heapstart" , UnexpandedConstant ( s" segment. $segment .heapstart " , 2 ) , ptr ) , position )
addThing ( ConstantThing ( thing . name + ".segment.end" , UnexpandedConstant ( s" segment. $segment .end " , 2 ) , ptr ) , position )
addThing ( ConstantThing ( thing . name + ".segment.length" , UnexpandedConstant ( s" segment. $segment .length " , 2 ) , w ) , position )
2018-06-08 22:18:21 +00:00
if ( ! thing . zeropage && options . flag ( CompilationFlag . LUnixRelocatableCode ) ) {
2020-11-18 09:08:58 +00:00
val relocatable = UninitializedMemoryVariable ( thing . name + ".addr" , w , VariableAllocationMethod . Static , None , Set . empty , defaultVariableAlignment ( options , 2 ) , isVolatile = false )
2018-06-08 22:18:21 +00:00
val addr = relocatable . toAddress
addThing ( relocatable , position )
2018-12-16 20:07:04 +00:00
addThing ( RelativeVariable ( thing . name + ".addr.hi" , addr + 1 , b , zeropage = false , None , isVolatile = false ) , position )
addThing ( RelativeVariable ( thing . name + ".addr.lo" , addr , b , zeropage = false , None , isVolatile = false ) , position )
2019-04-15 17:45:26 +00:00
targetType . foreach { tt =>
val typedPointer = RelativeVariable ( thing . name + ".pointer" , addr , PointerType ( "pointer." + tt . name , tt . name , Some ( tt ) ) , zeropage = false , None , isVolatile = false )
addThing ( typedPointer , position )
addThing ( RelativeVariable ( thing . name + ".pointer.hi" , addr + 1 , b , zeropage = false , None , isVolatile = false ) , position )
addThing ( RelativeVariable ( thing . name + ".pointer.lo" , addr , b , zeropage = false , None , isVolatile = false ) , position )
}
2018-06-09 23:56:04 +00:00
val rawaddr = thing . toAddress
2021-01-13 18:35:11 +00:00
addThing ( ConstantThing ( thing . name + ".rawaddr" , rawaddr , ptr ) , position )
addThing ( ConstantThing ( thing . name + ".rawaddr.hi" , rawaddr . hiByte , b ) , position )
addThing ( ConstantThing ( thing . name + ".rawaddr.lo" , rawaddr . loByte , b ) , position )
2019-07-26 22:58:10 +00:00
thing match {
case f : FunctionInMemory if f . canBePointedTo =>
2019-07-30 20:49:32 +00:00
val actualAddr = if ( f . requiresTrampoline ( options ) ) {
registerFunctionTrampoline ( f ) . toAddress
} else {
addr
}
val typedPointer = RelativeVariable ( thing . name + ".pointer" , actualAddr , getFunctionPointerType ( f ) , zeropage = false , None , isVolatile = false )
2019-07-26 22:58:10 +00:00
addThing ( typedPointer , position )
2019-07-30 20:49:32 +00:00
addThing ( RelativeVariable ( thing . name + ".pointer.hi" , actualAddr + 1 , b , zeropage = false , None , isVolatile = false ) , position )
addThing ( RelativeVariable ( thing . name + ".pointer.lo" , actualAddr , b , zeropage = false , None , isVolatile = false ) , position )
2020-11-10 23:28:21 +00:00
case f : FunctionInMemory if f . interrupt =>
val typedPointer = RelativeVariable ( thing . name + ".pointer" , f . toAddress , InterruptPointerType , zeropage = false , None , isVolatile = false )
addThing ( typedPointer , position )
addThing ( RelativeVariable ( thing . name + ".pointer.hi" , f . toAddress + 1 , b , zeropage = false , None , isVolatile = false ) , position )
addThing ( RelativeVariable ( thing . name + ".pointer.lo" , f . toAddress , b , zeropage = false , None , isVolatile = false ) , position )
2019-07-26 22:58:10 +00:00
case _ =>
}
2018-06-08 22:18:21 +00:00
} else {
val addr = thing . toAddress
2021-01-13 18:35:11 +00:00
addThing ( ConstantThing ( thing . name + ".addr" , addr , ptr ) , position )
2019-07-26 22:58:10 +00:00
addThing ( ConstantThing ( thing . name + ".addr.hi" , addr . hiByte , b ) , position )
addThing ( ConstantThing ( thing . name + ".addr.lo" , addr . loByte , b ) , position )
2021-01-13 18:35:11 +00:00
addThing ( ConstantThing ( thing . name + ".rawaddr" , addr , ptr ) , position )
2019-07-26 22:58:10 +00:00
addThing ( ConstantThing ( thing . name + ".rawaddr.hi" , addr . hiByte , b ) , position )
addThing ( ConstantThing ( thing . name + ".rawaddr.lo" , addr . loByte , b ) , position )
2019-04-15 17:45:26 +00:00
targetType . foreach { tt =>
val pointerType = PointerType ( "pointer." + tt . name , tt . name , Some ( tt ) )
addThing ( ConstantThing ( thing . name + ".pointer" , addr , pointerType ) , position )
2019-07-26 22:58:10 +00:00
addThing ( ConstantThing ( thing . name + ".pointer.hi" , addr . hiByte , b ) , position )
addThing ( ConstantThing ( thing . name + ".pointer.lo" , addr . loByte , b ) , position )
}
thing match {
case f : FunctionInMemory if f . canBePointedTo =>
val pointerType = getFunctionPointerType ( f )
2019-07-30 20:49:32 +00:00
val actualAddr = if ( f . requiresTrampoline ( options ) ) {
registerFunctionTrampoline ( f ) . toAddress
} else {
addr
}
addThing ( ConstantThing ( thing . name + ".pointer" , actualAddr , pointerType ) , position )
addThing ( ConstantThing ( thing . name + ".pointer.hi" , actualAddr . hiByte , b ) , position )
addThing ( ConstantThing ( thing . name + ".pointer.lo" , actualAddr . loByte , b ) , position )
2020-11-10 23:28:21 +00:00
case f : FunctionInMemory if f . interrupt =>
addThing ( ConstantThing ( thing . name + ".pointer" , f . toAddress , InterruptPointerType ) , position )
addThing ( ConstantThing ( thing . name + ".pointer.hi" , f . toAddress . hiByte , b ) , position )
addThing ( ConstantThing ( thing . name + ".pointer.lo" , f . toAddress . loByte , b ) , position )
2019-07-26 22:58:10 +00:00
case _ =>
2019-04-15 17:45:26 +00:00
}
2018-06-08 22:18:21 +00:00
}
2017-12-06 23:23:30 +00:00
}
2019-07-30 20:49:32 +00:00
def registerFunctionTrampoline ( function : FunctionInMemory ) : FunctionInMemory = {
options . platform . cpuFamily match {
case CpuFamily . M6502 =>
function . params match {
case NormalParamSignature ( List ( param ) ) =>
import Opcode._
import AddrMode._
val localNameForParam = param . name . stripPrefix ( function . name + '$' )
root . registerFunction ( FunctionDeclarationStatement (
function . name + ".trampoline" ,
function . returnType . name ,
List ( ParameterDeclaration ( param . typ . name , ByMosRegister ( MosRegister . AX ) ) ) ,
Some ( function . bank ( options ) ) ,
2020-11-18 09:08:58 +00:00
None , Set . empty , None ,
2019-07-30 20:49:32 +00:00
Some ( List (
MosAssemblyStatement ( STA , Absolute , VariableExpression ( localNameForParam ) , Elidability . Volatile ) ,
MosAssemblyStatement ( STX , Absolute , VariableExpression ( localNameForParam ) # + # 1 , Elidability . Volatile ) ,
MosAssemblyStatement ( JMP , Absolute , VariableExpression ( function . name + ".addr" ) , Elidability . Elidable )
) ) ,
isMacro = false ,
inlinable = Some ( false ) ,
assembly = true ,
interrupt = false ,
kernalInterrupt = false ,
2020-03-19 22:53:16 +00:00
reentrant = false ,
constPure = function . isConstPure
2019-07-30 20:49:32 +00:00
) , options )
get [ FunctionInMemory ] ( function . name + ".trampoline" )
}
case _ => function
}
}
2020-03-19 19:00:28 +00:00
def registerParameter ( stmt : ParameterDeclaration , options : CompilationOptions , pointies : Set [ String ] ) : Unit = {
2017-12-06 23:23:30 +00:00
val typ = get [ Type ] ( stmt . typ )
val b = get [ Type ] ( "byte" )
2018-05-14 00:16:46 +00:00
val w = get [ Type ] ( "word" )
2017-12-06 23:23:30 +00:00
val p = get [ Type ] ( "pointer" )
stmt . assemblyParamPassingConvention match {
case ByVariable ( name ) =>
2020-03-26 00:38:54 +00:00
val zp = pointies ( name ) // TODO
2020-04-06 09:35:14 +00:00
val allocationMethod =
if ( pointies ( name ) ) VariableAllocationMethod . Zeropage
else if ( typ . isPointy && options . platform . cpuFamily == CpuFamily . M6502 ) VariableAllocationMethod . Register
else VariableAllocationMethod . Auto
2020-11-18 09:08:58 +00:00
val v = UninitializedMemoryVariable ( prefix + name , typ , allocationMethod , None , Set . empty , defaultVariableAlignment ( options , 2 ) , isVolatile = false )
2017-12-06 23:23:30 +00:00
addThing ( v , stmt . position )
2019-04-15 17:45:26 +00:00
registerAddressConstant ( v , stmt . position , options , Some ( typ ) )
2018-05-14 00:16:46 +00:00
val addr = v . toAddress
2021-06-21 12:20:24 +00:00
for ( Subvariable ( suffix , offset , vol , t , arraySize ) <- getSubvariables ( typ ) ) {
2019-12-30 10:50:18 +00:00
if ( arraySize . isDefined ) ??? // TODO
2021-06-21 12:20:24 +00:00
val subv = RelativeVariable ( v . name + suffix , addr + offset , t , zeropage = zp , None , isVolatile = v . isVolatile || vol )
2019-04-14 23:30:47 +00:00
addThing ( subv , stmt . position )
2019-04-15 17:45:26 +00:00
registerAddressConstant ( subv , stmt . position , options , Some ( t ) )
2017-12-06 23:23:30 +00:00
}
2020-03-30 17:23:48 +00:00
case ByLazilyEvaluableExpressionVariable ( _ ) => ( )
2018-06-17 00:01:35 +00:00
case ByMosRegister ( _ ) => ( )
2018-07-04 22:49:51 +00:00
case ByZRegister ( _ ) => ( )
2019-08-05 09:43:51 +00:00
case ByM6809Register ( _ ) => ( )
2017-12-06 23:23:30 +00:00
case ByConstant ( name ) =>
val v = ConstantThing ( prefix + name , UnexpandedConstant ( prefix + name , typ . size ) , typ )
addThing ( v , stmt . position )
case ByReference ( name ) =>
val addr = UnexpandedConstant ( prefix + name , typ . size )
2018-12-16 20:07:04 +00:00
val v = RelativeVariable ( prefix + name , addr , p , zeropage = false , None , isVolatile = false )
2017-12-06 23:23:30 +00:00
addThing ( v , stmt . position )
2018-12-16 20:07:04 +00:00
addThing ( RelativeVariable ( v . name + ".hi" , addr + 1 , b , zeropage = false , None , isVolatile = false ) , stmt . position )
addThing ( RelativeVariable ( v . name + ".lo" , addr , b , zeropage = false , None , isVolatile = false ) , stmt . position )
2017-12-06 23:23:30 +00:00
}
}
2018-01-30 16:38:32 +00:00
def registerUnnamedArray ( array : InitializedArray ) : Unit = {
val b = get [ Type ] ( "byte" )
val p = get [ Type ] ( "pointer" )
if ( ! array . name . endsWith ( ".array" ) ) ???
val pointerName = array . name . stripSuffix ( ".array" )
addThing ( ConstantThing ( pointerName , array . toAddress , p ) , None )
addThing ( ConstantThing ( pointerName + ".addr" , array . toAddress , p ) , None )
2018-06-09 23:56:04 +00:00
addThing ( ConstantThing ( pointerName + ".rawaddr" , array . toAddress , p ) , None )
2018-01-30 16:38:32 +00:00
addThing ( array , None )
}
2019-04-29 23:30:22 +00:00
def extractStructArrayContents ( expr : Expression , targetType : Option [ Type ] ) : List [ Expression ] = {
( targetType , expr ) match {
case ( Some ( tt : StructType ) , FunctionCallExpression ( fname , fieldValues ) ) =>
maybeGet [ Thing ] ( fname ) match {
case Some ( tt2 : StructType ) if tt2 . name == tt . name =>
if ( tt . fields . length != fieldValues . length ) {
log . error ( s" Invalid number of struct fields for struct const ` ${ tt . name } ` " , fieldValues . headOption . flatMap ( _ . position ) )
List . fill ( tt . size ) ( LiteralExpression ( 0 , 1 ) )
} else {
tt . fields . zip ( fieldValues ) . flatMap {
2021-06-21 12:20:24 +00:00
case ( FieldDesc ( fieldTypeName , _ , _ , count ) , expr ) =>
2019-12-30 10:50:18 +00:00
// TODO: handle array fields
if ( count . isDefined ) ???
extractStructArrayContents ( expr , Some ( get [ Type ] ( fieldTypeName ) ) )
2019-04-29 23:30:22 +00:00
}
}
case _ =>
log . error ( s" Invalid struct type: ` $fname ` " , expr . position )
List . fill ( tt . size ) ( LiteralExpression ( 0 , 1 ) )
}
case ( Some ( tt : StructType ) , _ ) =>
log . error ( s" Invalid struct initializer for type ` ${ tt . name } ` " , expr . position )
List . fill ( tt . size ) ( LiteralExpression ( 0 , 1 ) )
case ( Some ( tt : PlainType ) , _ ) =>
tt . size match {
case 1 => List ( expr )
case 2 => List ( FunctionCallExpression ( "lo" , List ( expr ) ) , FunctionCallExpression ( "hi" , List ( expr ) ) )
case n => List . tabulate ( n ) ( i => FunctionCallExpression ( "lo" , List ( FunctionCallExpression ( ">>" , List ( expr , LiteralExpression ( 8 * i , 1 ) ) ) ) ) )
}
case ( Some ( tt : PointerType ) , _ ) => List ( FunctionCallExpression ( "lo" , List ( expr ) ) , FunctionCallExpression ( "hi" , List ( expr ) ) )
case ( Some ( tt : EnumType ) , _ ) => List ( FunctionCallExpression ( "byte" , List ( expr ) ) )
case ( Some ( tt ) , _ ) =>
log . error ( "Invalid field type for use in array initializers" , expr . position )
List . fill ( tt . size ) ( LiteralExpression ( 0 , 1 ) )
case ( None , FunctionCallExpression ( fname , fieldValues ) ) =>
maybeGet [ Thing ] ( fname ) match {
case Some ( tt : StructType ) =>
if ( tt . fields . length != fieldValues . length ) {
log . error ( s" Invalid number of struct fields for struct const ` ${ tt . name } ` " , fieldValues . headOption . flatMap ( _ . position ) )
List . fill ( tt . size ) ( LiteralExpression ( 0 , 1 ) )
} else {
tt . fields . zip ( fieldValues ) . flatMap {
2021-06-21 12:20:24 +00:00
case ( FieldDesc ( fieldTypeName , _ , _ , count ) , expr ) =>
2019-12-30 10:50:18 +00:00
// TODO: handle array fields
if ( count . isDefined ) ???
extractStructArrayContents ( expr , Some ( get [ Type ] ( fieldTypeName ) ) )
2019-04-29 23:30:22 +00:00
}
}
case _ =>
log . error ( s" Invalid struct type: ` $fname ` " , expr . position )
Nil
}
case _ =>
log . error ( s" Invalid struct initializer for unknown type " , expr . position )
Nil
}
}
def checkIfArrayContentsAreSimple ( xs : CombinedContents ) : Unit = {
xs . contents . foreach {
case x : CombinedContents => checkIfArrayContentsAreSimple ( x )
case x : LiteralContents => ( )
case x => log . error ( s" Invalid struct array contents " , x . position )
}
}
2020-07-30 23:53:58 +00:00
//noinspection ScalaUnusedExpression
private def markAsConstantsThatShouldHaveBeenImportedEarlier ( node : Expression ) : Unit = {
node match {
case VariableExpression ( v ) =>
if ( eval ( node ) . isEmpty && evalForAsm ( node , silent = true ) . isEmpty ) {
log . error ( s" The constant $v is undefined " , node . position )
log . info ( "Did you forget to import an appropriate module?" )
constantsThatShouldHaveBeenImportedEarlier . addBinding ( v . takeWhile ( _ != '.' ) . mkString ( "" ) , node . position )
}
case FunctionCallExpression ( _ , params ) =>
params . foreach ( markAsConstantsThatShouldHaveBeenImportedEarlier )
case SumExpression ( params , _ ) =>
params . foreach ( i => markAsConstantsThatShouldHaveBeenImportedEarlier ( i . _2 ) )
case SeparateBytesExpression ( h , l ) =>
markAsConstantsThatShouldHaveBeenImportedEarlier ( h )
markAsConstantsThatShouldHaveBeenImportedEarlier ( l )
case IndexedExpression ( a , i ) =>
constantsThatShouldHaveBeenImportedEarlier . addBinding ( a , node . position )
markAsConstantsThatShouldHaveBeenImportedEarlier ( i )
2021-06-21 12:20:24 +00:00
case DerefExpression ( p , _ , _ , _ ) =>
2020-07-30 23:53:58 +00:00
markAsConstantsThatShouldHaveBeenImportedEarlier ( p )
case DerefDebuggingExpression ( p , _ ) =>
markAsConstantsThatShouldHaveBeenImportedEarlier ( p )
case _ =>
// not a variable
}
}
2018-03-25 20:57:15 +00:00
def extractArrayContents ( contents1 : ArrayContents ) : List [ Expression ] = contents1 match {
case LiteralContents ( xs ) => xs
2021-06-21 12:18:17 +00:00
case FileChunkContents ( filePath , startE , lengthE ) =>
val data = Files . readAllBytes ( filePath )
val p = contents1 . position
val slice = ( eval ( startE ) . map ( _ . quickSimplify ) , lengthE . map ( l => eval ( l ) . map ( _ . quickSimplify ) ) ) match {
case ( Some ( NumericConstant ( start , _ ) ) , Some ( Some ( NumericConstant ( length , _ ) ) ) ) =>
if ( data . length < start ) {
log . error ( s" File $filePath is shorter ( ${ data . length } B) that the start offset $start " , p )
Array . fill ( length . toInt ) ( 0. toByte )
} else if ( data . length < start + length ) {
log . error ( s" File $filePath is shorter ( ${ data . length } B) that the start offset plus length ${ start + length } " , p )
Array . fill ( length . toInt ) ( 0. toByte )
} else {
data . slice ( start . toInt , start . toInt + length . toInt )
}
case ( Some ( NumericConstant ( start , _ ) ) , None ) =>
if ( data . length < start ) {
log . error ( s" File $filePath is shorter ( ${ data . length } B) that the start offset $start " , p )
Array [ Byte ] ( 0 )
} else {
data . drop ( start . toInt )
}
case ( None , Some ( Some ( _ ) ) ) =>
log . error ( s" Start offset is not a constant " , p )
Array [ Byte ] ( 0 )
case ( _ , Some ( None ) ) =>
log . error ( s" Length is not a constant " , p )
Array [ Byte ] ( 0 )
case ( None , Some ( None ) ) =>
log . error ( s" Start offset and length are not constants " , p )
Array [ Byte ] ( 0 )
}
slice . map ( c => LiteralExpression ( c & 0xff , 1 ) ) . toList
2018-03-25 20:57:15 +00:00
case CombinedContents ( xs ) => xs . flatMap ( extractArrayContents )
2019-04-29 23:30:22 +00:00
case pc @ProcessedContents ( "struct" , xs : CombinedContents ) =>
checkIfArrayContentsAreSimple ( xs )
2019-07-29 20:51:08 +00:00
xs . getAllExpressions ( options . isBigEndian ) . flatMap ( x => extractStructArrayContents ( x , None ) )
2019-04-29 23:30:22 +00:00
case pc @ProcessedContents ( "struct" , _ ) =>
log . error ( s" Invalid struct array contents " , pc . position )
Nil
2019-07-29 20:51:08 +00:00
case pc @ProcessedContents ( f , xs ) => pc . getAllExpressions ( options . isBigEndian )
2020-07-30 23:53:58 +00:00
case flc @ForLoopContents ( v , start , end , direction , body ) =>
2018-03-25 20:57:15 +00:00
( eval ( start ) , eval ( end ) ) match {
case ( Some ( NumericConstant ( s , sz1 ) ) , Some ( NumericConstant ( e , sz2 ) ) ) =>
val size = sz1 max sz2
val range = ( direction match {
case ForDirection . To | ForDirection . ParallelTo => s . to ( e )
case ForDirection . Until | ForDirection . ParallelUntil => s . until ( e )
case ForDirection . DownTo => s . to ( e , - 1 )
} ) . toList
range . flatMap ( i => extractArrayContents ( body ) . map ( _ . replaceVariable ( v , LiteralExpression ( i , size ) ) ) )
case ( Some ( _ ) , Some ( _ ) ) =>
2020-07-30 23:53:58 +00:00
log . error ( "Array range bounds cannot be evaluated" , flc . position . orElse ( flc . start . position ) )
2018-03-25 20:57:15 +00:00
Nil
2020-07-30 23:53:58 +00:00
case ( a , b ) =>
if ( a . isEmpty ) {
log . error ( "Non-constant array range bounds" , flc . start . position )
}
if ( b . isEmpty ) {
log . error ( "Non-constant array range bounds" , flc . `end` . position )
}
if ( a . isEmpty ) {
markAsConstantsThatShouldHaveBeenImportedEarlier ( flc . start )
}
if ( b . isEmpty ) {
markAsConstantsThatShouldHaveBeenImportedEarlier ( flc . `end` )
}
2018-03-25 20:57:15 +00:00
Nil
}
}
2018-08-07 15:31:41 +00:00
private def defaultArrayAlignment ( options : CompilationOptions , size : Long ) : MemoryAlignment = {
2018-08-08 15:50:27 +00:00
if ( options . flag ( CompilationFlag . OptimizeForSpeed ) && size <= 256 && size != 0 ) WithinPageAlignment
2018-08-07 15:31:41 +00:00
else NoAlignment
}
private def defaultVariableAlignment ( options : CompilationOptions , size : Long ) : MemoryAlignment = {
if ( options . flag ( CompilationFlag . PreventJmpIndirectBug ) && size == 2 ) WithinPageAlignment
else NoAlignment
}
2018-08-07 21:55:08 +00:00
private def defaultFunctionAlignment ( options : CompilationOptions , hot : Boolean ) : MemoryAlignment = {
// TODO:
if ( hot && options . platform . cpuFamily == CpuFamily . M6502 &&
options . flag ( CompilationFlag . OptimizeForSonicSpeed ) ) WithinPageAlignment
else NoAlignment
}
2020-11-18 09:08:58 +00:00
def prepareFunctionOptimizationHints ( options : CompilationOptions , stmt : FunctionDeclarationStatement ) : Set [ String ] = {
if ( ! options . flag ( CompilationFlag . UseOptimizationHints ) ) return Set . empty
2021-03-14 23:44:14 +00:00
def warn ( msg : String ) : Unit = {
if ( options . flag ( CompilationFlag . UnsupportedOptimizationHintWarning ) ) {
log . warn ( msg , stmt . position )
}
}
2020-11-18 09:08:58 +00:00
val filteredFlags = stmt . optimizationHints . flatMap {
case f @ ( "hot" | "cold" | "idempotent" | "preserves_memory" | "inline" | "odd" | "even" ) =>
Seq ( f )
case f @ ( "preserves_a" | "preserves_x" | "preserves_y" | "preserves_c" )
if options . platform . cpuFamily == CpuFamily . M6502 =>
if ( stmt . statements . isDefined && ! stmt . assembly ) {
2021-03-14 23:44:14 +00:00
warn ( s" Cannot use the $f optimization hint on non-assembly functions " )
2020-11-18 09:08:58 +00:00
Nil
} else {
Seq ( f )
}
case f @ ( "preserves_a" | "preserves_b" | "preserves_d" | "preserves_c" | "preserves_x" | "preserves_y" | "preserves_u" )
if options . platform . cpuFamily == CpuFamily . M6809 =>
if ( stmt . statements . isDefined && ! stmt . assembly ) {
2021-03-14 23:44:14 +00:00
warn ( s" Cannot use the $f optimization hints on non-assembly functions " )
Nil
} else {
Seq ( f )
}
case f @ ( "preserves_a" | "preserves_bc" | "preserves_de" | "preserves_hl" | "preserves_cf" )
if options . platform . cpuFamily == CpuFamily . I80 =>
if ( stmt . statements . isDefined && ! stmt . assembly ) {
warn ( s" Cannot use the $f optimization hints on non-assembly functions " )
2020-11-18 09:08:58 +00:00
Nil
} else {
Seq ( f )
}
case f @ ( "preserves_dp" )
if options . platform . cpuFamily == CpuFamily . M6809 =>
Seq ( f )
case f =>
2021-03-14 23:44:14 +00:00
warn ( s" Unsupported function optimization hint: $f " )
2020-11-18 09:08:58 +00:00
Nil
}
if ( filteredFlags ( "hot" ) && filteredFlags ( "cold" ) ) {
2021-03-14 23:44:14 +00:00
warn ( s" Conflicting optimization hints used: `hot` and `cold` " )
2020-11-18 09:08:58 +00:00
}
if ( filteredFlags ( "even" ) && filteredFlags ( "odd" ) ) {
2021-03-14 23:44:14 +00:00
warn ( s" Conflicting optimization hints used: `even` and `odd` " )
2020-11-18 09:08:58 +00:00
}
if ( filteredFlags ( "even" ) || filteredFlags ( "odd" ) ) {
maybeGet [ Type ] ( stmt . resultType ) match {
case Some ( t ) if t . size < 1 =>
2021-03-14 23:44:14 +00:00
warn ( s" Cannot use `even` or `odd` hints with an empty return type " )
2020-11-18 09:08:58 +00:00
case Some ( t : CompoundVariableType ) =>
2021-03-14 23:44:14 +00:00
warn ( s" Cannot use `even` or `odd` hints with a compound return type " )
2020-11-18 09:08:58 +00:00
case _ =>
}
}
filteredFlags
}
def prepareVariableOptimizationHints ( options : CompilationOptions , stmt : VariableDeclarationStatement ) : Set [ String ] = {
if ( ! options . flag ( CompilationFlag . UseOptimizationHints ) ) return Set . empty
2021-03-14 23:44:14 +00:00
def warn ( msg : String ) : Unit = {
if ( options . flag ( CompilationFlag . UnsupportedOptimizationHintWarning ) ) {
log . warn ( msg , stmt . position )
}
}
2020-11-18 09:08:58 +00:00
val filteredFlags = stmt . optimizationHints . flatMap {
case f @ ( "odd" | "even" ) =>
Seq ( f )
case f =>
2021-03-14 23:44:14 +00:00
warn ( s" Unsupported variable optimization hint: $f " )
2020-11-18 09:08:58 +00:00
Nil
}
if ( filteredFlags ( "even" ) && filteredFlags ( "odd" ) ) {
2021-03-14 23:44:14 +00:00
warn ( s" Conflicting optimization hints used: `even` and `odd` " )
2020-11-18 09:08:58 +00:00
}
filteredFlags
}
//noinspection UnnecessaryPartialFunction
def prepareArrayOptimizationHints ( options : CompilationOptions , stmt : ArrayDeclarationStatement ) : Set [ String ] = {
if ( ! options . flag ( CompilationFlag . UseOptimizationHints ) ) return Set . empty
val filteredFlags : Set [ String ] = stmt . optimizationHints . flatMap {
case f =>
2021-03-14 23:44:14 +00:00
log . warn ( s" Unsupported array optimization hint: $f " , stmt . position )
2020-11-18 09:08:58 +00:00
Nil
}
filteredFlags
}
2018-06-08 22:18:21 +00:00
def registerArray ( stmt : ArrayDeclarationStatement , options : CompilationOptions ) : Unit = {
2021-08-06 19:01:03 +00:00
new OverflowDetector ( this , options ) . detectOverflow ( stmt )
2018-12-28 10:26:55 +00:00
if ( options . flag ( CompilationFlag . LUnixRelocatableCode ) && stmt . alignment . exists ( _ . isMultiplePages ) ) {
log . error ( "Invalid alignment for LUnix code" , stmt . position )
}
2019-06-24 20:32:29 +00:00
if ( stmt . elements . isDefined && ! stmt . const && parent . isDefined ) {
log . error ( s" Local array ` ${ stmt . name } ` cannot be initialized if it's not const " , stmt . position )
}
val arrayName = prefix + stmt . name
2018-07-20 20:46:53 +00:00
val b = get [ VariableType ] ( "byte" )
val w = get [ VariableType ] ( "word" )
2017-12-06 23:23:30 +00:00
val p = get [ Type ] ( "pointer" )
2019-04-14 23:57:18 +00:00
val e = get [ VariableType ] ( stmt . elementType )
2019-07-10 14:51:12 +00:00
if ( e . size < 1 && e . size > 127 ) {
log . error ( s" Array elements should be of size between 1 and 127, ` ${ e . name } ` is of size ${ e . size } " , stmt . position )
2019-04-14 23:57:18 +00:00
}
2017-12-06 23:23:30 +00:00
stmt . elements match {
case None =>
2019-04-29 20:57:40 +00:00
if ( stmt . const && stmt . address . isEmpty ) {
log . error ( s" Constant array ` ${ stmt . name } ` without contents nor address " , stmt . position )
}
2020-11-18 22:08:09 +00:00
val l = stmt . length match {
case None =>
log . error ( s" Array ` ${ stmt . name } ` without size nor contents " , stmt . position )
LiteralExpression ( 1 , 1 )
case Some ( l ) => l
}
// array arr[...]
val address = stmt . address . map ( a => eval ( a ) . getOrElse ( log . fatal ( s" Array ` ${ stmt . name } ` has non-constant address " , stmt . position ) ) )
val ( indexType , lengthConst ) = l match {
case VariableExpression ( name ) =>
maybeGet [ Type ] ( name ) match {
case Some ( typ @EnumType ( _ , Some ( count ) ) ) =>
typ -> NumericConstant ( count , Constant . minimumSize ( count ) )
case Some ( typ ) =>
log . error ( s" Type $name cannot be used as an array index " , l . position )
w -> Constant . Zero
2018-07-20 20:46:53 +00:00
case _ =>
2020-07-30 23:53:58 +00:00
val constant = eval ( l ) . getOrElse ( errorConstant ( s" Array ` ${ stmt . name } ` has non-constant length " , Some ( l ) , stmt . position ) )
2020-07-19 21:34:14 +00:00
w -> constant
2018-07-20 20:46:53 +00:00
}
2020-11-18 22:08:09 +00:00
case _ =>
val constant = eval ( l ) . getOrElse ( errorConstant ( s" Array ` ${ stmt . name } ` has non-constant length " , Some ( l ) , stmt . position ) )
w -> constant
}
lengthConst match {
2017-12-06 23:23:30 +00:00
case NumericConstant ( length , _ ) =>
2018-07-30 16:15:44 +00:00
if ( length > 0xffff || length < 0 ) log . error ( s" Array ` ${ stmt . name } ` has invalid length " , stmt . position )
2018-08-07 15:31:41 +00:00
val alignment = stmt . alignment . getOrElse ( defaultArrayAlignment ( options , length ) )
2017-12-06 23:23:30 +00:00
val array = address match {
2019-06-24 20:32:29 +00:00
case None => UninitializedArray ( arrayName + ".array" , length . toInt ,
2020-11-18 09:08:58 +00:00
declaredBank = stmt . bank , indexType , e , stmt . const , prepareArrayOptimizationHints ( options , stmt ) , alignment )
2019-06-24 20:32:29 +00:00
case Some ( aa ) => RelativeArray ( arrayName + ".array" , aa , length . toInt ,
2019-04-29 20:57:40 +00:00
declaredBank = stmt . bank , indexType , e , stmt . const )
2017-12-06 23:23:30 +00:00
}
addThing ( array , stmt . position )
2020-11-18 09:08:58 +00:00
registerAddressConstant ( UninitializedMemoryVariable ( arrayName , p , VariableAllocationMethod . None , stmt . bank , Set . empty , alignment , isVolatile = false ) , stmt . position , options , Some ( e ) )
2017-12-06 23:23:30 +00:00
val a = address match {
case None => array . toAddress
case Some ( aa ) => aa
}
2021-02-22 22:23:00 +00:00
registerArrayAddresses ( arrayName , stmt . bank , a , indexType , e , length . toInt , alignment , stmt . position )
2018-07-30 16:15:44 +00:00
case _ => log . error ( s" Array ` ${ stmt . name } ` has weird length " , stmt . position )
2017-12-06 23:23:30 +00:00
}
2020-11-18 22:08:09 +00:00
2018-03-25 20:57:15 +00:00
case Some ( contents1 ) =>
val contents = extractArrayContents ( contents1 )
2018-07-20 20:46:53 +00:00
val indexType = stmt . length match {
case None => // array arr = [...]
w
case Some ( l ) => // array arr[...] = [...]
val ( indexTyp , lengthConst ) = l match {
case VariableExpression ( name ) =>
maybeGet [ Type ] ( name ) match {
case Some ( typ @EnumType ( _ , Some ( count ) ) ) =>
if ( count != contents . size )
2018-07-30 16:15:44 +00:00
log . error ( s" Array ` ${ stmt . name } ` has actual length different than the number of variants in the enum ` ${ typ . name } ` " , stmt . position )
2018-07-20 20:46:53 +00:00
typ -> NumericConstant ( count , 1 )
case Some ( typ @EnumType ( _ , None ) ) =>
// using a non-enumerable enum for an array index is ok if the array is preïnitialized
typ -> NumericConstant ( contents . length , 1 )
case Some ( _ ) =>
2018-07-30 16:15:44 +00:00
log . error ( s" Type $name cannot be used as an array index " , l . position )
2018-07-20 20:46:53 +00:00
w -> Constant . Zero
case _ =>
2020-07-30 23:53:58 +00:00
w -> eval ( l ) . getOrElse ( errorConstant ( s" Array ` ${ stmt . name } ` has non-constant length " , Some ( l ) , stmt . position ) )
2018-07-20 20:46:53 +00:00
}
case _ =>
2020-07-30 23:53:58 +00:00
w -> eval ( l ) . getOrElse ( errorConstant ( s" Array ` ${ stmt . name } ` has non-constant length " , Some ( l ) , stmt . position ) )
2018-07-20 20:46:53 +00:00
}
2017-12-06 23:23:30 +00:00
lengthConst match {
case NumericConstant ( ll , _ ) =>
2018-07-30 16:15:44 +00:00
if ( ll != contents . length ) log . error ( s" Array ` ${ stmt . name } ` has different declared and actual length " , stmt . position )
case _ => log . error ( s" Array ` ${ stmt . name } ` has weird length " , stmt . position )
2017-12-06 23:23:30 +00:00
}
2018-07-20 20:46:53 +00:00
indexTyp
2017-12-06 23:23:30 +00:00
}
val length = contents . length
2018-07-30 16:15:44 +00:00
if ( length > 0xffff || length < 0 ) log . error ( s" Array ` ${ stmt . name } ` has invalid length " , stmt . position )
2020-07-17 23:14:43 +00:00
val alignment = stmt . alignment . getOrElse ( defaultArrayAlignment ( options , length ) ) & e . alignment
2020-07-30 23:53:58 +00:00
val address = stmt . address . map ( a => eval ( a ) . getOrElse ( errorConstant ( s" Array ` ${ stmt . name } ` has non-constant address " , Some ( a ) , stmt . position ) ) )
2019-04-14 23:57:18 +00:00
for ( element <- contents ) {
2019-08-15 23:09:03 +00:00
AbstractExpressionCompiler . checkAssignmentTypeLoosely ( this , element , e )
2019-04-14 23:57:18 +00:00
}
2020-11-18 09:08:58 +00:00
val array = InitializedArray ( arrayName + ".array" , address , contents , declaredBank = stmt . bank , indexType , e , readOnly = stmt . const , prepareArrayOptimizationHints ( options , stmt ) , alignment )
2019-06-28 14:28:49 +00:00
if ( ! stmt . const && options . platform . ramInitialValuesBank . isDefined && array . bank ( options ) != "default" ) {
log . error ( s" Preinitialized writable array ` ${ stmt . name } ` has to be in the default segment. " , stmt . position )
}
2017-12-06 23:23:30 +00:00
addThing ( array , stmt . position )
val a = address match {
case None => array . toAddress
case Some ( aa ) => aa
}
2021-02-22 22:23:00 +00:00
registerArrayAddresses ( arrayName , stmt . bank , a , indexType , e , length , alignment , stmt . position )
}
}
def registerArrayAddresses (
arrayName : String ,
declaredBank : Option [ String ] ,
address : Constant ,
indexType : Type ,
elementType : Type ,
length : Int ,
alignment : MemoryAlignment ,
position : Option [ Position ] ) : Unit = {
val p = get [ Type ] ( "pointer" )
val b = get [ Type ] ( "byte" )
val w = get [ Type ] ( "word" )
registerAddressConstant ( UninitializedMemoryVariable ( arrayName , p , VariableAllocationMethod . None ,
declaredBank = declaredBank , Set . empty , alignment , isVolatile = false ) , position , options , Some ( elementType ) )
addThing ( RelativeVariable ( arrayName + ".first" , address , elementType , zeropage = false ,
declaredBank = declaredBank , isVolatile = false ) , position )
if ( options . flag ( CompilationFlag . LUnixRelocatableCode ) ) {
val b = get [ Type ] ( "byte" )
val w = get [ Type ] ( "word" )
val relocatable = UninitializedMemoryVariable ( arrayName , w , VariableAllocationMethod . Static , None , Set . empty , NoAlignment , isVolatile = false )
val addr = relocatable . toAddress
addThing ( relocatable , position )
addThing ( RelativeVariable ( arrayName + ".array.hi" , addr + 1 , b , zeropage = false , None , isVolatile = false ) , position )
addThing ( RelativeVariable ( arrayName + ".array.lo" , addr , b , zeropage = false , None , isVolatile = false ) , position )
} else {
addThing ( ConstantThing ( arrayName , address , p ) , position )
addThing ( ConstantThing ( arrayName + ".hi" , address . hiByte . quickSimplify , b ) , position )
addThing ( ConstantThing ( arrayName + ".lo" , address . loByte . quickSimplify , b ) , position )
addThing ( ConstantThing ( arrayName + ".array.hi" , address . hiByte . quickSimplify , b ) , position )
addThing ( ConstantThing ( arrayName + ".array.lo" , address . loByte . quickSimplify , b ) , position )
}
if ( length < 256 ) {
addThing ( ConstantThing ( arrayName + ".length" , NumericConstant ( length , 1 ) , b ) , position )
} else {
addThing ( ConstantThing ( arrayName + ".length" , NumericConstant ( length , 2 ) , w ) , position )
}
if ( length > 0 && indexType . isArithmetic ) {
if ( length <= 256 ) {
addThing ( ConstantThing ( arrayName + ".lastindex" , NumericConstant ( length - 1 , 1 ) , b ) , position )
} else {
addThing ( ConstantThing ( arrayName + ".lastindex" , NumericConstant ( length - 1 , 2 ) , w ) , position )
}
2017-12-06 23:23:30 +00:00
}
}
2018-12-21 21:36:05 +00:00
def registerVariable ( stmt : VariableDeclarationStatement , options : CompilationOptions , isPointy : Boolean ) : Unit = {
2021-08-06 19:01:03 +00:00
new OverflowDetector ( this , options ) . detectOverflow ( stmt )
2017-12-06 23:23:30 +00:00
val name = stmt . name
val position = stmt . position
2019-06-29 14:22:27 +00:00
if ( name == "" || name . contains ( "." ) && ! name . contains ( ".return" ) ) {
log . warn ( s" Invalid variable name: $name . Please report a bug. " , position )
}
2017-12-06 23:23:30 +00:00
if ( stmt . stack && parent . isEmpty ) {
2018-07-30 16:15:44 +00:00
if ( stmt . stack && stmt . global ) log . error ( s" ` $name ` is static or global and cannot be on stack " , position )
2017-12-06 23:23:30 +00:00
}
val b = get [ Type ] ( "byte" )
2018-05-14 00:16:46 +00:00
val w = get [ Type ] ( "word" )
2018-07-20 20:46:53 +00:00
val typ = get [ VariableType ] ( stmt . typ )
2020-07-17 23:14:43 +00:00
val alignment = stmt . alignment . getOrElse ( defaultVariableAlignment ( options , typ . size ) ) & typ . alignment
2017-12-06 23:23:30 +00:00
if ( stmt . constant ) {
2020-07-30 23:53:58 +00:00
val invalidUsesBefore = constantsThatShouldHaveBeenImportedEarlier . get ( stmt . name )
if ( invalidUsesBefore . nonEmpty ) {
log . info ( s" The constant ${ stmt . name } has been used before it was defined in a way that requires a definition beforehand " , stmt . position )
invalidUsesBefore . foreach { use =>
if ( use . isDefined ) {
log . info ( s" here: " , use )
}
}
if ( invalidUsesBefore ( None ) ) {
log . info ( "and in some other place or places." )
}
}
2018-07-30 16:15:44 +00:00
if ( stmt . stack ) log . error ( s" ` $name ` is a constant and cannot be on stack " , position )
if ( stmt . register ) log . error ( s" ` $name ` is a constant and cannot be in a register " , position )
if ( stmt . address . isDefined ) log . error ( s" ` $name ` is a constant and cannot have an address " , position )
if ( stmt . initialValue . isEmpty ) log . error ( s" ` $name ` is a constant and requires a value " , position )
2020-07-30 23:53:58 +00:00
val rawConstantValue = stmt . initialValue . flatMap ( eval ) . getOrElse ( errorConstant ( s" ` $name ` has a non-constant value " , stmt . initialValue , position ) ) . quickSimplify
2020-03-15 00:01:08 +00:00
rawConstantValue match {
2020-03-26 00:36:41 +00:00
case NumericConstant ( nv , _ ) if nv >= 2 && typ . size < 8 =>
if ( nv >= 1L . << ( 8 * typ . size ) ) {
2020-03-15 00:01:08 +00:00
log . error ( s" Constant value $nv too big for type ${ typ . name } " , stmt . position )
}
case _ => // ignore
}
val constantValue = rawConstantValue . fitInto ( typ )
2018-07-30 16:15:44 +00:00
if ( constantValue . requiredSize > typ . size ) log . error ( s" ` $name ` is has an invalid value: not in the range of ` $typ ` " , position )
2017-12-06 23:23:30 +00:00
addThing ( ConstantThing ( prefix + name , constantValue , typ ) , stmt . position )
2021-06-21 12:20:24 +00:00
for ( Subvariable ( suffix , offset , vol , t , arraySize ) <- getSubvariables ( typ ) ) {
2021-02-22 22:23:00 +00:00
if ( arraySize . isDefined ) {
log . error ( s" Constants of type ${ t . name } that contains array fields are not supported " , stmt . position )
} else {
addThing ( ConstantThing ( prefix + name + suffix , constantValue . subconstant ( options , offset , t . size ) , t ) , stmt . position )
}
2017-12-06 23:23:30 +00:00
}
} else {
2018-07-30 16:15:44 +00:00
if ( stmt . stack && stmt . global ) log . error ( s" ` $name ` is static or global and cannot be on stack " , position )
if ( stmt . register && typ . size != 1 ) log . error ( s" A register variable ` $name ` is too large " , position )
if ( stmt . register && stmt . global ) log . error ( s" ` $name ` is static or global and cannot be in a register " , position )
if ( stmt . register && stmt . stack ) log . error ( s" ` $name ` cannot be simultaneously on stack and in a register " , position )
2018-12-16 20:07:04 +00:00
if ( stmt . volatile && stmt . stack ) log . error ( s" ` $name ` cannot be simultaneously on stack and volatile " , position )
if ( stmt . volatile && stmt . register ) log . error ( s" ` $name ` cannot be simultaneously volatile and in a register " , position )
2018-07-30 16:15:44 +00:00
if ( stmt . initialValue . isDefined && parent . isDefined ) log . error ( s" ` $name ` is local and not a constant and therefore cannot have a value " , position )
2019-06-28 14:28:49 +00:00
if ( stmt . initialValue . isDefined && stmt . address . isDefined ) {
if ( options . platform . ramInitialValuesBank . isDefined ) {
log . error ( s" ` $name ` has both address and initial value, which is unsupported on this target " , position )
2020-03-17 20:08:43 +00:00
} else if ( options . flag ( CompilationFlag . BuggyCodeWarning ) ) {
2019-06-28 14:28:49 +00:00
log . warn ( s" ` $name ` has both address and initial value - this may not work as expected! " , position )
}
}
2018-07-30 16:15:44 +00:00
if ( stmt . register && stmt . address . isDefined ) log . error ( s" ` $name ` cannot by simultaneously at an address and in a register " , position )
2017-12-06 23:23:30 +00:00
if ( stmt . stack ) {
val v = StackVariable ( prefix + name , typ , this . baseStackOffset )
2019-04-16 14:34:17 +00:00
addVariable ( options , name , v , stmt . position )
2019-06-23 20:53:42 +00:00
addThing ( StackOffsetThing ( v . name + ".addr" , this . baseStackOffset , get [ Type ] ( "pointer" ) , None ) , stmt . position )
addThing ( StackOffsetThing ( v . name + ".addr.lo" , this . baseStackOffset , b , Some ( 0 ) ) , stmt . position )
addThing ( StackOffsetThing ( v . name + ".addr.hi" , this . baseStackOffset , b , Some ( 1 ) ) , stmt . position )
addThing ( StackOffsetThing ( v . name + ".pointer" , this . baseStackOffset , PointerType ( "pointer." + v . typ . name , v . typ . name , Some ( v . typ ) ) , None ) , stmt . position )
addThing ( StackOffsetThing ( v . name + ".pointer.lo" , this . baseStackOffset , b , Some ( 0 ) ) , stmt . position )
addThing ( StackOffsetThing ( v . name + ".pointer.hi" , this . baseStackOffset , b , Some ( 1 ) ) , stmt . position )
2018-12-14 21:01:52 +00:00
baseStackOffset += typ . size
2017-12-06 23:23:30 +00:00
} else {
val ( v , addr ) = stmt . address . fold [ ( VariableInMemory , Constant ) ] ( {
2018-02-01 21:39:38 +00:00
val alloc =
2019-04-14 22:27:34 +00:00
if ( isPointy && stmt . bank . isEmpty ) VariableAllocationMethod . Zeropage
else if ( typ . name == "__reg$type" ) VariableAllocationMethod . Zeropage
2018-02-01 21:39:38 +00:00
else if ( stmt . global ) VariableAllocationMethod . Static
else if ( stmt . register ) VariableAllocationMethod . Register
else VariableAllocationMethod . Auto
2020-03-17 20:08:43 +00:00
if ( stmt . volatile && ! stmt . global && options . flag ( CompilationFlag . FallbackValueUseWarning ) ) {
2020-01-03 20:28:10 +00:00
log . warn ( s" Volatile variable ` $name ` assumed to be static " , position )
}
if ( stmt . volatile && stmt . stack ) {
log . error ( s" Volatile variable ` $name ` cannot be allocated on stack " , position )
}
if ( stmt . volatile && stmt . register ) {
log . error ( s" Volatile variable ` $name ` cannot be allocated in a register " , position )
}
2017-12-19 17:58:33 +00:00
if ( alloc != VariableAllocationMethod . Static && stmt . initialValue . isDefined ) {
2018-07-30 16:15:44 +00:00
log . error ( s" ` $name ` cannot be preinitialized` " , position )
2017-12-19 17:58:33 +00:00
}
2020-11-18 09:08:58 +00:00
val optimizationHints = prepareVariableOptimizationHints ( options , stmt )
2018-03-15 22:09:19 +00:00
val v = stmt . initialValue . fold [ MemoryVariable ] ( UninitializedMemoryVariable ( prefix + name , typ , alloc ,
2020-11-18 09:08:58 +00:00
declaredBank = stmt . bank , optimizationHints , alignment , isVolatile = stmt . volatile ) ) { ive =>
InitializedMemoryVariable ( name , None , typ , ive , declaredBank = stmt . bank , optimizationHints , alignment , isVolatile = stmt . volatile )
2017-12-19 17:58:33 +00:00
}
2019-04-15 17:45:26 +00:00
registerAddressConstant ( v , stmt . position , options , Some ( typ ) )
2017-12-06 23:23:30 +00:00
( v , v . toAddress )
} ) ( a => {
2020-07-30 23:53:58 +00:00
val addr = eval ( a ) . getOrElse ( errorConstant ( s" Address of ` $name ` has a non-constant value " , Some ( a ) , position ) )
2017-12-06 23:23:30 +00:00
val zp = addr match {
case NumericConstant ( n , _ ) => n < 0x100
case _ => false
}
2018-03-15 22:09:19 +00:00
val v = RelativeVariable ( prefix + name , addr , typ , zeropage = zp ,
2018-12-16 20:07:04 +00:00
declaredBank = stmt . bank , isVolatile = stmt . volatile )
2019-04-15 17:45:26 +00:00
registerAddressConstant ( v , stmt . position , options , Some ( typ ) )
2017-12-23 23:09:22 +00:00
( v , addr )
2017-12-06 23:23:30 +00:00
} )
2019-04-16 14:34:17 +00:00
addVariable ( options , name , v , stmt . position )
}
}
}
def addVariable ( options : CompilationOptions , localName : String , variable : Variable , position : Option [ Position ] ) : Unit = {
2021-02-22 22:23:00 +00:00
val b = get [ VariableType ] ( "byte" )
2019-04-16 14:34:17 +00:00
variable match {
case v : StackVariable =>
2019-05-02 11:24:21 +00:00
addThing ( localName , v , position )
2021-06-21 12:20:24 +00:00
for ( Subvariable ( suffix , offset , vol , t , arraySize ) <- getSubvariables ( v . typ ) ) {
2019-12-30 10:50:18 +00:00
if ( arraySize . isDefined ) {
2021-02-22 22:23:00 +00:00
log . error ( s" Cannot create a stack variable $localName of compound type ${ v . typ . name } that contains an array member " , position )
2019-12-30 10:50:18 +00:00
} else {
addThing ( StackVariable ( prefix + localName + suffix , t , baseStackOffset + offset ) , position )
}
2017-12-06 23:23:30 +00:00
}
2019-04-16 14:34:17 +00:00
case v : MemoryVariable =>
2019-05-02 11:24:21 +00:00
addThing ( localName , v , position )
2021-06-21 12:20:24 +00:00
for ( Subvariable ( suffix , offset , vol , t , arrayIndexTypeAndSize ) <- getSubvariables ( v . typ ) ) {
2021-02-22 22:23:00 +00:00
arrayIndexTypeAndSize match {
2019-12-30 10:50:18 +00:00
case None =>
2021-06-21 12:20:24 +00:00
val subv = RelativeVariable ( prefix + localName + suffix , v . toAddress + offset , t , zeropage = v . zeropage , declaredBank = v . declaredBank , isVolatile = v . isVolatile || vol )
2019-12-30 10:50:18 +00:00
addThing ( subv , position )
registerAddressConstant ( subv , position , options , Some ( t ) )
2021-02-22 22:23:00 +00:00
case Some ( ( indexType , elemCount ) ) =>
val suba = RelativeArray ( prefix + localName + suffix + ".array" , v . toAddress + offset , elemCount , v . declaredBank , indexType , t , false )
addThing ( suba , position )
registerArrayAddresses ( prefix + localName + suffix , v . declaredBank , v . toAddress + offset , indexType , t , elemCount , NoAlignment , position )
2019-12-30 10:50:18 +00:00
}
2017-12-06 23:23:30 +00:00
}
2019-04-16 14:34:17 +00:00
case v : VariableInMemory =>
2019-05-02 11:24:21 +00:00
addThing ( localName , v , position )
2019-04-16 14:34:17 +00:00
addThing ( ConstantThing ( v . name + "`" , v . toAddress , get [ Type ] ( "word" ) ) , position )
2021-06-21 12:20:24 +00:00
for ( Subvariable ( suffix , offset , vol , t , arrayIndexTypeAndSize ) <- getSubvariables ( v . typ ) ) {
2021-02-22 22:23:00 +00:00
arrayIndexTypeAndSize match {
2019-12-30 10:50:18 +00:00
case None =>
2021-06-21 12:20:24 +00:00
val subv = RelativeVariable ( prefix + localName + suffix , v . toAddress + offset , t , zeropage = v . zeropage , declaredBank = v . declaredBank , isVolatile = v . isVolatile || vol )
2019-12-30 10:50:18 +00:00
addThing ( subv , position )
registerAddressConstant ( subv , position , options , Some ( t ) )
2021-02-22 22:23:00 +00:00
case Some ( ( indexType , elemCount ) ) =>
val suba = RelativeArray ( prefix + localName + suffix + ".array" , v . toAddress + offset , elemCount , v . declaredBank , indexType , t , false )
addThing ( suba , position )
registerArrayAddresses ( prefix + localName + suffix , v . declaredBank , v . toAddress + offset , indexType , t , elemCount , NoAlignment , position )
2019-12-30 10:50:18 +00:00
}
2019-04-16 14:34:17 +00:00
}
case _ => ???
2017-12-06 23:23:30 +00:00
}
}
2021-06-21 12:20:24 +00:00
//noinspection NameBooleanParameters
2019-12-30 10:50:18 +00:00
def getSubvariables ( typ : Type ) : List [ Subvariable ] = {
2018-07-30 12:33:16 +00:00
val b = get [ VariableType ] ( "byte" )
val w = get [ VariableType ] ( "word" )
2018-08-03 09:11:03 +00:00
if ( typ . name == "__reg$type" ) {
2019-07-29 20:51:08 +00:00
if ( options . isBigEndian ) {
throw new IllegalArgumentException ( "__reg$type on 6809???" )
}
2021-06-21 12:20:24 +00:00
return Subvariable ( ".lo" , 0 , false , b ) : :
Subvariable ( ".hi" , 1 , false , b ) : :
Subvariable ( ".loword" , 0 , false , w ) : :
Subvariable ( ".loword.lo" , 0 , false , b ) : :
Subvariable ( ".loword.hi" , 1 , false , b ) : :
Subvariable ( ".b2b3" , 2 , false , w ) : :
Subvariable ( ".b2b3.lo" , 2 , false , b ) : :
Subvariable ( ".b2b3.hi" , 3 , false , b ) : :
List . tabulate ( typ . size ) { i => Subvariable ( ".b" + i , i , false , b ) }
2018-08-03 09:11:03 +00:00
}
2018-07-30 12:33:16 +00:00
typ match {
case _ : PlainType => typ . size match {
2019-07-29 20:51:08 +00:00
case 2 => if ( options . isBigEndian ) List (
2021-06-21 12:20:24 +00:00
Subvariable ( ".lo" , 1 , false , b ) ,
Subvariable ( ".hi" , 0 , false , b )
2019-07-29 20:51:08 +00:00
) else List (
2021-06-21 12:20:24 +00:00
Subvariable ( ".lo" , 0 , false , b ) ,
Subvariable ( ".hi" , 1 , false , b ) )
2019-07-29 20:51:08 +00:00
case 3 => if ( options . isBigEndian ) List (
2021-06-21 12:20:24 +00:00
Subvariable ( ".loword" , 1 , false , w ) ,
Subvariable ( ".loword.lo" , 2 , false , b ) ,
Subvariable ( ".loword.hi" , 1 , false , b ) ,
Subvariable ( ".hiword" , 0 , false , w ) ,
Subvariable ( ".hiword.lo" , 1 , false , b ) ,
Subvariable ( ".hiword.hi" , 0 , false , b ) ,
Subvariable ( ".lo" , 2 , false , b ) ,
Subvariable ( ".b0" , 2 , false , b ) ,
Subvariable ( ".b1" , 1 , false , b ) ,
Subvariable ( ".b2" , 0 , false , b )
2019-07-29 20:51:08 +00:00
) else List (
2021-06-21 12:20:24 +00:00
Subvariable ( ".loword" , 0 , false , w ) ,
Subvariable ( ".loword.lo" , 0 , false , b ) ,
Subvariable ( ".loword.hi" , 1 , false , b ) ,
Subvariable ( ".hiword" , 1 , false , w ) ,
Subvariable ( ".hiword.lo" , 1 , false , b ) ,
Subvariable ( ".hiword.hi" , 2 , false , b ) ,
Subvariable ( ".lo" , 0 , false , b ) ,
Subvariable ( ".b0" , 0 , false , b ) ,
Subvariable ( ".b1" , 1 , false , b ) ,
Subvariable ( ".b2" , 2 , false , b ) )
2019-07-29 20:51:08 +00:00
case 4 => if ( options . isBigEndian ) List (
2021-06-21 12:20:24 +00:00
Subvariable ( ".loword" , 2 , false , w ) ,
Subvariable ( ".hiword" , 0 , false , w ) ,
Subvariable ( ".loword.lo" , 3 , false , b ) ,
Subvariable ( ".loword.hi" , 2 , false , b ) ,
Subvariable ( ".hiword.lo" , 1 , false , b ) ,
Subvariable ( ".hiword.hi" , 0 , false , b ) ,
Subvariable ( ".lo" , 3 , false , b ) ,
Subvariable ( ".b0" , 3 , false , b ) ,
Subvariable ( ".b1" , 2 , false , b ) ,
Subvariable ( ".b2" , 1 , false , b ) ,
Subvariable ( ".b3" , 0 , false , b )
2019-07-29 20:51:08 +00:00
) else List (
2021-06-21 12:20:24 +00:00
Subvariable ( ".loword" , 0 , false , w ) ,
Subvariable ( ".hiword" , 2 , false , w ) ,
Subvariable ( ".loword.lo" , 0 , false , b ) ,
Subvariable ( ".loword.hi" , 1 , false , b ) ,
Subvariable ( ".hiword.lo" , 2 , false , b ) ,
Subvariable ( ".hiword.hi" , 3 , false , b ) ,
Subvariable ( ".lo" , 0 , false , b ) ,
Subvariable ( ".b0" , 0 , false , b ) ,
Subvariable ( ".b1" , 1 , false , b ) ,
Subvariable ( ".b2" , 2 , false , b ) ,
Subvariable ( ".b3" , 3 , false , b )
2019-07-29 20:51:08 +00:00
)
2019-04-14 23:30:47 +00:00
case sz if sz > 4 =>
2019-07-29 20:51:08 +00:00
if ( options . isBigEndian ) {
2021-06-21 12:20:24 +00:00
Subvariable ( ".lo" , sz - 1 , false , b ) : :
Subvariable ( ".loword" , sz - 2 , false , w ) : :
Subvariable ( ".loword.lo" , sz - 1 , false , b ) : :
Subvariable ( ".loword.hi" , sz - 2 , false , b ) : :
List . tabulate ( sz ) { i => Subvariable ( ".b" + i , sz - 1 - i , false , b ) }
2019-07-29 20:51:08 +00:00
} else {
2021-06-21 12:20:24 +00:00
Subvariable ( ".lo" , 0 , false , b ) : :
Subvariable ( ".loword" , 0 , false , w ) : :
Subvariable ( ".loword.lo" , 0 , false , b ) : :
Subvariable ( ".loword.hi" , 1 , false , b ) : :
List . tabulate ( sz ) { i => Subvariable ( ".b" + i , i , false , b ) }
2019-07-29 20:51:08 +00:00
}
2018-07-30 12:33:16 +00:00
case _ => Nil
}
2020-11-10 23:28:21 +00:00
case InterruptPointerType | _ : FunctionPointerType | _ : PointerType => if ( options . isBigEndian ) List (
2021-06-21 12:20:24 +00:00
Subvariable ( ".raw" , 0 , false , get [ VariableType ] ( "pointer" ) ) ,
Subvariable ( ".raw.lo" , 1 , false , b ) ,
Subvariable ( ".raw.hi" , 0 , false , b ) ,
Subvariable ( ".lo" , 1 , false , b ) ,
Subvariable ( ".hi" , 0 , false , b )
2019-07-29 20:51:08 +00:00
) else List (
2021-06-21 12:20:24 +00:00
Subvariable ( ".raw" , 0 , false , get [ VariableType ] ( "pointer" ) ) ,
Subvariable ( ".raw.lo" , 0 , false , b ) ,
Subvariable ( ".raw.hi" , 1 , false , b ) ,
Subvariable ( ".lo" , 0 , false , b ) ,
Subvariable ( ".hi" , 1 , false , b ) )
2019-04-14 23:30:47 +00:00
case s : StructType =>
2019-12-30 10:50:18 +00:00
val builder = new ListBuffer [ Subvariable ]
2019-04-14 23:30:47 +00:00
var offset = 0
2021-06-21 12:20:24 +00:00
for ( ResolvedFieldDesc ( typ , fieldName , vol , indexTypeAndCount ) <- s . mutableFieldsWithTypes ) {
2020-09-01 22:44:24 +00:00
offset = getTypeAlignment ( typ , Set ( ) ) . roundSizeUp ( offset )
2021-02-22 22:23:00 +00:00
val suffix = "." + fieldName
2021-06-21 12:20:24 +00:00
builder += Subvariable ( suffix , offset , vol , typ , indexTypeAndCount )
2021-02-22 22:23:00 +00:00
if ( indexTypeAndCount . isEmpty ) {
builder ++= getSubvariables ( typ ) . map {
2021-06-21 12:20:24 +00:00
case Subvariable ( innerSuffix , innerOffset , innerVolatile , innerType , innerSize ) => Subvariable ( suffix + innerSuffix , offset + innerOffset , vol || innerVolatile , innerType , innerSize )
2021-02-22 22:23:00 +00:00
}
2019-04-14 23:30:47 +00:00
}
2021-02-22 22:23:00 +00:00
offset += typ . size * indexTypeAndCount . fold ( 1 ) ( _ . _2 )
2020-09-01 22:44:24 +00:00
offset = getTypeAlignment ( typ , Set ( ) ) . roundSizeUp ( offset )
2019-04-14 23:30:47 +00:00
}
builder . toList
2019-04-15 17:45:26 +00:00
case s : UnionType =>
2019-12-30 10:50:18 +00:00
val builder = new ListBuffer [ Subvariable ]
2021-09-17 20:19:39 +00:00
for ( ResolvedFieldDesc ( typ , fieldName , vol1 , indexTypeAndCount ) <- s . mutableFieldsWithTypes ) {
2019-04-15 17:45:26 +00:00
val suffix = "." + fieldName
2021-06-21 12:20:24 +00:00
builder += Subvariable ( suffix , 0 , vol1 , typ )
2021-09-17 20:19:39 +00:00
if ( indexTypeAndCount . isEmpty ) {
2021-02-22 22:23:00 +00:00
builder ++= getSubvariables ( typ ) . map {
2021-06-21 12:20:24 +00:00
case Subvariable ( innerSuffix , innerOffset , vol2 , innerType , innerSize ) => Subvariable ( suffix + innerSuffix , innerOffset , vol1 || vol2 , innerType , innerSize )
2021-02-22 22:23:00 +00:00
}
2019-04-15 17:45:26 +00:00
}
}
builder . toList
2018-07-30 12:33:16 +00:00
case _ => Nil
}
}
2017-12-06 23:23:30 +00:00
def lookup [ T <: Thing : Manifest ] ( name : String ) : Option [ T ] = {
if ( things . contains ( name ) ) {
maybeGet ( name )
} else {
parent . flatMap ( _ . lookup [ T ] ( name ) )
}
}
def lookupFunction ( name : String , actualParams : List [ ( Type , Expression ) ] ) : Option [ MangledFunction ] = {
if ( things . contains ( name ) ) {
2020-03-19 18:43:24 +00:00
val thing = get [ Thing ] ( name )
if ( ! thing . isInstanceOf [ MangledFunction ] ) {
return None
}
val function = thing . asInstanceOf [ MangledFunction ]
2020-11-18 08:34:02 +00:00
if ( function . name == "call" ) {
if ( actualParams . isEmpty || actualParams . length > 2 ) {
log . error ( "Invalid number of parameters for function `call`" , actualParams . headOption . flatMap ( _ . _2 . position ) )
}
} else {
if ( function . params . length != actualParams . length && function . name != "call" ) {
2020-11-18 09:08:58 +00:00
log . error ( s" Invalid number of parameters for function ` $name ` " , actualParams . headOption . flatMap ( _ . _2 . position ) )
}
2020-11-18 08:34:02 +00:00
}
2019-07-26 22:58:10 +00:00
if ( name == "call" ) return Some ( function )
2017-12-06 23:23:30 +00:00
function . params match {
case NormalParamSignature ( params ) =>
function . params . types . zip ( actualParams ) . zip ( params ) . foreach { case ( ( required , ( actual , expr ) ) , m ) =>
2020-03-30 17:23:48 +00:00
if ( ! actual . isAssignableTo ( required ) ) {
log . error ( s" Invalid value for parameter ` ${ m . name } ` of function ` $name ` " , expr . position )
2017-12-06 23:23:30 +00:00
}
}
2020-03-30 17:23:48 +00:00
case AssemblyOrMacroParamSignature ( params ) =>
params . zip ( actualParams ) . zipWithIndex . foreach { case ( ( AssemblyOrMacroParam ( requiredType , variable , behaviour ) , ( actual , expr ) ) , ix ) =>
function match {
case m : MacroFunction =>
behaviour match {
case AssemblyParameterPassingBehaviour . ByReference =>
if ( ! m . isInAssembly ) {
if ( requiredType != VoidType && actual != requiredType ) {
log . error ( s" Invalid argument type for parameter ` ${ variable . name } ` of macro function ` $name `: required: ${ requiredType . name } , actual: ${ actual . name } " , expr . position )
}
}
case AssemblyParameterPassingBehaviour . Copy if m . isInAssembly =>
if ( ! actual . isAssignableTo ( requiredType ) ) {
log . error ( s" Invalid value for parameter # ${ ix + 1 } of macro function ` $name ` " , expr . position )
}
case _ =>
if ( ! actual . isAssignableTo ( requiredType ) ) {
log . error ( s" Invalid value for parameter # ${ ix + 1 } ` ${ variable . name } ` of macro function ` $name ` " , expr . position )
}
}
case _ =>
if ( ! actual . isAssignableTo ( requiredType ) ) {
log . error ( s" Invalid value for parameter # ${ ix + 1 } ` ${ variable . name } ` of function ` $name ` " , expr . position )
}
2017-12-06 23:23:30 +00:00
}
}
}
Some ( function )
} else {
parent . flatMap ( _ . lookupFunction ( name , actualParams ) )
}
}
2019-03-18 14:14:13 +00:00
private def expandAliases ( ) : Unit = {
val aliasesToAdd = mutable . ListBuffer [ Alias ] ( )
things . values . foreach {
2021-11-12 01:10:07 +00:00
case a : Alias => aliasesToAdd ++= expandAliasImpl ( a )
2019-03-18 14:14:13 +00:00
case _ => ( )
}
aliasesToAdd . foreach ( a => things += a . name -> a )
}
2021-11-12 01:10:07 +00:00
private def expandAliasImpl ( a : Alias ) : Seq [ Alias ] = {
val aliasesToAdd = mutable . ListBuffer [ Alias ] ( )
val prefix = a . target + "."
root . things . foreach {
case ( thingName , thing ) =>
if ( thingName . startsWith ( prefix ) ) {
aliasesToAdd += Alias ( a . name + "." + thingName . stripPrefix ( prefix ) , thingName , a . deprecated , a . local )
}
}
aliasesToAdd
}
private def expandAlias ( a : Alias ) : Unit = {
expandAliasImpl ( a ) . foreach ( a => things += a . name -> a )
}
2020-09-01 22:44:24 +00:00
def fixStructAlignments ( ) : Unit = {
val allStructTypes : Iterable [ CompoundVariableType ] = things . values . flatMap {
case s @StructType ( name , _ , _ ) => Some ( s )
case s @UnionType ( name , _ , _ ) => Some ( s )
case _ => None
}
for ( t <- allStructTypes ) {
t . baseAlignment match {
case DivisibleAlignment ( n ) if n < 1 =>
log . error ( s" Type ${ t . name } has invalid alignment ${ t . alignment } " )
case WithinPageAlignment =>
log . error ( s" Type ${ t . name } has invalid alignment ${ t . alignment } " )
case _ =>
}
}
var iterations = allStructTypes . size
while ( iterations >= 0 ) {
var ok = true
for ( t <- allStructTypes ) {
if ( getTypeAlignment ( t , Set ( ) ) eq null ) ok = false
}
if ( ok ) return
iterations -= 1
}
log . error ( "Cycles in struct definitions found" )
}
2019-04-14 23:30:47 +00:00
def fixStructSizes ( ) : Unit = {
2020-09-01 22:44:24 +00:00
val allStructTypes : Iterable [ CompoundVariableType ] = things . values . flatMap {
2020-07-17 23:14:43 +00:00
case s @StructType ( name , _ , _ ) => Some ( s )
case s @UnionType ( name , _ , _ ) => Some ( s )
2019-04-14 23:30:47 +00:00
case _ => None
}
var iterations = allStructTypes . size
while ( iterations >= 0 ) {
var ok = true
for ( t <- allStructTypes ) {
if ( getTypeSize ( t , Set ( ) ) < 0 ) ok = false
}
if ( ok ) return
iterations -= 1
}
log . error ( "Cycles in struct definitions found" )
}
2020-09-01 22:44:24 +00:00
def fixAlignedSizes ( ) : Unit = {
val allTypes : Iterable [ VariableType ] = things . values . flatMap {
case s : VariableType => Some ( s )
case _ => None
}
for ( t <- allTypes ) {
t . alignedSize = getTypeAlignment ( t , Set ( ) ) . roundSizeUp ( getTypeSize ( t , Set ( ) ) )
}
}
2021-02-22 22:23:00 +00:00
def getArrayFieldIndexTypeAndSize ( expr : Expression ) : ( VariableType , Int ) = {
val b = get [ VariableType ] ( "byte" )
expr match {
case VariableExpression ( name ) =>
maybeGet [ Type ] ( name ) match {
case Some ( typ @EnumType ( _ , Some ( count ) ) ) =>
return typ -> count
case Some ( typ ) =>
log . error ( s" Type $name cannot be used as an array index " , expr . position )
return b -> 0
case _ =>
}
case _ =>
}
val constant : Int = eval ( expr ) . map ( _ . quickSimplify ) match {
case Some ( NumericConstant ( n , _ ) ) if n >= 0 && n <= 127 =>
n . toInt
case Some ( NumericConstant ( n , _ ) ) =>
log . error ( s" Array size too large " , expr . position )
1
case Some ( _ ) =>
log . error ( s" Array size cannot be fully resolved " , expr . position )
1
case _ =>
errorConstant ( s" Array has non-constant length " , Some ( expr ) , expr . position )
1
}
if ( constant <= 256 ) {
b -> constant
} else {
get [ VariableType ] ( "word" ) -> constant
}
}
2019-06-24 22:45:49 +00:00
def fixStructFields ( ) : Unit = {
2019-12-30 10:50:18 +00:00
// TODO: handle arrays?
2019-06-24 22:45:49 +00:00
things . values . foreach {
2020-07-17 23:14:43 +00:00
case st @StructType ( _ , fields , _ ) =>
2019-06-24 22:45:49 +00:00
st . mutableFieldsWithTypes = fields . map {
2021-06-21 12:20:24 +00:00
case FieldDesc ( tn , name , vol , arraySize ) => ResolvedFieldDesc ( get [ VariableType ] ( tn ) , name , vol , arraySize . map ( getArrayFieldIndexTypeAndSize ) )
2019-06-24 22:45:49 +00:00
}
2020-07-17 23:14:43 +00:00
case ut @UnionType ( _ , fields , _ ) =>
2019-06-24 22:45:49 +00:00
ut . mutableFieldsWithTypes = fields . map {
2021-06-21 12:20:24 +00:00
case FieldDesc ( tn , name , vol , arraySize ) => ResolvedFieldDesc ( get [ VariableType ] ( tn ) , name , vol , arraySize . map ( getArrayFieldIndexTypeAndSize ) )
2019-06-24 22:45:49 +00:00
}
case _ => ( )
}
}
2017-12-06 23:23:30 +00:00
def collectDeclarations ( program : Program , options : CompilationOptions ) : Unit = {
2018-07-20 20:46:53 +00:00
val b = get [ VariableType ] ( "byte" )
2018-12-31 12:20:32 +00:00
val v = get [ Type ] ( "void" )
2023-01-27 17:14:50 +00:00
if ( options . flag ( CompilationFlag . IdentityPage ) ) {
2020-11-18 09:08:58 +00:00
addThing ( InitializedArray ( "identity$" , None , IndexedSeq . tabulate ( 256 ) ( n => LiteralExpression ( n , 1 ) ) , declaredBank = None , b , b , readOnly = true , Set . empty , defaultArrayAlignment ( options , 256 ) ) , None )
2018-07-20 20:46:53 +00:00
}
program . declarations . foreach {
case a : AliasDefinitionStatement => registerAlias ( a )
case _ =>
}
program . declarations . foreach {
case e : EnumDefinitionStatement => registerEnum ( e )
case _ =>
2018-02-28 00:13:05 +00:00
}
2019-04-14 23:30:47 +00:00
program . declarations . foreach {
case s : StructDefinitionStatement => registerStruct ( s )
2019-04-15 17:45:26 +00:00
case s : UnionDefinitionStatement => registerUnion ( s )
2019-04-14 23:30:47 +00:00
case _ =>
}
2019-06-24 22:45:49 +00:00
fixStructFields ( )
2020-09-01 22:44:24 +00:00
fixStructAlignments ( )
2019-12-30 10:50:18 +00:00
fixStructSizes ( )
2020-09-01 22:44:24 +00:00
fixAlignedSizes ( )
2018-12-21 21:36:05 +00:00
val pointies = collectPointies ( program . declarations )
pointiesUsed ( "" ) = pointies
2019-10-31 11:14:52 +00:00
program . declarations . foreach { decl =>
try {
decl match {
case f : FunctionDeclarationStatement => registerFunction ( f , options )
case v : VariableDeclarationStatement => registerVariable ( v , options , pointies ( v . name ) )
case a : ArrayDeclarationStatement => registerArray ( a , options )
case _ =>
}
} catch {
case ex : NonFatalCompilationException =>
log . error ( ex . getMessage , ex . position . orElse ( decl . position ) )
}
2017-12-06 23:23:30 +00:00
}
2019-03-18 14:14:13 +00:00
expandAliases ( )
2018-07-06 20:45:59 +00:00
if ( options . zpRegisterSize > 0 && ! things . contains ( "__reg" ) ) {
addThing ( BasicPlainType ( "__reg$type" , options . zpRegisterSize ) , None )
2018-03-05 11:05:37 +00:00
registerVariable ( VariableDeclarationStatement (
name = "__reg" ,
2018-03-15 22:09:19 +00:00
bank = None ,
2018-07-06 20:45:59 +00:00
typ = "__reg$type" ,
2018-03-05 11:05:37 +00:00
global = true ,
stack = false ,
constant = false ,
volatile = false ,
register = false ,
initialValue = None ,
2018-08-07 15:31:41 +00:00
address = None ,
2020-11-18 09:08:58 +00:00
optimizationHints = Set . empty ,
2018-12-21 21:36:05 +00:00
alignment = None ) , options , isPointy = true )
2018-03-05 11:05:37 +00:00
}
2018-06-17 00:01:35 +00:00
if ( CpuFamily . forType ( options . platform . cpu ) == CpuFamily . M6502 ) {
if ( ! things . contains ( "__constant8" ) ) {
2020-11-18 09:08:58 +00:00
things ( "__constant8" ) = InitializedArray ( "__constant8" , None , List ( LiteralExpression ( 8 , 1 ) ) , declaredBank = None , b , b , readOnly = true , Set . empty , NoAlignment )
2018-06-17 00:01:35 +00:00
}
2018-12-14 21:01:52 +00:00
if ( options . flag ( CompilationFlag . SoftwareStack ) ) {
if ( ! things . contains ( "__sp" ) ) {
2020-11-18 09:08:58 +00:00
things ( "__sp" ) = UninitializedMemoryVariable ( "__sp" , b , VariableAllocationMethod . Auto , None , Set . empty , NoAlignment , isVolatile = false )
things ( "__stack" ) = UninitializedArray ( "__stack" , 256 , None , b , b , readOnly = false , Set . empty , DivisibleAlignment ( 256 ) )
2018-12-14 21:01:52 +00:00
}
}
2018-01-31 21:26:20 +00:00
}
2018-12-31 12:20:32 +00:00
if ( ! things . contains ( "memory_barrier" ) ) {
2021-11-12 01:10:07 +00:00
things ( "memory_barrier" ) = MacroFunction ( "memory_barrier" , v , AssemblyOrMacroParamSignature ( Nil ) , isInAssembly = true , this , Nil , CpuFamily . forType ( options . platform . cpu ) match {
2018-12-31 12:20:32 +00:00
case CpuFamily . M6502 => List ( MosAssemblyStatement ( Opcode . CHANGED_MEM , AddrMode . DoesNotExist , LiteralExpression ( 0 , 1 ) , Elidability . Fixed ) )
case CpuFamily . I80 => List ( Z80AssemblyStatement ( ZOpcode . CHANGED_MEM , NoRegisters , None , LiteralExpression ( 0 , 1 ) , Elidability . Fixed ) )
2019-05-31 15:03:35 +00:00
case CpuFamily . I86 => List ( Z80AssemblyStatement ( ZOpcode . CHANGED_MEM , NoRegisters , None , LiteralExpression ( 0 , 1 ) , Elidability . Fixed ) )
2019-07-08 07:26:51 +00:00
case CpuFamily . M6809 => List ( M6809AssemblyStatement ( MOpcode . CHANGED_MEM , NonExistent , LiteralExpression ( 0 , 1 ) , Elidability . Fixed ) )
2018-12-31 12:20:32 +00:00
case _ => ???
} )
}
2020-03-15 22:48:27 +00:00
if ( ! things . contains ( "breakpoint" ) ) {
val p = get [ VariableType ] ( "pointer" )
if ( options . flag ( CompilationFlag . EnableBreakpoints ) ) {
2021-11-12 01:10:07 +00:00
things ( "breakpoint" ) = MacroFunction ( "breakpoint" , v , AssemblyOrMacroParamSignature ( Nil ) , isInAssembly = true , this , Nil , CpuFamily . forType ( options . platform . cpu ) match {
2020-03-15 22:48:27 +00:00
case CpuFamily . M6502 => List ( MosAssemblyStatement ( Opcode . CHANGED_MEM , AddrMode . DoesNotExist , VariableExpression ( "..brk" ) , Elidability . Fixed ) )
case CpuFamily . I80 => List ( Z80AssemblyStatement ( ZOpcode . CHANGED_MEM , NoRegisters , None , VariableExpression ( "..brk" ) , Elidability . Fixed ) )
case CpuFamily . I86 => List ( Z80AssemblyStatement ( ZOpcode . CHANGED_MEM , NoRegisters , None , VariableExpression ( "..brk" ) , Elidability . Fixed ) )
case CpuFamily . M6809 => List ( M6809AssemblyStatement ( MOpcode . CHANGED_MEM , NonExistent , VariableExpression ( "..brk" ) , Elidability . Fixed ) )
case _ => ???
} )
} else {
2021-11-12 01:10:07 +00:00
things ( "breakpoint" ) = MacroFunction ( "breakpoint" , v , AssemblyOrMacroParamSignature ( Nil ) , isInAssembly = true , this , Nil , Nil )
2020-03-15 22:48:27 +00:00
}
}
2017-12-06 23:23:30 +00:00
}
2018-01-19 23:57:37 +00:00
2018-12-30 17:55:03 +00:00
def hintTypo ( name : String ) : Unit = {
val realThings = this . things . keySet ++ parent . map ( _ . things . keySet ) . getOrElse ( Set ( ) )
2019-06-12 20:55:34 +00:00
//noinspection ScalaDeprecation
2020-07-31 15:07:25 +00:00
val matchingThings = realThings . filter ( thing => ! thing . contains ( "$" ) && StringUtils . getJaroWinklerDistance ( thing , name ) > 0.9 ) ++ hardcodedHints ( name )
2018-12-30 17:55:03 +00:00
if ( matchingThings . nonEmpty ) {
2020-07-31 15:07:25 +00:00
log . info ( "Did you mean: " + matchingThings . toSeq . sorted . mkString ( ", " ) )
}
}
private def hardcodedHints ( name : String ) : Set [ String ] = {
name match {
case "int" => Set ( "word" , "signed16" , "int32" )
case "unsigned" => Set ( "word" , "ubyte" , "unsigned16" , "unsigned32" )
case "char" => Set ( "byte" , "sbyte" )
case "signed" => Set ( "sbyte" , "signed16" )
case "uintptr_t" | "size_t" | "usize" => Set ( "word" )
case "short" | "intptr_t" | "ptrdiff_t" | "ssize_t" | "isize" => Set ( "word" , "signed16" )
case "uint8_t" | "u8" => Set ( "byte" , "ubyte" )
case "int8_t" | "i8" => Set ( "byte" , "sbyte" )
case "uint16_t" | "u16" => Set ( "word" , "unsigned16" )
case "int16_t" | "i16" => Set ( "word" , "signed16" )
case "uint32_t" | "u32" => Set ( "int32" , "unsigned32" )
case "int32_t" | "i32" => Set ( "int32" , "signed32" )
case "int64_t" | "i64" => Set ( "int64" , "signed64" )
case "boolean" | "_Bool" => Set ( "bool" )
case "string" => Set ( "pointer" )
case "puts" | "printf" | "print" => Set ( "putstrz" )
case "println" => Set ( "putstrz" , "new_line" )
case "strlen" => Set ( "strzlen" , "scrstrzlen" )
case "strcmp" => Set ( "strzcmp" , "scrstrzcmp" )
case "strcpy" => Set ( "strzcopy" , "scrstrzcopy" )
case "getch" | "getchar" => Set ( "readkey" )
case _ => Set . empty
2018-12-30 17:55:03 +00:00
}
}
2018-01-20 00:53:58 +00:00
private def checkName [ T <: Thing : Manifest ] ( objType : String , name : String , pos : Option [ Position ] ) : Unit = {
2018-01-19 23:57:37 +00:00
if ( maybeGet [ T ] ( name ) . isEmpty ) {
2018-07-30 16:15:44 +00:00
log . error ( s" $objType ` $name ` is not defined " , pos )
2018-12-30 17:55:03 +00:00
hintTypo ( name )
2018-01-19 23:57:37 +00:00
}
}
def nameCheck ( nodes : List [ _ <: Node ] ) : Unit = nodes . foreach ( nameCheck )
def nameCheck ( node : Node ) : Unit = node match {
2018-06-12 20:46:20 +00:00
case _ : MosAssemblyStatement => ( )
2018-07-16 21:00:26 +00:00
case _ : Z80AssemblyStatement => ( )
2020-06-09 19:43:04 +00:00
case _ : M6809AssemblyStatement => ( )
2018-01-19 23:57:37 +00:00
case _ : DeclarationStatement => ( )
2018-01-20 01:04:58 +00:00
case s : ForStatement =>
checkName [ Variable ] ( "Variable" , s . variable , s . position )
nameCheck ( s . start )
nameCheck ( s . end )
nameCheck ( s . body )
2020-07-24 17:11:27 +00:00
nameCheck ( s . extraIncrement )
case s : ForEachStatement =>
checkName [ Variable ] ( "Variable" , s . variable , s . position )
s . pointerVariable . foreach ( pv => checkName [ Variable ] ( "Variable" , pv , s . position ) )
nameCheck ( s . body )
2018-01-20 01:04:58 +00:00
case s : IfStatement =>
nameCheck ( s . condition )
nameCheck ( s . thenBranch )
nameCheck ( s . elseBranch )
case s : WhileStatement =>
nameCheck ( s . condition )
nameCheck ( s . body )
case s : DoWhileStatement =>
nameCheck ( s . body )
nameCheck ( s . condition )
2018-01-19 23:57:37 +00:00
case s : Statement => nameCheck ( s . getAllExpressions )
2018-03-17 17:12:31 +00:00
case BlackHoleExpression => ( )
case _ : BooleanLiteralExpression => ( )
2018-01-19 23:57:37 +00:00
case _ : LiteralExpression => ( )
2018-07-21 21:59:16 +00:00
case _ : GeneratedConstantExpression => ( )
2018-07-27 22:58:20 +00:00
case _ : TextLiteralExpression => ( )
2018-01-20 00:53:58 +00:00
case VariableExpression ( name ) =>
checkName [ VariableLikeThing ] ( "Variable or constant" , name , node . position )
2018-01-19 23:57:37 +00:00
case IndexedExpression ( name , index ) =>
2018-01-20 00:53:58 +00:00
checkName [ IndexableThing ] ( "Array or pointer" , name , node . position )
2018-01-19 23:57:37 +00:00
nameCheck ( index )
2019-04-15 17:45:26 +00:00
case DerefDebuggingExpression ( inner , _ ) =>
nameCheck ( inner )
2021-06-21 12:20:24 +00:00
case DerefExpression ( inner , _ , _ , _ ) =>
2019-04-15 17:45:26 +00:00
nameCheck ( inner )
2019-04-18 14:24:46 +00:00
case IndirectFieldExpression ( inner , firstIndices , fields ) =>
2019-04-15 17:45:26 +00:00
nameCheck ( inner )
2019-04-18 14:24:46 +00:00
firstIndices . foreach ( nameCheck )
2019-07-10 14:51:12 +00:00
fields . foreach ( f => f . _3 . foreach ( nameCheck ) )
2018-01-19 23:57:37 +00:00
case SeparateBytesExpression ( h , l ) =>
nameCheck ( h )
nameCheck ( l )
case SumExpression ( params , _ ) =>
nameCheck ( params . map ( _ . _2 ) )
2021-02-17 23:38:30 +00:00
case FunctionCallExpression ( "sizeof" | "typeof" , List ( ve @VariableExpression ( e ) ) ) =>
2018-12-16 14:43:17 +00:00
checkName [ Thing ] ( "Type, variable or constant" , e , ve . position )
2018-01-19 23:57:37 +00:00
case FunctionCallExpression ( name , params ) =>
2018-02-28 00:13:05 +00:00
if ( name . exists ( _ . isLetter ) && ! Environment . predefinedFunctions ( name ) ) {
2018-01-20 00:53:58 +00:00
checkName [ CallableThing ] ( "Function or type" , name , node . position )
2018-01-19 23:57:37 +00:00
}
nameCheck ( params )
}
2018-12-19 16:33:51 +00:00
def getBooleanConstant ( literal : String ) : Option [ Boolean ] =
maybeGet [ TypedThing ] ( literal ) . flatMap ( _ . typ match {
case ConstantBooleanType ( _ , x ) => Some ( x )
case _ => None
} )
def isAlias ( name : String ) : Boolean = {
things . get ( name ) . map ( _ . isInstanceOf [ Alias ] ) . orElse ( parent . map ( _ . isAlias ( name ) ) ) . getOrElse ( false )
}
def getAliases : Map [ String , String ] = {
things . values . flatMap {
2021-11-12 01:10:07 +00:00
case Alias ( a , b , _ , _ ) => Some ( a -> b )
2018-12-19 16:33:51 +00:00
case _ => None
} . toMap ++ parent . map ( _ . getAliases ) . getOrElse ( Map . empty )
}
2020-03-15 00:06:09 +00:00
def isVolatile ( target : Expression ) : Boolean = {
if ( eval ( target ) . isDefined ) return false
target match {
case _ : LiteralExpression => false
case _ : GeneratedConstantExpression => false
case e : VariableExpression => maybeGet [ Thing ] ( e . name ) match {
case Some ( v : Variable ) => v . isVolatile
case Some ( v : MfArray ) => true // TODO: all arrays assumed volatile for now
case Some ( _ : Constant ) => false
case Some ( _ : Type ) => false
case _ => true // TODO: ?
}
case e : FunctionCallExpression => e . expressions . exists ( isVolatile )
2021-02-02 18:30:13 +00:00
case e : SumExpression => e . expressions . exists ( e => isVolatile ( e . _2 ) )
2020-03-15 00:06:09 +00:00
case e : IndexedExpression => isVolatile ( VariableExpression ( e . name ) ) || isVolatile ( e . index )
case _ => true
}
}
2021-02-02 18:30:13 +00:00
def isGoodEmptyLoopCondition ( target : Expression ) : Boolean = {
if ( eval ( target ) . isDefined ) {
// the user means an infinite loop or an empty loop
return true
}
target match {
case _ : LiteralExpression => false
case _ : GeneratedConstantExpression => false
case e : VariableExpression => maybeGet [ Thing ] ( e . name ) match {
case Some ( v : Variable ) => v . isVolatile
case Some ( v : MfArray ) => true // TODO: all arrays assumed volatile for now
case Some ( _ : Constant ) => false
case Some ( _ : Type ) => false
case _ => true // TODO: ?
}
case e : FunctionCallExpression =>
e . functionName match {
case "==" | "!=" | ">" | "<" | ">=" | "<=" |
"*" | "*'" | "/" | "%%" |
"<<" | ">>" | "<<'" | ">>'" | ">>>>"
| "&" | "^" | "|" | "&&" | "^^" | "||" | "not" | "hi" | "lo" =>
e . expressions . exists ( isVolatile )
case _ => true
}
case e : SumExpression => e . expressions . exists ( e => isGoodEmptyLoopCondition ( e . _2 ) )
case e : IndexedExpression => isGoodEmptyLoopCondition ( VariableExpression ( e . name ) ) || isGoodEmptyLoopCondition ( e . index )
case _ => true
}
}
2020-03-15 00:06:09 +00:00
def overlapsVariable ( variable : String , expr : Expression ) : Boolean = {
if ( eval ( expr ) . isDefined ) return false
if ( expr . containsVariable ( variable ) ) return true
2021-03-20 00:23:51 +00:00
val varRootName = maybeGet [ Thing ] ( variable ) . getOrElse { return false } . rootName
2020-03-15 00:06:09 +00:00
if ( varRootName == "?" ) return true
if ( varRootName == "" ) return false
overlapsVariableImpl ( varRootName , expr )
}
private def overlapsVariableImpl ( varRootName : String , expr : Expression ) : Boolean = {
expr match {
case _ : LiteralExpression => false
case _ : GeneratedConstantExpression => false
case e : VariableExpression => maybeGet [ Thing ] ( e . name ) match {
case Some ( t ) =>
val rootName = t . rootName
rootName == "?" || rootName == varRootName
case _ => true // TODO: ?
}
case e : FunctionCallExpression => e . expressions . exists ( x => overlapsVariableImpl ( varRootName , x ) )
case e : IndexedExpression => overlapsVariableImpl ( varRootName , VariableExpression ( e . name ) ) || overlapsVariableImpl ( varRootName , e . index )
case _ => true
}
}
2017-12-06 23:23:30 +00:00
}
2018-02-28 00:13:05 +00:00
object Environment {
2019-10-07 23:33:55 +00:00
// built-in special-cased functions; can be considered keywords by some:
2021-02-17 23:38:30 +00:00
val predefinedFunctions : Set [ String ] = Set ( "not" , "hi" , "lo" , "nonet" , "sizeof" , "typeof" )
2019-10-07 23:33:55 +00:00
// built-in special-cased functions, not keywords, but assumed to work almost as such:
2020-03-19 18:43:24 +00:00
val specialFunctions : Set [ String ] = Set ( "call" )
// functions that exist only in constants:
val constOnlyBuiltinFunction : Set [ String ] = Set ( "sin" , "cos" , "tan" , "min" , "max" )
2019-10-07 23:33:55 +00:00
// keywords:
val neverIdentifiers : Set [ String ] = Set (
"array" , "const" , "alias" , "import" , "static" , "register" , "stack" , "volatile" , "asm" , "extern" , "kernal_interrupt" , "interrupt" , "reentrant" , "segment" ,
"struct" , "union" , "enum" ,
"for" , "if" , "do" , "while" , "else" , "return" , "default" ,
"to" , "until" , "paralleluntil" , "parallelto" , "downto" ,
"break" , "continue" ,
2018-12-21 21:35:16 +00:00
"inline" , "noinline"
) ++ predefinedFunctions
2019-10-07 23:33:55 +00:00
// predefined identifiers that cannot be overridden and do not name a type:
val neverValidTypeIdentifiers : Set [ String ] = Set (
"true" , "false" ,
) ++ neverIdentifiers
// predefined type identifiers:
val invalidNewIdentifiers : Set [ String ] = Set (
"byte" , "sbyte" , "word" , "pointer" , "void" , "long" , "bool" ,
"set_carry" , "set_zero" , "set_overflow" , "set_negative" ,
2020-03-25 22:53:26 +00:00
"clear_carry" , "clear_zero" , "clear_overflow" , "clear_negative" ) ++
Seq . iterate ( 8 , 16 ) ( _ + 8 ) . map ( "int" + _ ) ++
Seq . iterate ( 8 , 16 ) ( _ + 8 ) . map ( "unsigned" + _ ) ++
Seq . iterate ( 8 , 16 ) ( _ + 8 ) . map ( "signed" + _ ) ++ neverValidTypeIdentifiers
2019-10-07 23:33:55 +00:00
// built-in special-cased field names; can be considered keywords by some:
2019-04-16 14:34:17 +00:00
val invalidFieldNames : Set [ String ] = Set ( "addr" , "rawaddr" , "pointer" , "return" )
2018-02-28 00:13:05 +00:00
}