Skip to content

Commit

Permalink
Support variable scopes for sub-blocks. Optimize local allocation.
Browse files Browse the repository at this point in the history
  • Loading branch information
fubark committed Oct 5, 2023
1 parent bf73c50 commit f8892f1
Show file tree
Hide file tree
Showing 22 changed files with 870 additions and 902 deletions.
22 changes: 9 additions & 13 deletions docs/hugo/content/docs/toc/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,18 @@ var a = 123
a = 234
```

A new variable can be declared in function blocks with the same name as a variable from a parent block.
Blocks create a new variable scope. Variables declared in the current scope will take precedence over any parent variables with the same name.
```cy
var a = 123
foo = func():
-- A new local `a` inside function `foo`.
func foo():
-- `a` declared inside `foo`.
var a = 234
foo()
print a -- '123'
```

However, variables declared in sub-blocks such as `if` and `for` can not shadow variables in the same main/function block.
```cy
var a = 123
if true:
-- CompileError, `a` is already declared in the main block.
var a = 234
if true:
-- A new `a` declared inside `if`.
var a = 345
print a -- Prints "345"

print a -- Prints "234"
```

When a parent local is referenced in a [lambda function]({{<relref "/docs/toc/functions#lambdas">}}), the variable is automatically captured. Note that [static functions]({{<relref "/docs/toc/functions#static-functions">}}) can not capture parent locals.
Expand Down
33 changes: 14 additions & 19 deletions src/arc.zig
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,9 @@ pub fn runTempReleaseOps(vm: *cy.VM, fp: [*]const cy.Value, tempIdx: u32) void {
}
}

pub fn runBlockEndReleaseOps(vm: *cy.VM, stack: []const cy.Value, framePtr: usize, startPc: usize) void {
if (vm.ops[startPc].opcode() == .releaseN) {
const numLocals = vm.ops[startPc+1].val;
for (vm.ops[startPc+2..startPc+2+numLocals]) |local| {
release(vm, stack[framePtr + local.val]);
}
pub fn releaseLocals(vm: *cy.VM, stack: []const cy.Value, framePtr: usize, locals: cy.IndexSlice(u8)) void {
for (stack[framePtr+locals.start..framePtr+locals.end]) |val| {
release(vm, val);
}
}

Expand Down Expand Up @@ -146,11 +143,11 @@ pub inline fn retain(self: *cy.VM, val: cy.Value) linksection(cy.HotSection) voi
}
if (val.isPointer()) {
const obj = val.asHeapObject();
obj.head.rc += 1;
if (cy.Trace) {
checkRetainDanglingPointer(self, obj);
log.tracev("retain {} {}", .{obj.getUserTag(), obj.head.rc});
}
obj.head.rc += 1;
if (cy.TrackGlobalRC) {
self.refCounts += 1;
}
Expand All @@ -166,11 +163,11 @@ pub inline fn retainInc(self: *cy.VM, val: cy.Value, inc: u32) linksection(cy.Ho
}
if (val.isPointer()) {
const obj = val.asHeapObject();
obj.head.rc += inc;
if (cy.Trace) {
checkRetainDanglingPointer(self, obj);
log.tracev("retain {} {}", .{obj.getUserTag(), obj.head.rc});
}
obj.head.rc += inc;
if (cy.TrackGlobalRC) {
self.refCounts += inc;
}
Expand Down Expand Up @@ -293,10 +290,12 @@ fn performSweep(vm: *cy.VM) !GCResult {
vm.numFreed += @intCast(cycObjs.items.len);
}

return GCResult{
const res = GCResult{
.numCycFreed = @intCast(cycObjs.items.len),
.numObjFreed = if (cy.Trace) vm.numFreed else 0,
};
log.tracev("gc result: num cyc {}, num obj {}", .{res.numCycFreed, res.numObjFreed});
return res;
}

fn markMainStackRoots(vm: *cy.VM) !void {
Expand All @@ -311,8 +310,8 @@ fn markMainStackRoots(vm: *cy.VM) !void {
const symIdx = cy.debug.indexOfDebugSym(vm, pcOff) orelse return error.NoDebugSym;
const sym = cy.debug.getDebugSymByIndex(vm, symIdx);
const tempIdx = cy.debug.getDebugTempIndex(vm, symIdx);
const endLocalsPc = cy.debug.debugSymToEndLocalsPc(vm, sym);
log.debug("mark frame: {} {} {} {}", .{pcOff, vm.ops[pcOff].opcode(), tempIdx, endLocalsPc});
const locals = sym.getLocals();
log.tracev("mark frame: pc={} {} fp={} {}, locals={}..{}", .{pcOff, vm.ops[pcOff].opcode(), fpOff, tempIdx, locals.start, locals.end});

if (tempIdx != cy.NullId) {
const fp = vm.stack.ptr + fpOff;
Expand All @@ -328,14 +327,10 @@ fn markMainStackRoots(vm: *cy.VM) !void {
}
}

if (endLocalsPc != cy.NullId) {
if (vm.ops[endLocalsPc].opcode() == .releaseN) {
const numLocals = vm.ops[endLocalsPc+1].val;
for (vm.ops[endLocalsPc+2..endLocalsPc+2+numLocals]) |local| {
const v = vm.stack[fpOff + local.val];
if (v.isCycPointer()) {
markValue(vm, v);
}
if (locals.len() > 0) {
for (vm.stack[fpOff+locals.start..fpOff+locals.end]) |val| {
if (val.isCycPointer()) {
markValue(vm, val);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/builtins/bindings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2055,7 +2055,7 @@ pub const ModuleBuilder = struct {
) !void {
const funcSigId = try sema.ensureFuncSig(self.compiler, params, ret);
const funcSig = self.compiler.sema.getFuncSig(funcSigId);
if (funcSig.isParamsTyped) {
if (funcSig.reqCallTypeCheck) {
try self.vm.addMethod(typeId, mgId, rt.MethodInit.initHostTyped(funcSigId, ptr, @intCast(params.len)));
} else {
try self.vm.addMethod(typeId, mgId, rt.MethodInit.initHostUntyped(funcSigId, ptr, @intCast(params.len)));
Expand Down
36 changes: 13 additions & 23 deletions src/bytecode.zig
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,14 @@ pub const ByteCodeBuffer = struct {
});
}

pub fn pushFailableDebugSym(self: *ByteCodeBuffer, pc: usize, file: u32, loc: u32, frameLoc: u32, unwindTempIdx: u32) !void {
pub fn pushFailableDebugSym(self: *ByteCodeBuffer, pc: usize, file: u32, loc: u32, frameLoc: u32, unwindTempIdx: u32, localStart: u8, localEnd: u8) !void {
try self.debugTable.append(self.alloc, .{
.pc = @intCast(pc),
.loc = loc,
.file = @intCast(file),
.frameLoc = frameLoc,
.localStart = localStart,
.localEnd = localEnd,
});
try self.debugTempIndexTable.append(self.alloc, unwindTempIdx);
}
Expand Down Expand Up @@ -388,12 +390,6 @@ pub fn dumpInst(pcOffset: u32, code: OpCode, pc: [*]const Inst, len: usize, extr
const dst = pc[3].val;
fmt.printStderr("{} {} left={}, right={}, dst={}", &.{v(pcOffset), v(code), v(left), v(right), v(dst)});
},
.field => {
const recv = pc[1].val;
const dst = pc[2].val;
const symId = @as(*const align(1) u16, @ptrCast(pc + 3)).*;
fmt.printStderr("{} {} recv={}, dst={}, sym={}", &.{v(pcOffset), v(code), v(recv), v(dst), v(symId)});
},
.fieldRetain => {
const recv = pc[1].val;
const dst = pc[2].val;
Expand Down Expand Up @@ -492,11 +488,6 @@ pub fn dumpInst(pcOffset: u32, code: OpCode, pc: [*]const Inst, len: usize, extr
const right = pc[3].val;
fmt.printStderr("{} {} list={}, index={}, right={}", &.{v(pcOffset), v(code), v(list), v(index), v(right)});
},
.init => {
const start = pc[1].val;
const numLocals = pc[2].val;
fmt.printStderr("{} {} start={}, numLocals={}", &.{v(pcOffset), v(code), v(start), v(numLocals) });
},
.coinit => {
const startArgs = pc[1].val;
const numArgs = pc[2].val;
Expand Down Expand Up @@ -609,7 +600,15 @@ pub const DebugSym = extern struct {
frameLoc: u32,

/// CompileChunkId.
file: u32,
file: u16,

/// Which locals are alive before this instruction.
localStart: u8,
localEnd: u8,

pub fn getLocals(sym: cy.DebugSym) cy.IndexSlice(u8) {
return cy.IndexSlice(u8).init(sym.localStart, @intCast(sym.localEnd));
}
};

const DebugMarkerType = enum(u8) {
Expand Down Expand Up @@ -687,7 +686,6 @@ pub fn getInstLenAt(pc: [*]const Inst) u8 {
const numVars = pc[1].val;
return 2 + numVars;
},
.init,
.popTry,
.copy,
.copyRetainSrc,
Expand Down Expand Up @@ -763,8 +761,6 @@ pub fn getInstLenAt(pc: [*]const Inst) u8 {
},
.fieldRetain,
.fieldRetainIC,
.field,
.fieldIC,
.forRangeInit => {
return 8;
},
Expand Down Expand Up @@ -878,8 +874,6 @@ pub const OpCode = enum(u8) {
/// [calleeLocal] [numArgs] [numRet=0/1]
call = vmc.CodeCall,

field = vmc.CodeField,
fieldIC = vmc.CodeFieldIC,
fieldRetain = vmc.CodeFieldRetain,
fieldRetainIC = vmc.CodeFieldRetainIC,
lambda = vmc.CodeLambda,
Expand All @@ -905,10 +899,6 @@ pub const OpCode = enum(u8) {
stringTemplate = vmc.CodeStringTemplate,
negFloat = vmc.CodeNegFloat,

/// Initialize locals starting from `startLocal` to the `none` value.
/// init [startLocal] [numLocals]
init = vmc.CodeInit,

objectTypeCheck = vmc.CodeObjectTypeCheck,
objectSmall = vmc.CodeObjectSmall,
object = vmc.CodeObject,
Expand Down Expand Up @@ -999,7 +989,7 @@ pub const OpCode = enum(u8) {
};

test "bytecode internals." {
try t.eq(std.enums.values(OpCode).len, 109);
try t.eq(std.enums.values(OpCode).len, 106);
try t.eq(@sizeOf(Inst), 1);
try t.eq(@sizeOf(Const), 8);
try t.eq(@alignOf(Const), 8);
Expand Down
Loading

0 comments on commit f8892f1

Please sign in to comment.