add offsetof()

This commit is contained in:
Irmen de Jong
2025-09-17 23:25:44 +02:00
parent 9461e4088c
commit f6c8e693a5
12 changed files with 86 additions and 36 deletions
@@ -103,6 +103,7 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *(BaseDataType.entries - BaseDataType.STRUCT_INSTANCE).toTypedArray())),
"offsetof" to FSignature(true, BaseDataType.UBYTE, FParam("field", BaseDataType.UBYTE)),
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
@@ -54,6 +54,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
"prog8_lib_structalloc" -> funcStructAlloc(call)
"sizeof" -> throw AssemblyError("sizeof must have been replaced with a constant")
"offsetof" -> throw AssemblyError("offsetof must have been replaced with a constant")
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
}
}
@@ -5,6 +5,7 @@ import prog8.ast.FatalAstException
import prog8.ast.Program
import prog8.ast.SyntaxError
import prog8.ast.expressions.*
import prog8.ast.statements.StructDecl
import prog8.ast.statements.VarDecl
import prog8.code.core.*
import kotlin.math.*
@@ -15,6 +16,7 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
"abs" to ::builtinAbs,
"len" to ::builtinLen,
"sizeof" to ::builtinSizeof,
"offsetof" to ::builtinOffsetof,
"sgn" to ::builtinSgn,
"sqrt__ubyte" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
"sqrt__uword" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
@@ -90,6 +92,25 @@ private fun builtinAbs(args: List<Expression>, position: Position, program: Prog
else throw SyntaxError("abs requires one integer argument", position)
}
private fun builtinOffsetof(args: List<Expression>, position: Position, program: Program): NumericLiteral {
// 1 arg, "Struct.field"
if(args.size!=1)
throw SyntaxError("offsetof requires one argument", position)
val identifier = (args[0] as? IdentifierReference)?.nameInSource
if(identifier==null || identifier.size<2)
throw CannotEvaluateException("offsetof","argument should be an identifier of the form Struct.field")
val structname = identifier.dropLast(1)
val fieldname = identifier.last()
val struct = args[0].definingScope.lookup(structname) as? StructDecl
if(struct==null)
throw SyntaxError("cannot find struct '$structname'", args[0].position)
val offset = struct.offsetof(fieldname, program.memsizer)
if(offset==null)
throw SyntaxError("no such field '${identifier.joinToString(".")}'", args[0].position)
return NumericLiteral.optimalInteger(offset.toInt(), position)
}
private fun builtinSizeof(args: List<Expression>, position: Position, program: Program): NumericLiteral {
// 1 arg, type = anything, result type = ubyte or uword
if(args.size!=1)
@@ -414,6 +414,15 @@ class StructDecl(override val name: String, val fields: Array<Pair<DataType, Str
override fun getFieldType(name: String): DataType? = fields.firstOrNull { it.second==name }?.first
override val scopedNameString by lazy { scopedName.joinToString(".") }
fun offsetof(fieldname: String, sizer: IMemSizer): UByte? {
fields.fold(0) { offset, field ->
if (field.second == fieldname)
return offset.toUByte()
offset + sizer.memorySize(field.first, 1)
}
return null
}
}
class StructFieldRef(val pointer: IdentifierReference, val struct: StructDecl, val type: DataType, override val name: String, override val position: Position): Statement(), INamedStatement {
+5
View File
@@ -118,6 +118,11 @@ mkword (msb, lsb)
Don't get confused by how the system actually stores this 16-bit word value in memory (which is
in little-endian format, so lsb first then msb)
offsetof (Struct.field)
The offset in bytes of the given field in the struct. The first field will always have offset 0.
Usually you just reference the fields directly but in some cases it might be useful to know how many
bytes from the start of the structure a field is located at.
peek (address)
same as @(address) - reads the byte at the given address in memory.
-1
View File
@@ -61,7 +61,6 @@ and for example the below code omits line 5::
STRUCTS and TYPED POINTERS
--------------------------
- add offsetof()
- fix code size regressions (if any left)
- optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0
- update structpointers.rst docs with 6502 specific things?
+44 -30
View File
@@ -1,36 +1,50 @@
%option enable_floats
%import textio
%zeropage basicsafe
main {
struct Node {
ubyte id
str name
uword array
bool flag
float perc
}
struct Foobar {
bool thing
}
sub start() {
^^Node test = []
txt.print("expected: 0 10 30\n")
counter = 0
txt.print_ub(nodefer(10))
txt.spc()
txt.print_ub(nodefer(20))
txt.spc()
txt.print_ub(nodefer(30))
txt.nl()
test.id ++
test.array += 1000
test.id <<= 2
test.id <<= cx16.r0L
test.id >>= 3
test.id >>= cx16.r0L
test.id &= 1
; test.id *= 5 ; TODO implement this
; test.id /= 5 ; TODO implement this
test.array ^= 1000
test.array |= 1000
test.array &= 1000
test.array >>= 3
test.array >>= cx16.r0L
test.array <<= 2
test.array <<= cx16.r0L
test.array *= 5
counter = 0
txt.print_ub(add(10))
txt.spc()
txt.print_ub(add(20))
txt.spc()
txt.print_ub(add(30))
txt.nl()
counter = 0
txt.print_ub(add2(10))
txt.spc()
txt.print_ub(add2(20))
txt.spc()
txt.print_ub(add2(30))
txt.nl()
}
ubyte counter = 0
sub nodefer(ubyte amount) -> ubyte {
ubyte result = counter
counter += amount
return result
}
sub add(ubyte amount) -> ubyte {
defer counter += amount ; TODO FIX : BORKED!
return counter
}
sub add2(ubyte amount) -> ubyte {
cx16.r0L = 0
defer counter += amount
return counter + cx16.r0L
}
}
+1 -1
View File
@@ -14,7 +14,7 @@
<keywords keywords="&amp;;&amp;&amp;;&amp;&lt;;&amp;&gt;;-&gt;;@;^^;alias;and;as;asmsub;break;clobbers;continue;do;downto;else;extsub;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;on;or;repeat;return;step;sub;to;true;unroll;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%align;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%jmptable;%launcher;%memtop;%option;%output;%zeropage;%zpallowed;%zpreserved;@align64;@alignpage;@alignword;@bank;@dirty;@nosplit;@nozp;@requirezp;@shared;@split;@zp;atascii:;c64os:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" />
<keywords3 keywords="^^bool;^^byte;^^float;^^long;^^str;^^ubyte;^^uword;^^word;bool;byte;const;float;long;str;struct;ubyte;uword;void;word" />
<keywords4 keywords="abs;bmx;call;callfar;callfar2;cbm;clamp;cmp;conv;cx16;defer;diskio;divmod;floats;len;lsb;lsw;math;max;memory;min;mkword;msb;msw;peek;peekbool;peekf;peekw;poke;pokebool;pokef;pokew;psg;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt;strings;sys;txt;verafx" />
<keywords4 keywords="abs;bmx;call;callfar;callfar2;cbm;clamp;cmp;conv;cx16;defer;diskio;divmod;floats;len;lsb;lsw;math;max;memory;min;mkword;msb;msw;offsetof;peek;peekbool;peekf;peekw;poke;pokebool;pokef;pokew;psg;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt;strings;sys;txt;verafx" />
</highlighting>
<extensionMap>
<mapping ext="p8" />
+1 -1
View File
@@ -27,7 +27,7 @@
<Keywords name="Keywords1">void const&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte bool&#x000D;&#x000A;long word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared split nosplit requirezp nozp struct</Keywords>
<Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%ir&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%align&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%encoding&#x000D;&#x000A;%import&#x000D;&#x000A;%jmptable&#x000D;&#x000A;%memtop&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved&#x000D;&#x000A;%zpallowed</Keywords>
<Keywords name="Keywords3">inline sub asmsub extsub&#x000D;&#x000A;clobbers&#x000D;&#x000A;asm&#x000D;&#x000A;if&#x000D;&#x000A;when else&#x000D;&#x000A;if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z&#x000D;&#x000A;for in step do while repeat unroll&#x000D;&#x000A;break continue return goto</Keywords>
<Keywords name="Keywords4">alias abs call callfar callfar2 clamp cmp defer divmod len lsb lsw lsl lsr memory mkword min max msb msw peek peekw peekf peekbool poke pokew pokef pokebool rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sqrtw</Keywords>
<Keywords name="Keywords4">alias abs call callfar callfar2 clamp cmp defer divmod len lsb lsw lsl lsr memory mkword min max msb msw peek peekw peekf peekbool poke pokew pokef pokebool rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof offsetof sqrtw</Keywords>
<Keywords name="Keywords5">true false&#x000D;&#x000A;not and or xor&#x000D;&#x000A;as to downto |&gt;</Keywords>
<Keywords name="Keywords6"></Keywords>
<Keywords name="Keywords7"></Keywords>
+1 -1
View File
@@ -27,7 +27,7 @@
<Keywords name="Keywords1">void const&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte bool&#x000D;&#x000A;long word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared split nosplit requirezp nozp struct</Keywords>
<Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%ir&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%align&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%encoding&#x000D;&#x000A;%import&#x000D;&#x000A;%jmptable&#x000D;&#x000A;%memtop&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved&#x000D;&#x000A;%zpallowed</Keywords>
<Keywords name="Keywords3">inline sub asmsub extsub&#x000D;&#x000A;clobbers&#x000D;&#x000A;asm&#x000D;&#x000A;if&#x000D;&#x000A;when else&#x000D;&#x000A;if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z&#x000D;&#x000A;for in step do while repeat unroll&#x000D;&#x000A;break continue return goto</Keywords>
<Keywords name="Keywords4">alias abs call callfar callfar2 clamp cmp defer divmod len lsb lsw lsl lsr memory mkword min max msb msw peek peekw peekf peekbool poke pokew pokef pokebool rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sqrtw</Keywords>
<Keywords name="Keywords4">alias abs call callfar callfar2 clamp cmp defer divmod len lsb lsw lsl lsr memory mkword min max msb msw peek peekw peekf peekbool poke pokew pokef pokebool rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof offsetof sqrtw</Keywords>
<Keywords name="Keywords5">true false&#x000D;&#x000A;not and or xor&#x000D;&#x000A;as to downto |&gt;</Keywords>
<Keywords name="Keywords6"></Keywords>
<Keywords name="Keywords7"></Keywords>
@@ -156,7 +156,7 @@ contexts:
- match: (\b(const)\b)
scope: storage.modifier.prog8
support:
- match: (\b(abs|atan|ceil|cos|cos8u|cos8|cos16u|cos16|deg|floor|ln|log2|rad|round|sin|sgn|sin8u|sin8|sin16u|sin16|sqrt16|sqrt|tan|any|all|len|max|min|reverse|sum|sort|memcopy|memset|memsetw|leftstr|rightstr|strlen|strcmp|strncmp|substr|exit|lsb|msb|lsw|msw|mkword|rnd|rndw|rndf|rol|rol2|ror|ror2|rsave|rrestore|read_flags|sizeof|set_carry|clear_carry|set_irqd|clear_irqd|swap)\b)
- match: (\b(abs|atan|ceil|cos|cos8u|cos8|cos16u|cos16|deg|floor|ln|log2|rad|round|sin|sgn|sin8u|sin8|sin16u|sin16|sqrt16|sqrt|tan|any|all|len|max|min|reverse|sum|sort|memcopy|memset|memsetw|leftstr|rightstr|strlen|strcmp|strncmp|substr|exit|lsb|msb|lsw|msw|mkword|rnd|rndw|rndf|rol|rol2|ror|ror2|rsave|rrestore|read_flags|sizeof|offsetof|set_carry|clear_carry|set_irqd|clear_irqd|swap)\b)
scope: support.function.prog8
variable:
- match: (\b\w+\b)
+1 -1
View File
@@ -14,7 +14,7 @@ syn keyword prog8BuiltInFunc len
" Miscellaneous functions
syn keyword prog8BuiltInFunc cmp divmod lsb msb lsw msw mkword min max peek peekw peekf peekbool poke pokew pokef pokebool rsave rsavex rrestore rrestorex
syn keyword prog8BuiltInFunc rol rol2 ror ror2 sizeof setlsb setmsb
syn keyword prog8BuiltInFunc rol rol2 ror ror2 sizeof offsetof setlsb setmsb
syn keyword prog8BuiltInFunc memory call callfar callfar2 clamp defer alias