Skip to content

Commit

Permalink
fix(jit): Try to detect not compilable nodes early
Browse files Browse the repository at this point in the history
  • Loading branch information
giann committed May 7, 2024
1 parent b675152 commit 0d68fd2
Show file tree
Hide file tree
Showing 2 changed files with 221 additions and 1 deletion.
159 changes: 159 additions & 0 deletions src/Ast.zig
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,165 @@ pub const Node = struct {
};
};

pub fn usesFiber(self: Self, node: Node.Index, seen: *std.AutoHashMap(Node.Index, void)) !bool {
if (seen.get(node) != null) {
return false;
}

try seen.put(node, {});

const components = self.nodes.items(.components)[node];
return switch (self.nodes.items(.tag)[node]) {
.As => try self.usesFiber(components.As.left, seen),
.AsyncCall,
.Resolve,
.Resume,
.Yield,
=> true,
.Binary => try self.usesFiber(components.Binary.left, seen) or try self.usesFiber(components.Binary.right, seen),
.Block => blk: {
for (components.Block) |stmt| {
if (try self.usesFiber(stmt, seen)) {
break :blk true;
}
}

break :blk false;
},
.BlockExpression => blk: {
for (components.BlockExpression) |stmt| {
if (try self.usesFiber(stmt, seen)) {
break :blk true;
}
}

break :blk false;
},
.String => blk: {
for (components.String) |stmt| {
if (try self.usesFiber(stmt, seen)) {
break :blk true;
}
}

break :blk false;
},
.Call => call: {
if (try self.usesFiber(components.Call.callee, seen) or (components.Call.catch_default != null and try self.usesFiber(components.Call.catch_default.?, seen))) {
break :call true;
}

for (components.Call.arguments) |argument| {
if (try self.usesFiber(argument.value, seen)) {
break :call true;
}
}

break :call false;
},
.Dot => dot: {
if (try self.usesFiber(components.Dot.callee, seen)) {
break :dot true;
}

switch (components.Dot.member_kind) {
.Value => {
if (try self.usesFiber(components.Dot.value_or_call_or_enum.Value, seen)) {
break :dot true;
}
},
.Call => {
if (try self.usesFiber(components.Dot.value_or_call_or_enum.Call, seen)) {
break :dot true;
}
},
else => {},
}

break :dot false;
},
.DoUntil => try self.usesFiber(components.DoUntil.condition, seen) or try self.usesFiber(components.DoUntil.body, seen),
.Expression => try self.usesFiber(components.Expression, seen),
.For => for_loop: {
if (try self.usesFiber(components.For.condition, seen) or try self.usesFiber(components.For.body, seen)) {
break :for_loop true;
}

for (components.For.init_declarations) |decl| {
if (try self.usesFiber(decl, seen)) {
break :for_loop true;
}
}

for (components.For.post_loop) |decl| {
if (try self.usesFiber(decl, seen)) {
break :for_loop true;
}
}

break :for_loop false;
},
.ForceUnwrap => try self.usesFiber(components.ForceUnwrap.unwrapped, seen),
.ForEach => try self.usesFiber(components.ForEach.iterable, seen) or try self.usesFiber(components.ForEach.key, seen) or try self.usesFiber(components.ForEach.value, seen) or try self.usesFiber(components.ForEach.body, seen),
.Function => components.Function.body != null and try self.usesFiber(components.Function.body.?, seen),
.Grouping => try self.usesFiber(components.Grouping, seen),
.If => try self.usesFiber(components.If.condition, seen) or try self.usesFiber(components.If.body, seen) or (components.If.else_branch != null and try self.usesFiber(components.If.else_branch.?, seen)),
.Is => try self.usesFiber(components.Is.left, seen),
.List => list: {
for (components.List.items) |item| {
if (try self.usesFiber(item, seen)) {
break :list true;
}
}

break :list false;
},
.Map => map: {
for (components.Map.entries) |entry| {
if (try self.usesFiber(entry.key, seen) or try self.usesFiber(entry.value, seen)) {
break :map true;
}
}

break :map false;
},
.NamedVariable => components.NamedVariable.value != null and try self.usesFiber(components.NamedVariable.value.?, seen),
.ObjectInit => obj_init: {
for (components.ObjectInit.properties) |property| {
if (try self.usesFiber(property.value, seen)) {
break :obj_init true;
}
}

break :obj_init false;
},
.Out => try self.usesFiber(components.Out, seen),
.Range => try self.usesFiber(components.Range.low, seen) or try self.usesFiber(components.Range.high, seen),
.Return => components.Return.value != null and try self.usesFiber(components.Return.value.?, seen),
.Subscript => try self.usesFiber(components.Subscript.subscripted, seen) or try self.usesFiber(components.Subscript.index, seen) or (components.Subscript.value != null and try self.usesFiber(components.Subscript.value.?, seen)),
.Throw => try self.usesFiber(components.Throw.expression, seen),
.Try => @"try": {
if (try self.usesFiber(components.Try.body, seen) or (components.Try.unconditional_clause != null and try self.usesFiber(components.Try.unconditional_clause.?, seen))) {
break :@"try" true;
}

for (components.Try.clauses) |clause| {
if (try self.usesFiber(clause.body, seen)) {
break :@"try" true;
}
}

break :@"try" false;
},
.TypeOfExpression => try self.usesFiber(components.TypeOfExpression, seen),
.Unary => try self.usesFiber(components.Unary.expression, seen),
.Unwrap => try self.usesFiber(components.Unwrap.unwrapped, seen),
.VarDeclaration => components.VarDeclaration.value != null and try self.usesFiber(components.VarDeclaration.value.?, seen),
.While => try self.usesFiber(components.While.condition, seen) or try self.usesFiber(components.While.body, seen),
else => false,
};
}

pub const AnonymousObjectType = struct {
fields: []Field,

Expand Down
63 changes: 62 additions & 1 deletion src/Jit.zig
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,28 @@ pub fn compileFunction(self: *Self, ast: Ast, closure: *o.ObjClosure) Error!void
const function = closure.function;
const ast_node = function.node;

var seen = std.AutoHashMap(Ast.Node.Index, void).init(self.vm.gc.allocator);
defer seen.deinit();
if (try ast.usesFiber(
ast_node,
&seen,
)) {
if (BuildOptions.jit_debug) {
std.debug.print(
"Not compiling node {s}#{}, likely because it uses a fiber\n",
.{
@tagName(ast.nodes.items(.tag)[ast_node]),
ast_node,
},
);
}
_ = self.functions_queue.remove(ast_node);
_ = self.objclosures_queue.remove(closure);
try self.blacklisted_closures.put(closure, {});

return error.CantCompile;
}

// Remember we need to set this functions fields
try self.objclosures_queue.put(closure, {});

Expand Down Expand Up @@ -197,6 +219,27 @@ pub fn compileFunction(self: *Self, ast: Ast, closure: *o.ObjClosure) Error!void
}

pub fn compileHotSpot(self: *Self, ast: Ast, hotspot_node: Ast.Node.Index) Error!*anyopaque {
var seen = std.AutoHashMap(Ast.Node.Index, void).init(self.vm.gc.allocator);
defer seen.deinit();
if (try ast.usesFiber(
hotspot_node,
&seen,
)) {
if (BuildOptions.jit_debug) {
std.debug.print(
"Not compiling node {s}#{}, likely because it uses a fiber\n",
.{
@tagName(ast.nodes.items(.tag)[hotspot_node]),
hotspot_node,
},
);
}

try self.blacklisted_hotspots.put(hotspot_node, {});

return error.CantCompile;
}

// Build function surrounding the node
try self.buildFunction(ast, null, hotspot_node);

Expand Down Expand Up @@ -4292,7 +4335,25 @@ fn generateHotspotFunction(self: *Self, node: Ast.Node.Index) Error!?m.MIR_op_t
),
);

_ = try self.generateNode(node);
_ = self.generateNode(node) catch |err| {
if (err == error.CantCompile) {
if (BuildOptions.jit_debug) {
std.debug.print(
"Not compiling node {s}#{}, likely because it uses a fiber\n",
.{
@tagName(self.state.?.ast.nodes.items(.tag)[self.state.?.ast_node]),
self.state.?.ast_node,
},
);
}

m.MIR_finish_func(self.ctx);

try self.blacklisted_hotspots.put(self.state.?.ast_node, {});
}

return err;
};

// If we reach here, return 0 meaning there was no early return in the hotspot
self.RET(m.MIR_new_int_op(self.ctx, 0));
Expand Down

0 comments on commit 0d68fd2

Please sign in to comment.