Array access (WIP)

This commit is contained in:
MrLetsplay 2024-10-27 16:21:46 +01:00
parent 0c23c326ac
commit c119a077d6
Signed by: mr
SSH Key Fingerprint: SHA256:92jBH80vpXyaZHjaIl47pjRq+Yt7XGTArqQg1V7hSqg
4 changed files with 151 additions and 2 deletions

View File

@ -12,6 +12,7 @@ The Elysium programming language.
- [ ] Modules - [ ] Modules
- [ ] Arrays - [ ] Arrays
- [ ] Structs - [ ] Structs
- [ ] Strings
- [X] Raw memory access - [X] Raw memory access
- [ ] Memory allocation, Heap allocator - [ ] Memory allocation, Heap allocator
- [ ] Garbage collector - [ ] Garbage collector

View File

@ -112,6 +112,34 @@ func (c *Compiler) getWATType(t Type) string {
panic(fmt.Sprintf("type not implemented in getWATType(): %s", t)) panic(fmt.Sprintf("type not implemented in getWATType(): %s", t))
} }
func (c *Compiler) getPrimitiveTypeSizeBytes(primitive PrimitiveType) int {
switch primitive {
case Primitive_I8, Primitive_U8, Primitive_Bool:
return 1
case Primitive_I16, Primitive_U16:
return 2
case Primitive_I32, Primitive_U32, Primitive_F32:
return 4
case Primitive_I64, Primitive_U64, Primitive_F64:
return 8
}
panic(fmt.Sprintf("unhandled type in getPrimitiveTypeSizeBytes(): %s", primitive))
}
func (c *Compiler) getTypeSizeBytes(t Type) int {
switch t.Type {
case Type_Primitive:
return c.getPrimitiveTypeSizeBytes(t.Value.(PrimitiveType))
case Type_Named, Type_Array:
return c.getPrimitiveTypeSizeBytes(Primitive_U64)
case Type_Tuple:
panic(fmt.Sprintf("tuple type passed to getTypeSizeBytes(): %s", t))
}
panic(fmt.Sprintf("unhandled type in getTypeSizeBytes(): %s", t))
}
func castPrimitiveWAT(from PrimitiveType, to PrimitiveType) (string, error) { func castPrimitiveWAT(from PrimitiveType, to PrimitiveType) (string, error) {
if from == to { if from == to {
return "", nil return "", nil
@ -492,6 +520,71 @@ func (c *Compiler) compileExpressionWAT(expr Expression) (string, error) {
wat += c.getWATType(raw.Type) + ".load\n" // TODO: use load8/load16(_s/u) for smaller types wat += c.getWATType(raw.Type) + ".load\n" // TODO: use load8/load16(_s/u) for smaller types
} }
return wat, nil
case Expression_ArrayAccess:
array := expr.Value.(ArrayAccessExpression)
localArray := Local{Name: "", Type: Type{Type: Type_Primitive, Value: c.getEffectiveAddressType(), Position: unknownPosition()}, IsParameter: false, Index: len(c.CurrentFunction.Locals)}
c.CurrentFunction.Locals = append(c.CurrentFunction.Locals, localArray)
localIndex := Local{Name: "", Type: Type{Type: Type_Primitive, Value: c.getEffectiveAddressType(), Position: unknownPosition()}, IsParameter: false, Index: len(c.CurrentFunction.Locals)}
c.CurrentFunction.Locals = append(c.CurrentFunction.Locals, localIndex)
arrayWAT, err := c.compileExpressionWAT(array.Array)
if err != nil {
return "", err
}
arrayWAT += "local.set $" + strconv.Itoa(localArray.Index) + "\n"
indexWAT, err := c.compileExpressionWAT(array.Index)
if err != nil {
return "", err
}
if !c.Wasm64 {
cast, err := castPrimitiveWAT(Primitive_I64, Primitive_I32)
if err != nil {
return "", err
}
indexWAT += cast
}
indexWAT += "local.set $" + strconv.Itoa(localIndex.Index) + "\n"
wat := arrayWAT + indexWAT
// Error if index <= 0
wat += "block\n"
wat += "local.get $" + strconv.Itoa(localIndex.Index) + "\n"
wat += c.getAddressWATType() + ".const 0\n"
wat += c.getAddressWATType() + ".gt_s\n"
wat += "br_if 0\n"
wat += "call $__builtin_panic\n"
wat += "end\n"
// Error if index >= array length
wat += "block\n"
wat += "local.get $" + strconv.Itoa(localIndex.Index) + "\n"
wat += "local.get $" + strconv.Itoa(localArray.Index) + "\n"
wat += "i32.load\n" // Load array length
wat += c.getAddressWATType() + ".lt_s\n"
wat += "br_if 0\n"
wat += "call $__builtin_panic\n"
wat += "end\n"
elementType := array.Array.ValueType.Value.(ArrayType).ElementType
wat += "local.get $" + strconv.Itoa(localIndex.Index) + "\n"
wat += c.getAddressWATType() + ".const " + strconv.Itoa(c.getTypeSizeBytes(elementType)) + "\n"
wat += c.getAddressWATType() + ".mul\n"
wat += "local.get $" + strconv.Itoa(localArray.Index) + "\n"
wat += c.getAddressWATType() + ".add\n"
wat += c.getAddressWATType() + ".const 4\n" // first 4 bytes = length
wat += c.getAddressWATType() + ".add\n"
wat += c.getWATType(elementType) + ".load\n" // TODO: use load8/load16(_s/u) for smaller types
return wat, nil return wat, nil
} }
@ -727,6 +820,13 @@ func (c *Compiler) compileFunctionWAT(function *ParsedFunction) (string, error)
func (c *Compiler) compile() (string, error) { func (c *Compiler) compile() (string, error) {
module := "(module (memory 0)\n" module := "(module (memory 0)\n"
module += "(func $__builtin_panic\n"
module += "i32.const 0\n"
module += "i32.const 0\n"
module += "i32.div_u\n"
module += "drop\n"
module += ")\n"
for _, file := range c.Files { for _, file := range c.Files {
for i := range file.Functions { for i := range file.Functions {
wat, err := c.compileFunctionWAT(&file.Functions[i]) wat, err := c.compileFunctionWAT(&file.Functions[i])

View File

@ -38,3 +38,7 @@ u64 assign(u64 a) {
a += 1u64; a += 1u64;
return raw(u64, a) += 2u64; return raw(u64, a) += 2u64;
} }
u64 test() {
return raw(u64[], 0x0u8)[0];
}

View File

@ -127,6 +127,10 @@ func isPrimitiveTypeExpandableTo(from PrimitiveType, to PrimitiveType) bool {
return true return true
} }
if isSignedInt(from) && !isSignedInt(to) {
return false
}
switch from { switch from {
case Primitive_I8, Primitive_U8: case Primitive_I8, Primitive_U8:
if to == Primitive_I16 || to == Primitive_U16 { if to == Primitive_I16 || to == Primitive_U16 {
@ -396,11 +400,14 @@ func (v *Validator) validatePotentiallyVoidExpression(expr *Expression) []error
return errors return errors
} }
if raw.Address.ValueType.Type != Type_Primitive || raw.Address.ValueType.Value.(PrimitiveType) != Primitive_U64 { typeU64 := Type{Type: Type_Primitive, Value: Primitive_U64, Position: unknownPosition()}
errors = append(errors, v.createError("address must evaluate to a u64 value", expr.Position)) if !isTypeExpandableTo(*raw.Address.ValueType, typeU64) {
errors = append(errors, v.createError("address must be expandable to a u64 value", expr.Position))
return errors return errors
} }
expandExpressionToType(&raw.Address, typeU64)
if !v.Wasm64 { if !v.Wasm64 {
castTo := Type{Type: Type_Primitive, Value: Primitive_U32} castTo := Type{Type: Type_Primitive, Value: Primitive_U32}
raw.Address = Expression{Type: Expression_Cast, Value: CastExpression{Type: castTo, Value: raw.Address}, ValueType: &castTo, Position: raw.Address.Position} raw.Address = Expression{Type: Expression_Cast, Value: CastExpression{Type: castTo, Value: raw.Address}, ValueType: &castTo, Position: raw.Address.Position}
@ -408,6 +415,43 @@ func (v *Validator) validatePotentiallyVoidExpression(expr *Expression) []error
expr.ValueType = &raw.Type expr.ValueType = &raw.Type
expr.Value = raw expr.Value = raw
case Expression_ArrayAccess:
array := expr.Value.(ArrayAccessExpression)
arrayErrors := v.validateExpression(&array.Array)
if len(arrayErrors) != 0 {
errors = append(errors, arrayErrors...)
}
indexErrors := v.validateExpression(&array.Index)
if len(indexErrors) != 0 {
errors = append(errors, indexErrors...)
return errors
}
if len(errors) != 0 {
return errors
}
if array.Array.ValueType.Type != Type_Array {
errors = append(errors, v.createError("trying to access non-array type as an array", array.Array.Position))
}
typeI64 := Type{Type: Type_Primitive, Value: Primitive_I64, Position: unknownPosition()}
if !isTypeExpandableTo(*array.Index.ValueType, typeI64) {
errors = append(errors, v.createError("array index must be expandable to an i64 value", array.Index.Position))
return errors
}
expandExpressionToType(&array.Index, typeI64)
if len(errors) != 0 {
return errors
}
elementType := array.Array.ValueType.Value.(ArrayType).ElementType
expr.ValueType = &elementType
expr.Value = array
default: default:
panic("expr not implemented") panic("expr not implemented")
} }