From 7bc96397eb0ec2ba5d298c84ef0e430c04c956c4 Mon Sep 17 00:00:00 2001 From: fubark Date: Wed, 10 Jul 2024 12:01:43 -0400 Subject: [PATCH] Flatten struct fields. --- src/bc_gen.zig | 149 ++++++++++++++++++++---------- src/builtins/builtins.zig | 19 ++-- src/builtins/builtins_vm.cy | 6 +- src/bytecode.zig | 65 +++++++++----- src/cgen.zig | 2 +- src/heap.zig | 41 ++++++--- src/ir.zig | 3 + src/sema.zig | 83 ++++++++++++----- src/sym.zig | 14 +-- src/types.zig | 16 +++- src/vm.c | 165 +++++++++++++++++++++++++++++----- src/vm.h | 6 +- src/vm.zig | 5 +- test/memory/default_memory.cy | 38 ++++---- 14 files changed, 449 insertions(+), 163 deletions(-) diff --git a/src/bc_gen.zig b/src/bc_gen.zig index 3febd3620..bc529cfd9 100644 --- a/src/bc_gen.zig +++ b/src/bc_gen.zig @@ -758,13 +758,14 @@ fn genAddressOf2(c: *Chunk, expr: usize, cstr: Cstr, node: *ast.Node) !GenValue } else { if (slot.boxed_up) { const temp = try bc.reserveTemp(c, bt.Integer); - try c.pushCode(.addr_local, &.{ local_slot, temp }, node); + const lifted_struct = c.sema.isStructType(expr_t); + try c.pushCode(.addr_local, &.{ local_slot, @intFromBool(lifted_struct), temp }, node); try initSlot(c, temp, false, node); - try pushAddrField(c, temp, 0, inst.dst, node); + try c.pushCode(.copy, &.{ temp, inst.dst }, node); try popTemp(c, temp, node); } else { - try c.pushCode(.addr_local, &.{ local_slot, inst.dst }, node); + try c.pushCode(.addr_local, &.{ local_slot, 0, inst.dst }, node); } } if (inst.own_dst) { @@ -778,7 +779,8 @@ fn genAddressOf2(c: *Chunk, expr: usize, cstr: Cstr, node: *ast.Node) !GenValue const childv = try genAddressOf2(c, data.rec, Cstr.simple, node); try initTempValue(c, childv, node); - try pushAddrField(c, childv.reg, data.idx, inst.dst, node); + const fields = c.sema.getTypeSym(data.parent_t).cast(.struct_t).getFields(); + try pushAddrConstIndex(c, childv.reg, @intCast(fields[data.idx].offset), inst.dst, node); try popTempValue(c, childv, node); if (inst.own_dst) { try initSlot(c, inst.dst, false, node); @@ -794,30 +796,11 @@ fn genAddressOf2(c: *Chunk, expr: usize, cstr: Cstr, node: *ast.Node) !GenValue return genExpr(c, deref.expr, cstr); }, .call_sym => { - const call_sym = c.ir.getExprData(expr, .call_sym); - // TODO: This is a hack to check for pointer $index call. - // Perhaps check for a function tag instead. - if (call_sym.func.is_method and std.mem.eql(u8, "$index", call_sym.func.name())) { - const args = c.ir.getArray(call_sym.args, u32, call_sym.numArgs); - - const inst = try bc.selectForDstInst(c, cstr, bt.Integer, false, node); - - const ptrv = try genAddressOf2(c, args[0], Cstr.simple, node); - try initTempValue(c, ptrv, node); - - const idxv = try genExpr(c, args[1], Cstr.simple); - try initTempValue(c, idxv, node); - - try c.pushCode(.addr_index, &.{ ptrv.reg, idxv.reg, inst.dst }, node); - try popTempValue(c, idxv, node); - try popTempValue(c, ptrv, node); - if (inst.own_dst) { - try initSlot(c, inst.dst, false, node); - } - return finishDstInst(c, inst, false); - } else { - return error.TODO; + const ret_t = c.ir.getExprType(expr).id; + if (c.sema.isPointerType(ret_t)) { + return genCallFuncSym(c, expr, cstr, node); } + return error.TODO; }, else => { log.tracev("{}", .{code}); @@ -830,7 +813,8 @@ fn genDeref(c: *Chunk, idx: usize, cstr: Cstr, node: *ast.Node) !GenValue { const data = c.ir.getExprData(idx, .deref); const ret_t = c.ir.getExprType(idx).id; - const ret_is_struct = c.sema.getTypeKind(ret_t) == .@"struct"; + const ret_te = c.sema.getType(ret_t); + const ret_is_struct = ret_te.kind == .struct_t; const retain = ret_is_struct or c.sema.isRcCandidateType(ret_t); const inst = try bc.selectForDstInst(c, cstr, ret_t, ret_is_struct, node); @@ -838,8 +822,10 @@ fn genDeref(c: *Chunk, idx: usize, cstr: Cstr, node: *ast.Node) !GenValue { try initTempValue(c, srcv, node); if (ret_is_struct) { - const nfields: u8 = @intCast(c.sema.types.items[ret_t].data.@"struct".numFields); - try c.pushCode(.deref_struct, &.{ srcv.reg, nfields, inst.dst }, node); + const nfields: u8 = @intCast(c.sema.types.items[ret_t].data.struct_t.nfields); + const start = c.buf.ops.items.len; + try c.pushCode(.deref_struct, &.{ srcv.reg, 0, 0, nfields, inst.dst }, node); + c.buf.setOpArgU16(start + 2, @intCast(ret_t)); } else { try c.pushCode(.deref, &.{ srcv.reg, @intFromBool(retain), inst.dst }, node); } @@ -857,17 +843,19 @@ fn genField(c: *Chunk, idx: usize, cstr: Cstr, node: *ast.Node) !GenValue { const inst = try bc.selectForDstInst(c, cstr, ret_t, true, node); const rec_t = c.ir.getExprType(data.rec).id; - const rec_is_struct = c.sema.getTypeKind(rec_t) == .@"struct"; + const rec_te = c.sema.getType(rec_t); const rec_is_pointer = c.sema.isPointerType(rec_t); const willRetain = c.sema.isRcCandidateType(ret_t); - if (rec_is_struct or rec_is_pointer) { + if (rec_te.kind == .struct_t or rec_is_pointer) { const addrv = try genAddressOf2(c, idx, Cstr.simple, node); try initTempValue(c, addrv, node); - const ret_is_struct = c.sema.getTypeKind(ret_t) == .@"struct"; - if (ret_is_struct) { - const numFields: u8 = @intCast(c.sema.types.items[ret_t].data.@"struct".numFields); - try c.pushCode(.deref_struct, &.{ addrv.reg, numFields, inst.dst }, node); + const ret_te = c.sema.getType(ret_t); + if (ret_te.kind == .struct_t) { + const numFields: u8 = @intCast(c.sema.types.items[ret_t].data.struct_t.nfields); + const start = c.buf.ops.items.len; + try c.pushCode(.deref_struct, &.{ addrv.reg, 0, 0, numFields, inst.dst }, node); + c.buf.setOpArgU16(start + 2, @intCast(ret_t)); } else { try c.pushCode(.deref, &.{ addrv.reg, @intFromBool(willRetain), inst.dst }, node); } @@ -888,12 +876,51 @@ fn genField(c: *Chunk, idx: usize, cstr: Cstr, node: *ast.Node) !GenValue { return finishDstInst(c, inst, willRetain); } -fn genObjectInit(c: *Chunk, idx: usize, cstr: Cstr, node: *ast.Node) !GenValue { +fn genStructInit(c: *Chunk, idx: usize, cstr: Cstr, node: *ast.Node) !GenValue { const data = c.ir.getExprData(idx, .object_init); const ret_t = c.ir.getExprType(idx).id; const inst = try bc.selectForDstInst(c, cstr, ret_t, true, node); + const args = c.ir.getArray(data.args, u32, data.numArgs); + const argStart = numSlots(c); + for (args) |argIdx| { + const arg_t = c.ir.getExprType(argIdx).id; + const temp = try bc.reserveTemp(c, arg_t); + const argv = try genExpr(c, argIdx, Cstr.toTempRetain(temp)); + try initSlot(c, temp, argv.retained, node); + } + + const ret_te = c.sema.types.items[ret_t]; + const ret_ts = ret_te.sym.cast(.struct_t); + const nfields = ret_te.data.struct_t.nfields; + try pushStructInit(c, data.typeId, @intCast(argStart), @intCast(nfields), ret_ts.getFields(), inst.dst, node); + for (0..args.len) |i| { + const slot_id = argStart + args.len - i - 1; + const slot = getSlot(c, slot_id); + if (slot.boxed_retains) { + try consumeTemp(c, @intCast(slot_id), node); + } + } + try popTemps(c, args.len, node); + if (inst.own_dst) { + try initSlot(c, inst.dst, true, node); + } + + return finishDstInst(c, inst, true); +} + +fn genObjectInit(c: *Chunk, idx: usize, cstr: Cstr, node: *ast.Node) !GenValue { + const ret_t = c.ir.getExprType(idx).id; + const ret_te = c.sema.getType(ret_t); + if (ret_te.kind == .struct_t) { + return genStructInit(c, idx, cstr, node); + } + + const data = c.ir.getExprData(idx, .object_init); + + const inst = try bc.selectForDstInst(c, cstr, ret_t, true, node); + // TODO: Would it be faster/efficient to copy the fields into contiguous registers // and copy all at once to heap or pass locals into the operands and iterate each and copy to heap? // The current implementation is the former. @@ -1043,8 +1070,12 @@ fn genSetDeref(c: *Chunk, idx: usize, node: *ast.Node) !void { try initTempValue(c, rightv, node); const right_t = c.ir.getExprType(data.right).id; - const right_is_struct = c.sema.getTypeKind(right_t) == .@"struct"; - try c.pushCode(.set_deref, &.{ ptrv.reg, @intFromBool(right_is_struct), rightv.reg }, node); + const right_te = c.sema.types.items[right_t]; + if (right_te.kind == .struct_t) { + try c.pushCode(.set_deref_struct, &.{ ptrv.reg, @intCast(right_te.data.struct_t.nfields), rightv.reg }, node); + } else { + try c.pushCode(.set_deref, &.{ ptrv.reg, rightv.reg }, node); + } try consumeTempValue(c, rightv, node); try popTempValue(c, rightv, node); try popTempValue(c, ptrv, node); @@ -1055,17 +1086,23 @@ fn setField(c: *Chunk, idx: usize, node: *ast.Node) !void { const fieldData = c.ir.getExprData(data.field, .field); const type_id = c.ir.getExprType(fieldData.rec).id; - const isStruct = c.sema.getTypeKind(type_id) == .@"struct"; + const type_e = c.sema.getType(type_id); + // const rec_is_pointer = c.sema.isPointerType(type_id); // Receiver. - if (isStruct) { + if (type_e.kind == .struct_t) { const addrv = try genAddressOf2(c, data.field, Cstr.simple, node); try initTempValue(c, addrv, node); const rightv = try genExpr(c, data.right, Cstr.simpleRetain); try initTempValue(c, rightv, node); - try c.pushCode(.set_deref, &.{ addrv.reg, 1, rightv.reg }, node); + const right_te = c.sema.getType(c.ir.getExprType(data.right).id); + if (right_te.kind == .struct_t) { + try c.pushCode(.set_deref_struct, &.{ addrv.reg, @intCast(right_te.data.struct_t.nfields), rightv.reg }, node); + } else { + try c.pushCode(.set_deref, &.{ addrv.reg, rightv.reg }, node); + } try consumeTempValue(c, rightv, node); try popTempValue(c, rightv, node); @@ -1780,8 +1817,8 @@ fn genLocalReg(c: *Chunk, reg: SlotId, slot_t: cy.TypeId, cstr: Cstr, node: *ast if (!slot.boxed_up) { const type_e = c.sema.getType(slot_t); - if (type_e.kind == .@"struct") { - return genValueLocal(c, reg, @intCast(type_e.data.@"struct".numFields), cstr, node); + if (type_e.kind == .struct_t) { + return genValueLocal(c, reg, @intCast(type_e.data.struct_t.nfields), cstr, node); } const srcv = regValue(c, reg, false); @@ -1796,7 +1833,7 @@ fn genLocalReg(c: *Chunk, reg: SlotId, slot_t: cy.TypeId, cstr: Cstr, node: *ast return genToExact(c, srcv, exact_cstr, node); } else { const type_e = c.sema.getType(slot_t); - if (type_e.kind == .@"struct") { + if (type_e.kind == .struct_t) { return genLiftedValueLocal(c, reg, slot, cstr, node); } @@ -3861,6 +3898,24 @@ fn pushObjectInit(c: *cy.Chunk, typeId: cy.TypeId, startLocal: u8, numFields: u8 } } +fn pushStructInit(c: *cy.Chunk, typeId: cy.TypeId, startLocal: u8, val_size: u8, fields: []const cy.sym.FieldInfo, dst: SlotId, debugNode: *ast.Node) !void { + const start = c.buf.ops.items.len; + if (val_size <= 4) { + try c.pushCode(.struct_small, &.{ 0, 0, startLocal, @intCast(fields.len), dst }, debugNode); + } else { + try c.pushFCode(.struct_init, &.{ 0, 0, startLocal, @intCast(fields.len), dst }, debugNode); + } + c.buf.setOpArgU16(start + 1, @intCast(typeId)); + for (fields) |field| { + const type_e = c.sema.types.items[field.type]; + if (type_e.kind == .struct_t) { + try c.buf.pushOperand(@intCast(type_e.data.struct_t.nfields)); + } else { + try c.buf.pushOperand(0); + } + } +} + fn pushFieldDyn(c: *cy.Chunk, recv: u8, dst: u8, fieldId: u16, debugNode: *ast.Node) !void { const start = c.buf.ops.items.len; try c.pushFCode(.fieldDyn, &.{ recv, dst, 0, 0, 0, 0, 0, 0, 0, 0 }, debugNode); @@ -3871,8 +3926,8 @@ fn pushField(c: *cy.Chunk, recv: u8, fieldIdx: u8, retain: bool, dst: u8, debugN try c.pushCode(.field, &.{ recv, fieldIdx, @intFromBool(retain), dst }, debugNode); } -fn pushAddrField(c: *cy.Chunk, recv: u8, fieldIdx: u8, dst: u8, debugNode: *ast.Node) !void { - try c.pushCode(.addr_field, &.{ recv, fieldIdx, dst }, debugNode); +fn pushAddrConstIndex(c: *cy.Chunk, recv: u8, offset: u8, dst: u8, debugNode: *ast.Node) !void { + try c.pushCode(.addr_const_index, &.{ recv, offset, dst }, debugNode); } /// Selecting for a non local inst with a dst operand. diff --git a/src/builtins/builtins.zig b/src/builtins/builtins.zig index c91379120..6357fd3af 100644 --- a/src/builtins/builtins.zig +++ b/src/builtins/builtins.zig @@ -1170,13 +1170,13 @@ fn pointerIndex(vm: *cy.VM) anyerror!Value { const ptr: [*]cy.Value = @ptrCast(@alignCast(vm.getPointer(0))); const elem_t: cy.TypeId = @intCast(vm.getInt(1)); const idx: usize = @intCast(vm.getInt(2)); - // Always an 8 byte stride. But cstructs need to be deep copied. if (vm.sema.isUnboxedType(elem_t)) { - return ptr[idx]; + // Always an 8 byte stride. + return Value.initRaw(@intCast(@intFromPtr(ptr + idx))); } else { - const obj = ptr[idx].asHeapObject(); - const type_e = vm.c.types[obj.getTypeId()]; - return cy.vm.copyObject(vm, ptr[idx].asHeapObject(), @intCast(type_e.data.@"struct".numFields)); + const type_e = vm.c.types[elem_t]; + const n = type_e.data.struct_t.nfields; + return Value.initRaw(@intCast(@intFromPtr(ptr + idx*n))); } } @@ -1200,14 +1200,15 @@ fn pointerSetIndex(vm: *cy.VM) anyerror!Value { const idx: usize = @intCast(vm.getInt(2)); const val = vm.getValue(3); - // Always an 8 byte stride. But cstructs need to be deep copied. if (vm.sema.isUnboxedType(elem_t)) { + // Always an 8 byte stride. ptr[idx] = val; } else { const type_e = vm.c.types[elem_t]; - const prev = ptr[idx]; - ptr[idx] = try cy.vm.copyObject(vm, val.asHeapObject(), @intCast(type_e.data.@"struct".numFields)); - vm.release(prev); + const n = type_e.data.struct_t.nfields; + const dst = ptr[idx*n..idx*n+n]; + const src = val.asHeapObject().object.getValuesPtr()[0..n]; + @memcpy(dst, src); } return Value.Void; } diff --git a/src/builtins/builtins_vm.cy b/src/builtins/builtins_vm.cy index 013381291..f4f3efc6a 100644 --- a/src/builtins/builtins_vm.cy +++ b/src/builtins/builtins_vm.cy @@ -453,10 +453,10 @@ type ArrayIterator: return res type pointer[T type] #int64_t: - func $index(self, idx int) T: + func $index(self, idx int) *T: return self.index((T).id(), idx) - @host -func index(self, elem_t int, idx int) T + @host -func index(self, elem_t int, idx int) *T func $index(self, range Range) []T: return self.indexRange(([]T).id(), range) @@ -547,7 +547,7 @@ type Slice[T type] struct: ptr *T n int - func $index(self, idx int) T: + func $index(self, idx int) *T: return self.ptr[idx] func $index(self, range Range) []T: diff --git a/src/bytecode.zig b/src/bytecode.zig index cc1e12e06..3a6c82c11 100644 --- a/src/bytecode.zig +++ b/src/bytecode.zig @@ -570,12 +570,6 @@ pub fn dumpInst(vm: *cy.VM, pcOffset: u32, code: OpCode, pc: [*]const Inst, opts const dst = pc[4].val; len += try fmt.printCount(w, "%{} = (%{}).{}, +{}", &.{v(dst), v(recv), v(fieldIdx), v(retain)}); }, - .addr_field => { - const recv = pc[1].val; - const fieldIdx = pc[2].val; - const dst = pc[3].val; - len += try fmt.printCount(w, "%{} = &%{}.{}", &.{v(dst), v(recv), v(fieldIdx)}); - }, .fieldDyn => { const recv = pc[1].val; const dst = pc[2].val; @@ -661,21 +655,23 @@ pub fn dumpInst(vm: *cy.VM, pcOffset: u32, code: OpCode, pc: [*]const Inst, opts v(dst), v(type_id), v(vtable), v(src), }); }, - .object => { + .object, + .objectSmall => { const typeId = @as(*const align(1) u16, @ptrCast(pc + 1)).*; const argStart = pc[3].val; const numFields = pc[4].val; const dst = pc[5].val; - len += try fmt.printCount(w, "%{} = [type={} %{}..%{}]", &.{ - v(dst), v(typeId), v(argStart), v(argStart+numFields), + len += try fmt.printCount(w, "%{} = type={}{%{}..%{}}", &.{ + v(dst), v(typeId), v(argStart), v(argStart+numFields) }); }, - .objectSmall => { + .struct_init, + .struct_small => { const typeId = @as(*const align(1) u16, @ptrCast(pc + 1)).*; const argStart = pc[3].val; const numFields = pc[4].val; const dst = pc[5].val; - len += try fmt.printCount(w, "%{} = [type={} %{}..%{}]", &.{ + len += try fmt.printCount(w, "%{} = type={}{%{}..%{}}", &.{ v(dst), v(typeId), v(argStart), v(argStart+numFields) }); }, @@ -724,8 +720,15 @@ pub fn dumpInst(vm: *cy.VM, pcOffset: u32, code: OpCode, pc: [*]const Inst, opts }, .addr_local => { const local = pc[1].val; - const dst = pc[2].val; - len += try fmt.printCount(w, "%{} = &%{}", &.{v(dst), v(local)}); + const up_struct = pc[2].val == 1; + const dst = pc[3].val; + len += try fmt.printCount(w, "%{} = &%{}.values, upstruct={}", &.{v(dst), v(local), v(up_struct)}); + }, + .addr_const_index => { + const local = pc[1].val; + const offset = pc[2].val; + const dst = pc[3].val; + len += try fmt.printCount(w, "%{} = &%{}[{}]", &.{v(dst), v(local), v(offset)}); }, .addr_index => { const local = pc[1].val; @@ -741,15 +744,20 @@ pub fn dumpInst(vm: *cy.VM, pcOffset: u32, code: OpCode, pc: [*]const Inst, opts }, .deref_struct => { const ptr = pc[1].val; - const nfields = pc[2].val; - const dst = pc[3].val; + const nfields = pc[4].val; + const dst = pc[5].val; len += try fmt.printCount(w, "%{} = copy_struct(%{}.*, nfields={})", &.{v(dst), v(ptr), v(nfields)}); }, .set_deref => { const ref = pc[1].val; - const is_struct = pc[2].val == 1; + const val = pc[2].val; + len += try fmt.printCount(w, "%{}.* = %{}", &.{v(ref), v(val)}); + }, + .set_deref_struct => { + const ref = pc[1].val; + const nfields = pc[2].val; const val = pc[3].val; - len += try fmt.printCount(w, "%{}.* = %{}, struct={}", &.{v(ref), v(val), v(is_struct)}); + len += try fmt.printCount(w, "%{}.* = %{}, size={}", &.{v(ref), v(val), v(nfields)}); }, .setFieldDyn, .setFieldDynIC => { @@ -968,7 +976,7 @@ pub fn getInstLenAt(pc: [*]const Inst) u8 { const numVars = pc[1].val; return 2 + numVars; }, - .addr_local, + .set_deref, .not, .copy, .copyRetainSrc, @@ -990,10 +998,11 @@ pub fn getInstLenAt(pc: [*]const Inst) u8 { .symbol => { return 3; }, + .addr_local, + .addr_const_index, .addr_index, .deref, - .set_deref, - .addr_field, + .set_deref_struct, .set_up_value, .copyObj, .call, @@ -1009,7 +1018,6 @@ pub fn getInstLenAt(pc: [*]const Inst) u8 { .list_dyn, .enumOp, .setCaptured, - .deref_struct, .jumpNotCond => { return 4; }, @@ -1032,6 +1040,8 @@ pub fn getInstLenAt(pc: [*]const Inst) u8 { const numConds = pc[2].val; return 5 + numConds * 3; }, + .deref_struct, + .field_struct, .list, .object, .objectSmall, @@ -1039,6 +1049,11 @@ pub fn getInstLenAt(pc: [*]const Inst) u8 { .forRangeReverse => { return 6; }, + .struct_init, + .struct_small => { + const num_fields = pc[4].val; + return 6 + num_fields; + }, .trait, .coinit, .metatype => { @@ -1181,8 +1196,8 @@ pub const OpCode = enum(u8) { typeCheck = vmc.CodeTypeCheck, typeCheckOption = vmc.CodeTypeCheckOption, + field_struct = vmc.CodeFieldStruct, field = vmc.CodeField, - addr_field = vmc.CodeAddrField, fieldDyn = vmc.CodeFieldDyn, fieldDynIC = vmc.CodeFieldDynIC, lambda = vmc.CodeLambda, @@ -1208,6 +1223,8 @@ pub const OpCode = enum(u8) { stringTemplate = vmc.CodeStringTemplate, negFloat = vmc.CodeNegFloat, + struct_small = vmc.CodeStructSmall, + struct_init = vmc.CodeStruct, objectSmall = vmc.CodeObjectSmall, object = vmc.CodeObject, trait = vmc.CodeTrait, @@ -1215,10 +1232,12 @@ pub const OpCode = enum(u8) { box = vmc.CodeBox, unbox = vmc.CodeUnbox, addr_local = vmc.CodeAddrLocal, + addr_const_index = vmc.CodeAddrConstIndex, addr_index = vmc.CodeAddrIndex, deref = vmc.CodeDeref, deref_struct = vmc.CodeDerefStruct, set_deref = vmc.CodeSetDeref, + set_deref_struct = vmc.CodeSetDerefStruct, unwrapChoice = vmc.CodeUnwrapChoice, setFieldDyn = vmc.CodeSetFieldDyn, @@ -1298,7 +1317,7 @@ pub const OpCode = enum(u8) { }; test "bytecode internals." { - try t.eq(std.enums.values(OpCode).len, 123); + try t.eq(std.enums.values(OpCode).len, 127); try t.eq(@sizeOf(Inst), 1); if (cy.is32Bit) { try t.eq(@sizeOf(DebugMarker), 16); diff --git a/src/cgen.zig b/src/cgen.zig index bfd83433b..a8c2004c9 100644 --- a/src/cgen.zig +++ b/src/cgen.zig @@ -1320,7 +1320,7 @@ fn genObjectInit(c: *Chunk, loc: usize, cstr: Cstr, node: *ast.Node) !Value { const typ = c.sema.types.items[data.typeId]; switch (typ.kind) { - .@"struct" => { + .struct_t => { return error.TODO; }, .object => { diff --git a/src/heap.zig b/src/heap.zig index 7c8df3379..f37a72911 100644 --- a/src/heap.zig +++ b/src/heap.zig @@ -1589,6 +1589,8 @@ pub const VmExt = struct { pub const allocListIterDyn = Root.allocListIterDyn; pub const allocMapIterator = Root.allocMapIterator; pub const allocObjectSmall = Root.allocObjectSmall; + pub const allocObject = Root.allocObject; + pub const allocObject2 = Root.allocObject2; pub const allocType = Root.allocType; pub const allocTrait = Root.allocTrait; pub const allocFuture = Root.allocFuture; @@ -1906,6 +1908,14 @@ pub fn allocEmptyObject(self: *cy.VM, type_id: cy.TypeId, nfields: u32) !Value { return Value.initCycPtr(obj); } +pub fn allocObject2(self: *cy.VM, type_id: cy.TypeId, fields: []const Value) !Value { + if (fields.len <= 4) { + return self.allocObjectSmall(type_id, fields); + } else { + return self.allocObject(type_id, fields); + } +} + pub fn allocObjectSmall(self: *cy.VM, type_id: cy.TypeId, fields: []const Value) !Value { const obj = try allocPoolObject(self); obj.object = .{ @@ -2135,7 +2145,7 @@ pub fn freeObject(vm: *cy.VM, obj: *HeapObject, comptime skip_cyc_children: bool }, else => { if (cy.Trace) { - log.tracev("free {s} {}", .{vm.getTypeName(typeId), typeId}); + log.tracevIf(log_mem, "free {s} {}", .{vm.getTypeName(typeId), typeId}); // Check range. if (typeId >= vm.c.types_len) { @@ -2157,18 +2167,27 @@ pub fn freeObject(vm: *cy.VM, obj: *HeapObject, comptime skip_cyc_children: bool .int => { freePoolObject(vm, obj); }, - .@"struct" => { - const numFields = entry.data.@"struct".numFields; - for (obj.object.getValuesConstPtr()[0..numFields]) |child| { - if (skip_cyc_children and child.isCycPointer()) { - continue; + .struct_t => { + if (entry.data.struct_t.cstruct) { + const nfields = entry.data.struct_t.nfields; + if (nfields <= 4) { + freePoolObject(vm, obj); + } else { + freeExternalObject(vm, obj, (1 + nfields) * @sizeOf(Value), true); } - cy.arc.release(vm, child); - } - if (numFields <= 4) { - freePoolObject(vm, obj); } else { - freeExternalObject(vm, obj, (1 + numFields) * @sizeOf(Value), true); + const nfields = entry.data.struct_t.nfields; + for (obj.object.getValuesConstPtr()[0..nfields]) |child| { + if (skip_cyc_children and child.isCycPointer()) { + continue; + } + cy.arc.release(vm, child); + } + if (nfields <= 4) { + freePoolObject(vm, obj); + } else { + freeExternalObject(vm, obj, (1 + nfields) * @sizeOf(Value), true); + } } }, .trait => { diff --git a/src/ir.zig b/src/ir.zig index 04ae0a1b4..5a024b35b 100644 --- a/src/ir.zig +++ b/src/ir.zig @@ -289,6 +289,9 @@ pub const Field = struct { /// Receiver. rec: Loc, + /// Parent type is recorded since a field can be applied to a type or a pointer. + parent_t: cy.TypeId, + /// Field index of receiver. idx: u8, diff --git a/src/sema.zig b/src/sema.zig index a61de9b0d..77727bea8 100644 --- a/src/sema.zig +++ b/src/sema.zig @@ -875,6 +875,7 @@ fn assignStmt(c: *cy.Chunk, node: *ast.Node, left_n: *ast.Node, right: *ast.Node var loc = try c.ir.pushExpr(.field, c.alloc, field_s.type, debug_node, .{ .idx = @intCast(field_s.idx), .rec = rec.irIdx, + .parent_t = child_t, }); const ptr_t = try getPointerType(c, field_s.type); loc = try c.ir.pushExpr(.address_of, c.alloc, ptr_t, debug_node, .{ @@ -896,7 +897,7 @@ fn assignStmt(c: *cy.Chunk, node: *ast.Node, left_n: *ast.Node, right: *ast.Node } const field_s = sym.cast(.field); - if (c.sema.getTypeKind(rec.type.id) == .@"struct") { + if (c.sema.getTypeKind(rec.type.id) == .struct_t) { if (rec.resType == .field) { const prev = c.ir.getExprDataPtr(rec.irIdx, .field); prev.member_cont = true; @@ -905,6 +906,7 @@ fn assignStmt(c: *cy.Chunk, node: *ast.Node, left_n: *ast.Node, right: *ast.Node const loc = try c.ir.pushExpr(.field, c.alloc, field_s.type, debug_node, .{ .idx = @intCast(field_s.idx), .rec = rec.irIdx, + .parent_t = rec.type.id, }); const expr = Expr.initRequire(right, field_s.type); @@ -1090,9 +1092,19 @@ fn semaIndexExpr(c: *cy.Chunk, left: *ast.Node, left_res: ExprResult, expr: Expr const sym = try c.mustFindSym(recTypeSym, "$index", expr.node); const func_sym = try requireFuncSym(c, sym, expr.node); - return c.semaCallFuncSymRec(func_sym, + const res = try c.semaCallFuncSymRec(func_sym, left, left_res, array.args, expr.getRetCstr(), expr.node); + + if (!expr.use_addressable and c.sema.isPointerType(res.type.id) and expr.target_t != res.type.id) { + const child_t = c.sema.getPointerChildType(res.type.id); + const loc = try c.ir.pushExpr(.deref, c.alloc, child_t, expr.node, .{ + .expr = res.irIdx, + }); + return ExprResult.initStatic(loc, child_t); + } else { + return res; + } } fn semaAccessField(c: *cy.Chunk, rec: ExprResult, field: *ast.Node) !ExprResult { @@ -1124,6 +1136,7 @@ fn semaAccessEnumPayload(c: *cy.Chunk, rec: ExprResult, name: []const u8, node: const loc = try c.ir.pushExpr(.field, c.alloc, payload_t, node, .{ .idx = 1, .rec = rec.irIdx, + .parent_t = rec.type.id, }); return ExprResult.initCustom(loc, .field, CompactType.init(payload_t), undefined); } @@ -1168,6 +1181,7 @@ fn semaAccessFieldName(c: *cy.Chunk, rec: ExprResult, name: []const u8, field: * const loc = try c.ir.pushExpr(.field, c.alloc, field_s.type, field, .{ .idx = @intCast(field_s.idx), .rec = rec.irIdx, + .parent_t = child_t, }); return ExprResult.initCustom(loc, .field, CompactType.init(field_s.type), undefined); } @@ -1190,6 +1204,7 @@ fn semaField(c: *cy.Chunk, rec: ExprResult, idx: usize, type_id: cy.TypeId, node const loc = try c.ir.pushExpr(.field, c.alloc, type_id, node, .{ .idx = @intCast(idx), .rec = rec.irIdx, + .parent_t = rec.type.id, }); return ExprResult.initCustom(loc, .field, CompactType.init(type_id), undefined); } @@ -2078,6 +2093,7 @@ pub fn resolveTableFields(c: *cy.Chunk, obj: *cy.sym.ObjectType) !void { fields[0] = .{ .sym = @ptrCast(field_sym), .type = bt.Map, + .offset = 0, }; // Load custom fields. @@ -2087,6 +2103,7 @@ pub fn resolveTableFields(c: *cy.Chunk, obj: *cy.sym.ObjectType) !void { fields[i] = .{ .sym = @ptrCast(field_sym), .type = bt.Dyn, + .offset = 0, }; } obj.fields = fields.ptr; @@ -2112,9 +2129,10 @@ pub fn resolveStructTypeId(c: *cy.Chunk, struct_t: *cy.sym.ObjectType, opt_type: struct_t.type = typeid; c.compiler.sema.types.items[typeid] = .{ .sym = @ptrCast(struct_t), - .kind = .@"struct", - .data = .{ .@"struct" = .{ - .numFields = cy.NullU16, + .kind = .struct_t, + .data = .{ .struct_t = .{ + .nfields = cy.NullU16, + .cstruct = struct_t.cstruct, }}, .info = .{}, }; @@ -2171,6 +2189,7 @@ pub fn resolveObjectFields(c: *cy.Chunk, object_like: *cy.Sym, decl: *ast.Object } // Load fields. + var num_total_fields: u32 = 0; const fields = try c.alloc.alloc(cy.sym.FieldInfo, decl.fields.len); errdefer c.alloc.free(fields); @@ -2184,8 +2203,16 @@ pub fn resolveObjectFields(c: *cy.Chunk, object_like: *cy.Sym, decl: *ast.Object fields[i] = .{ .sym = @ptrCast(sym), .type = fieldType, + .offset = num_total_fields, }; has_boxed_fields = has_boxed_fields or !c.sema.isUnboxedType(fieldType); + + const field_te = c.sema.types.items[fieldType]; + if (field_te.kind == .struct_t) { + num_total_fields += field_te.data.struct_t.nfields; + } else { + num_total_fields += 1; + } } obj.fields = fields.ptr; obj.numFields = @intCast(fields.len); @@ -2205,9 +2232,11 @@ pub fn resolveObjectFields(c: *cy.Chunk, object_like: *cy.Sym, decl: *ast.Object }; }, .struct_t => { - c.sema.types.items[obj.type].data.@"struct" = .{ - .numFields = @intCast(obj.numFields), + c.sema.types.items[obj.type].data.struct_t = .{ + .nfields = @intCast(num_total_fields), + .cstruct = obj.cstruct, }; + obj.resolving_struct = false; }, else => return error.Unexpected, } @@ -2960,6 +2989,10 @@ pub const Expr = struct { prefer_addressable: bool = false, + /// By default some addressable expressions (eg. $index calls that return a pointer) + /// will deref unless `use_addressable` is true. + use_addressable: bool = false, + node: *ast.Node, target_t: TypeId, @@ -3207,6 +3240,7 @@ fn semaLocal(c: *cy.Chunk, id: LocalVarId, node: *ast.Node) !ExprResult { const loc = try c.ir.pushExpr(.field, c.alloc, svar.vtype.id, node, .{ .idx = svar.inner.objectMemberAlias.fieldIdx, .rec = rec_loc, + .parent_t = rec_t, }); return ExprResult.initCustom(loc, .field, svar.vtype, undefined); }, @@ -3218,6 +3252,7 @@ fn semaLocal(c: *cy.Chunk, id: LocalVarId, node: *ast.Node) !ExprResult { const loc = try c.ir.pushExpr(.field, c.alloc, svar.vtype.id, node, .{ .idx = svar.inner.parentObjectMemberAlias.fieldIdx, .rec = rec_loc, + .parent_t = rec_t, }); return ExprResult.init(loc, svar.vtype); }, @@ -4408,6 +4443,7 @@ fn semaSwitchChoicePrologue(c: *cy.Chunk, info: *SwitchInfo, expr: ExprResult, e const exprLoc = try c.ir.pushExpr(.field, c.alloc, bt.Integer, exprId, .{ .idx = 0, .rec = recLoc, + .parent_t = expr.type.id, }); return exprLoc; } @@ -4522,6 +4558,7 @@ fn semaSwitchCase(c: *cy.Chunk, info: SwitchInfo, case: *ast.CaseBlock) !u32 { const fieldLoc = try c.ir.pushExpr(.field, c.alloc, declT, case.capture.?, .{ .idx = 1, .rec = recLoc, + .parent_t = info.exprType.id, }); const declare = c.ir.getStmtDataPtr(declareLoc, .declareLocalInit); @@ -4738,7 +4775,7 @@ pub const ChunkExt = struct { pub fn semaObjectInit(c: *cy.Chunk, expr: Expr) !ExprResult { const node = expr.node.cast(.record_expr); - const left = try c.semaExprSkipSym(node.left); + const left = try c.semaExprSkipSym(node.left, false); if (left.resType != .sym) { const desc = try c.encoder.allocFmt(c.alloc, node.left); defer c.alloc.free(desc); @@ -4969,14 +5006,14 @@ pub const ChunkExt = struct { } /// Skips emitting IR for a sym. - pub fn semaExprSkipSym(c: *cy.Chunk, node: *ast.Node) !ExprResult { + pub fn semaExprSkipSym(c: *cy.Chunk, node: *ast.Node, use_addressable: bool) !ExprResult { switch (node.type()) { .ident => { return semaIdent(c, node, false, false); }, .array_expr => { const array_expr = node.cast(.array_expr); - var left = try semaExprSkipSym(c, array_expr.left); + var left = try semaExprSkipSym(c, array_expr.left, true); if (left.resType == .sym) { if (left.data.sym.type == .template) { const final_sym = try cte.expandTemplateOnCallArgs(c, left.data.sym.cast(.template), array_expr.args, node); @@ -4993,7 +5030,8 @@ pub const ChunkExt = struct { } left = try sema.symbol(c, left.data.sym, node, true); } - const expr = Expr.init(node); + var expr = Expr.init(node); + expr.use_addressable = use_addressable; return semaIndexExpr(c, array_expr.left, left, expr); }, .accessExpr => { @@ -5444,7 +5482,7 @@ pub const ChunkExt = struct { }, .array_init => { const array_init = node.cast(.array_init); - var left = try c.semaExprSkipSym(array_init.left); + var left = try c.semaExprSkipSym(array_init.left, false); if (left.resType == .sym) { if (left.data.sym.getStaticType()) |type_id| { if (left.data.sym.getVariant()) |variant| { @@ -5470,7 +5508,7 @@ pub const ChunkExt = struct { }, .array_expr => { const array_expr = node.cast(.array_expr); - var left = try c.semaExprSkipSym(array_expr.left); + var left = try c.semaExprSkipSym(array_expr.left, true); if (left.resType == .sym) { if (left.data.sym.type == .template) { const final_sym = try cte.expandTemplateOnCallArgs(c, left.data.sym.cast(.template), array_expr.args, node); @@ -5710,7 +5748,7 @@ pub const ChunkExt = struct { const coinit = node.cast(.coinit); const callExpr = coinit.child; - const callee = try c.semaExprSkipSym(callExpr.callee); + const callee = try c.semaExprSkipSym(callExpr.callee, false); // Callee is already pushed as a value or is a symbol. var call_res: ExprResult = undefined; @@ -5786,7 +5824,7 @@ pub const ChunkExt = struct { if (node.callee.type() == .accessExpr) { const callee = node.callee.cast(.accessExpr); - const leftRes = try c.semaExprSkipSym(callee.left); + const leftRes = try c.semaExprSkipSym(callee.left, true); if (callee.right.type() != .ident) { return error.Unexpected; } @@ -5865,7 +5903,7 @@ pub const ChunkExt = struct { } } else { // preCall. - const calleeRes = try c.semaExprSkipSym(node.callee); + const calleeRes = try c.semaExprSkipSym(node.callee, false); if (calleeRes.resType == .sym) { return callSym(c, calleeRes.data.sym, node.callee, node.args, expr.getRetCstr(), expr.node); } else if (calleeRes.resType == .func) { @@ -6168,8 +6206,8 @@ pub const ChunkExt = struct { if (left.type.id == right.type.id) { const left_te = c.sema.types.items[left.type.id]; - if (left_te.kind == .option or left_te.kind == .@"struct") { - return semaStructCompare(c, left, leftId, op, right, rightId, left_te, node); + if (left_te.kind == .option or left_te.kind == .struct_t) { + return semaStructCompare(c, left, leftId, op, right, rightId, left.type.id, node); } } @@ -6195,7 +6233,7 @@ pub const ChunkExt = struct { return error.Unexpected; } - const rec = try c.semaExprSkipSym(node.left); + const rec = try c.semaExprSkipSym(node.left, true); if (rec.resType == .sym) { const sym = rec.data.sym; const rightName = c.ast.nodeString(node.right); @@ -6294,9 +6332,10 @@ fn semaInitChoiceNoPayload(c: *cy.Chunk, member: *cy.sym.EnumMember, node: *ast. } fn semaStructCompare(c: *cy.Chunk, left: ExprResult, left_id: *ast.Node, op: cy.BinaryExprOp, - right: ExprResult, right_id: *ast.Node, left_te: cy.types.Type, node_id: *ast.Node) !ExprResult { + right: ExprResult, right_id: *ast.Node, left_t: cy.TypeId, node_id: *ast.Node) !ExprResult { // Struct memberwise comparison. + const left_te = c.sema.types.items[left_t]; const fields = left_te.sym.getFields().?; var field_t = c.sema.getTypeSym(fields[0].type); @@ -6307,10 +6346,12 @@ fn semaStructCompare(c: *cy.Chunk, left: ExprResult, left_id: *ast.Node, op: cy. const left_f = try c.ir.pushExpr(.field, c.alloc, fields[0].type, left_id, .{ .idx = 0, .rec = left.irIdx, + .parent_t = left_t, }); const right_f = try c.ir.pushExpr(.field, c.alloc, fields[0].type, right_id, .{ .idx = 0, .rec = right.irIdx, + .parent_t = left_t, }); it = try c.ir.pushExpr(.preBinOp, c.alloc, bt.Boolean, node_id, .{ .binOp = .{ .leftT = fields[0].type, @@ -6329,10 +6370,12 @@ fn semaStructCompare(c: *cy.Chunk, left: ExprResult, left_id: *ast.Node, op: cy. const left_f = try c.ir.pushExpr(.field, c.alloc, field.type, left_id, .{ .idx = @as(u8, @intCast(fidx)), .rec = left.irIdx, + .parent_t = left_t, }); const right_f = try c.ir.pushExpr(.field, c.alloc, field.type, right_id, .{ .idx = @as(u8, @intCast(fidx)), .rec = right.irIdx, + .parent_t = left_t, }); const compare = try c.ir.pushExpr(.preBinOp, c.alloc, bt.Boolean, node_id, .{ .binOp = .{ .leftT = field.type, diff --git a/src/sym.zig b/src/sym.zig index d72a8176b..c28dc1f27 100644 --- a/src/sym.zig +++ b/src/sym.zig @@ -405,8 +405,8 @@ pub const Sym = extern struct { pub fn getFields(self: *Sym) ?[]const FieldInfo { switch (self.type) { .enum_t => return &[_]FieldInfo{ - .{ .sym = undefined, .type = bt.Integer }, - .{ .sym = undefined, .type = bt.Any }, + .{ .sym = undefined, .type = bt.Integer, .offset = 0 }, + .{ .sym = undefined, .type = bt.Any, .offset = 0 }, }, .struct_t => return self.cast(.struct_t).getFields(), .object_t => return self.cast(.object_t).getFields(), @@ -792,6 +792,9 @@ pub const Field = extern struct { pub const FieldInfo = packed struct { sym: *Field, type: cy.TypeId, + + /// For struct/cstruct. Field offset from the start of the parent. + offset: u32, }; pub const Impl = struct { @@ -1403,9 +1406,10 @@ pub const ChunkExt = struct { @as(*cy.Module, @ptrCast(&sym.mod)).* = cy.Module.init(c); c.compiler.sema.types.items[typeId] = .{ .sym = @ptrCast(sym), - .kind = .@"struct", - .data = .{ .@"struct" = .{ - .numFields = cy.NullU16, + .kind = .struct_t, + .data = .{ .struct_t = .{ + .nfields = cy.NullU16, + .cstruct = cstruct, }}, .info = .{}, }; diff --git a/src/types.zig b/src/types.zig index 82d60d5e4..79591a2ef 100644 --- a/src/types.zig +++ b/src/types.zig @@ -22,7 +22,7 @@ pub const TypeKind = enum(u8) { custom, @"enum", choice, - @"struct", + struct_t, option, trait, bare, @@ -79,8 +79,11 @@ pub const Type = extern struct { getChildrenFn: C.GetChildrenFn, finalizerFn: C.FinalizerFn, }, - @"struct": extern struct { - numFields: u16, + struct_t: extern struct { + // Includes fields for nested structs. + nfields: u16, + + cstruct: bool, }, ct_ref: extern struct { ct_param_idx: u32, @@ -223,6 +226,11 @@ pub const SemaExt = struct { return variant.root_template == s.pointer_tmpl; } + pub fn getPointerChildType(s: *cy.Sema, id: cy.TypeId) cy.TypeId { + const sym = s.getTypeSym(id); + return sym.getVariant().?.args[0].castHeapObject(*cy.heap.Type).type; + } + pub fn getType(s: *cy.Sema, id: TypeId) Type { return s.types.items[id]; } @@ -289,7 +297,7 @@ pub const SemaExt = struct { if (id < BuiltinEnd) { return false; } - return s.types.items[id].kind == .@"struct"; + return s.types.items[id].kind == .struct_t; } pub fn isEnumType(s: *cy.Sema, typeId: TypeId) bool { diff --git a/src/vm.c b/src/vm.c index e428c8d01..51284a357 100644 --- a/src/vm.c +++ b/src/vm.c @@ -192,6 +192,66 @@ static inline ValueResult allocInt(VM* vm, i64 i) { return (ValueResult){ .val = VALUE_NOCYC_PTR(res.obj), .code = RES_CODE_SUCCESS }; } +static inline ValueResult allocStructSmall(VM* vm, TypeId typeId, Value* fields, u8* sizes, u8 numFields) { + HeapObjectResult res = zAllocPoolObject(vm); + if (UNLIKELY(res.code != RES_CODE_SUCCESS)) { + return (ValueResult){ .code = res.code }; + } + res.obj->object = (Object){ + .typeId = typeId | CYC_TYPE_MASK, + .rc = 1, + }; + + Value* dst = objectGetValuesPtr(&res.obj->object); + u8 offset = 0; + for (int i = 0; i < numFields; i += 1) { + u8 size = sizes[i]; + if (size > 0) { + // Copy. + HeapObject* field = VALUE_AS_HEAPOBJECT(fields[i]); + Value* src = objectGetValuesPtr((Object*)field); + memcpy(dst + offset, src, size * sizeof(Value)); + release(vm, fields[i]); + offset += size; + } else { + dst[offset] = fields[i]; + offset += 1; + } + } + + return (ValueResult){ .val = VALUE_CYC_PTR(res.obj), .code = RES_CODE_SUCCESS }; +} + +static inline ValueResult allocStruct(VM* vm, TypeId typeId, Value* fields, u8* sizes, u8 numFields) { + // First slot holds the typeId and rc. + HeapObjectResult res = zAllocExternalCycObject(vm, (1 + numFields) * sizeof(Value)); + if (UNLIKELY(res.code != RES_CODE_SUCCESS)) { + return (ValueResult){ .code = res.code }; + } + res.obj->object = (Object){ + .typeId = typeId | CYC_TYPE_MASK, + .rc = 1, + }; + + Value* dst = objectGetValuesPtr(&res.obj->object); + u8 offset = 0; + for (int i = 0; i < numFields; i += 1) { + u8 size = sizes[i]; + if (size > 0) { + // Copy. + HeapObject* field = VALUE_AS_HEAPOBJECT(fields[i]); + Value* src = objectGetValuesPtr((Object*)field); + memcpy(dst + offset, src, size * sizeof(Value)); + offset += size; + } else { + dst[i] = fields[i]; + offset += 1; + } + } + + return (ValueResult){ .val = VALUE_CYC_PTR(res.obj), .code = RES_CODE_SUCCESS }; +} + static inline ValueResult allocObjectSmall(VM* vm, TypeId typeId, Value* fields, u8 numFields) { HeapObjectResult res = zAllocPoolObject(vm); if (UNLIKELY(res.code != RES_CODE_SUCCESS)) { @@ -550,8 +610,8 @@ ResultCode execBytecode(VM* vm) { JENTRY(Call), JENTRY(TypeCheck), JENTRY(TypeCheckOption), + JENTRY(FieldStruct), JENTRY(Field), - JENTRY(AddrField), JENTRY(FieldDyn), JENTRY(FieldDynIC), JENTRY(Lambda), @@ -572,16 +632,20 @@ ResultCode execBytecode(VM* vm) { JENTRY(CompareNot), JENTRY(StringTemplate), JENTRY(NegFloat), + JENTRY(StructSmall), + JENTRY(Struct), JENTRY(ObjectSmall), JENTRY(Object), JENTRY(Trait), JENTRY(Box), JENTRY(Unbox), JENTRY(AddrLocal), + JENTRY(AddrConstIndex), JENTRY(AddrIndex), JENTRY(Deref), JENTRY(DerefStruct), JENTRY(SetDeref), + JENTRY(SetDerefStruct), JENTRY(UnwrapChoice), JENTRY(SetFieldDyn), JENTRY(SetFieldDynIC), @@ -1268,6 +1332,25 @@ ResultCode execBytecode(VM* vm) { pc += 2; NEXT(); } + CASE(FieldStruct): { + Value recv = stack[pc[1]]; + u16 type_id = READ_U16(2); + HeapObject* obj = VALUE_AS_HEAPOBJECT(recv); + Value* start = objectGetFieldPtr((Object*)obj, pc[3]); + u8 size = pc[4]; + ValueResult res; + if (size <= 4) { + res = allocObjectSmall(vm, type_id, start, size); + } else { + res = allocObject(vm, type_id, start, size); + } + if (res.code != RES_CODE_SUCCESS) { + RETURN(res.code); + } + stack[pc[5]] = res.val; + pc += 6; + NEXT(); + } CASE(Field): { Value recv = stack[pc[1]]; HeapObject* obj = VALUE_AS_HEAPOBJECT(recv); @@ -1279,13 +1362,6 @@ ResultCode execBytecode(VM* vm) { pc += 5; NEXT(); } - CASE(AddrField): { - Value* ptr = (Value*)stack[pc[1]]; - Value* field = objectGetFieldPtr((Object*)VALUE_AS_HEAPOBJECT(*ptr), pc[2]); - stack[pc[3]] = (u64)field; - pc += 4; - NEXT(); - } CASE(FieldDyn): { u8 left = pc[1]; u8 dst = pc[2]; @@ -1457,6 +1533,30 @@ ResultCode execBytecode(VM* vm) { CASE(NegFloat): { FLOAT_UNOP(stack[pc[2]] = VALUE_FLOAT(-VALUE_AS_FLOAT(val))) } + CASE(StructSmall): { + u16 typeId = READ_U16(1); + u8 startLocal = pc[3]; + u8 numFields = pc[4]; + ValueResult res = allocStructSmall(vm, typeId, stack + startLocal, pc + 6, numFields); + if (res.code != RES_CODE_SUCCESS) { + RETURN(res.code); + } + stack[pc[5]] = res.val; + pc += 6 + numFields; + NEXT(); + } + CASE(Struct): { + u16 typeId = READ_U16(1); + u8 startLocal = pc[3]; + u8 numFields = pc[4]; + ValueResult res = allocStruct(vm, typeId, stack + startLocal, pc + 6, numFields); + if (res.code != RES_CODE_SUCCESS) { + RETURN(res.code); + } + stack[pc[5]] = res.val; + pc += 6 + numFields; + NEXT(); + } CASE(ObjectSmall): { u16 typeId = READ_U16(1); u8 startLocal = pc[3]; @@ -1507,9 +1607,21 @@ ResultCode execBytecode(VM* vm) { NEXT(); } CASE(AddrLocal): { - Value* ptr = &stack[pc[1]]; - stack[pc[2]] = (u64)ptr; - pc += 3; + HeapObject* obj = VALUE_AS_HEAPOBJECT(stack[pc[1]]); + bool lifted_struct = pc[2]; + if (lifted_struct) { + HeapObject* inner = VALUE_AS_HEAPOBJECT(obj->object.firstValue); + stack[pc[3]] = (u64)(&inner->object.firstValue); + } else { + stack[pc[3]] = (u64)(&obj->object.firstValue); + } + pc += 4; + NEXT(); + } + CASE(AddrConstIndex): { + Value* ptr = (Value*)stack[pc[1]]; + stack[pc[3]] = (u64)(ptr + pc[2]); + pc += 4; NEXT(); } CASE(AddrIndex): { @@ -1531,22 +1643,35 @@ ResultCode execBytecode(VM* vm) { } CASE(DerefStruct): { Value* ptr = (Value*)stack[pc[1]]; - u8 numFields = pc[2]; - ValueResult res = zCopyObject(vm, VALUE_AS_HEAPOBJECT(*ptr), numFields); + u16 type_id = READ_U16(2); + u8 nfields = pc[4]; + + ValueResult res; + if (nfields <= 4) { + res = allocObjectSmall(vm, type_id, ptr, nfields); + } else { + res = allocObject(vm, type_id, ptr, nfields); + } if (res.code != RES_CODE_SUCCESS) { RETURN(res.code); } - stack[pc[3]] = res.val; - pc += 4; + stack[pc[5]] = res.val; + pc += 6; NEXT(); } CASE(SetDeref): { Value* dst = (Value*)stack[pc[1]]; - bool is_struct = stack[pc[2]]; - if (is_struct) { - release(vm, *dst); - } - *dst = stack[pc[3]]; + *dst = stack[pc[2]]; + pc += 3; + NEXT(); + } + CASE(SetDerefStruct): { + Value* dst = (Value*)stack[pc[1]]; + u8 nfields = pc[2]; + HeapObject* obj = VALUE_AS_HEAPOBJECT(stack[pc[3]]); + Value* src = objectGetValuesPtr((Object*)obj); + memcpy(dst, src, nfields * sizeof(Value)); + releaseObject(vm, obj); pc += 4; NEXT(); } diff --git a/src/vm.h b/src/vm.h index d704ea612..24d738959 100644 --- a/src/vm.h +++ b/src/vm.h @@ -268,8 +268,8 @@ typedef enum { CodeCall, CodeTypeCheck, CodeTypeCheckOption, + CodeFieldStruct, CodeField, - CodeAddrField, CodeFieldDyn, CodeFieldDynIC, CodeLambda, @@ -294,6 +294,8 @@ typedef enum { CodeCompareNot, CodeStringTemplate, CodeNegFloat, + CodeStructSmall, + CodeStruct, CodeObjectSmall, CodeObject, CodeTrait, @@ -301,10 +303,12 @@ typedef enum { CodeBox, CodeUnbox, CodeAddrLocal, + CodeAddrConstIndex, CodeAddrIndex, CodeDeref, CodeDerefStruct, CodeSetDeref, + CodeSetDerefStruct, CodeUnwrapChoice, /// Set field with runtime type check. diff --git a/src/vm.zig b/src/vm.zig index 4231e9d28..87bd17151 100644 --- a/src/vm.zig +++ b/src/vm.zig @@ -985,6 +985,7 @@ pub const VM = struct { infos[i] = .{ .sym = @ptrCast(field_sym), .type = bt.Any, + .offset = 0, }; } sym.fields = infos.ptr; @@ -2796,8 +2797,8 @@ pub fn copyObject(vm: *VM, obj: *HeapObject, numFields: u8) !cy.Value { for (0..numFields) |i| { const field_t = values[i].getTypeId(); const type_e = vm.c.types[field_t]; - if (type_e.kind == .@"struct") { - const child_num_fields = type_e.data.@"struct".numFields; + if (type_e.kind == .struct_t) { + const child_num_fields = type_e.data.struct_t.nfields; const child = try copyObject(vm, values[i].asHeapObject(), @intCast(child_num_fields)); dst[i] = child; } else { diff --git a/test/memory/default_memory.cy b/test/memory/default_memory.cy index a0a9edd69..cb852f440 100644 --- a/test/memory/default_memory.cy +++ b/test/memory/default_memory.cy @@ -12,13 +12,13 @@ intp.* = 123 test.eq(intp.*, 123) mem.free(intp) --- -- Memory.new cstruct --- var foop = mem.new(Foo) --- foop.a = 123 --- foop.b = 1.23 --- test.eq(foop.a, 123) --- test.eq(foop.b, 1.23) --- mem.free(foop) +-- Memory.new cstruct +var foop = mem.new(Foo) +foop.a = 123 +foop.b = 1.23 +test.eq(foop.a, 123) +test.eq(foop.b, 1.23) +mem.free(foop) -- Memory.alloc primitive var ints = mem.alloc(int, 2) @@ -29,15 +29,19 @@ test.eq(ints[0], 123) test.eq(ints[1], 234) mem.free(ints) --- -- Memory.alloc cstruct --- var foos = mem.alloc(Foo, 2) --- test.eq(foos.len(), 2) --- foos[0] = Foo{a=123, b=1.23} --- foos[1] = Foo{a=234, b=2.34} --- test.eq(foos[0].a, 123) --- test.eq(foos[0].b, 1.23) --- test.eq(foos[1].a, 234) --- test.eq(foos[1].b, 2.34) --- mem.free(foos) +-- Memory.alloc cstruct +var foos = mem.alloc(Foo, 2) +test.eq(foos.len(), 2) +foos[0] = Foo{a=123, b=1.23} +foos[1] = Foo{a=234, b=2.34} +test.eq(foos[0].a, 123) +test.eq(foos[0].b, 1.23) +test.eq(foos[1].a, 234) +test.eq(foos[1].b, 2.34) +foos[0].a = 234 +foos[0].b = 2.34 +test.eq(foos[0].a, 234) +test.eq(foos[0].b, 2.34) +mem.free(foos) --cytest: pass \ No newline at end of file