self-hosted: basic IR pass2
This commit is contained in:
parent
c87102c304
commit
e78b1b810f
@ -14,6 +14,13 @@ pub const Color = enum {
|
||||
pub const Span = struct {
|
||||
first: ast.TokenIndex,
|
||||
last: ast.TokenIndex,
|
||||
|
||||
pub fn token(i: TokenIndex) Span {
|
||||
return Span {
|
||||
.first = i,
|
||||
.last = i,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Msg = struct {
|
||||
|
@ -9,31 +9,34 @@ const Type = Value.Type;
|
||||
const assert = std.debug.assert;
|
||||
const Token = std.zig.Token;
|
||||
const ParsedFile = @import("parsed_file.zig").ParsedFile;
|
||||
const Span = @import("errmsg.zig").Span;
|
||||
|
||||
pub const LVal = enum {
|
||||
None,
|
||||
Ptr,
|
||||
};
|
||||
|
||||
pub const Mut = enum {
|
||||
Mut,
|
||||
Const,
|
||||
};
|
||||
|
||||
pub const Volatility = enum {
|
||||
NonVolatile,
|
||||
Volatile,
|
||||
};
|
||||
|
||||
pub const IrVal = union(enum) {
|
||||
Unknown,
|
||||
Known: *Value,
|
||||
KnownType: *Type,
|
||||
KnownValue: *Value,
|
||||
|
||||
const Init = enum {
|
||||
Unknown,
|
||||
NoReturn,
|
||||
Void,
|
||||
};
|
||||
|
||||
pub fn dump(self: IrVal) void {
|
||||
switch (self) {
|
||||
IrVal.Unknown => std.debug.warn("Unknown"),
|
||||
IrVal.Known => |value| {
|
||||
std.debug.warn("Known(");
|
||||
IrVal.Unknown => typeof.dump(),
|
||||
IrVal.KnownType => |typeof| {
|
||||
std.debug.warn("KnownType(");
|
||||
typeof.dump();
|
||||
std.debug.warn(")");
|
||||
},
|
||||
IrVal.KnownValue => |value| {
|
||||
std.debug.warn("KnownValue(");
|
||||
value.dump();
|
||||
std.debug.warn(")");
|
||||
},
|
||||
@ -46,10 +49,18 @@ pub const Instruction = struct {
|
||||
scope: *Scope,
|
||||
debug_id: usize,
|
||||
val: IrVal,
|
||||
ref_count: usize,
|
||||
span: Span,
|
||||
|
||||
/// true if this instruction was generated by zig and not from user code
|
||||
is_generated: bool,
|
||||
|
||||
/// the instruction that is derived from this one in analysis
|
||||
child: ?*Instruction,
|
||||
|
||||
/// the instruction that this one derives from in analysis
|
||||
parent: ?*Instruction,
|
||||
|
||||
pub fn cast(base: *Instruction, comptime T: type) ?*T {
|
||||
if (base.id == comptime typeToId(T)) {
|
||||
return @fieldParentPtr(T, "base", base);
|
||||
@ -81,6 +92,47 @@ pub const Instruction = struct {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn hasSideEffects(base: *const Instruction) bool {
|
||||
comptime var i = 0;
|
||||
inline while (i < @memberCount(Id)) : (i += 1) {
|
||||
if (base.id == @field(Id, @memberName(Id, i))) {
|
||||
const T = @field(Instruction, @memberName(Id, i));
|
||||
return @fieldParentPtr(T, "base", base).hasSideEffects();
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn analyze(base: *Instruction, ira: *Analyze) Analyze.Error!*Instruction {
|
||||
comptime var i = 0;
|
||||
inline while (i < @memberCount(Id)) : (i += 1) {
|
||||
if (base.id == @field(Id, @memberName(Id, i))) {
|
||||
const T = @field(Instruction, @memberName(Id, i));
|
||||
const new_inst = try @fieldParentPtr(T, "base", base).analyze(ira);
|
||||
new_inst.linkToParent(base);
|
||||
return new_inst;
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
fn getAsParam(param: *Instruction) !*Instruction {
|
||||
const child = param.child orelse return error.SemanticAnalysisFailed;
|
||||
switch (child.val) {
|
||||
IrVal.Unknown => return error.SemanticAnalysisFailed,
|
||||
else => return child,
|
||||
}
|
||||
}
|
||||
|
||||
/// asserts that the type is known
|
||||
fn getKnownType(self: *Instruction) *Type {
|
||||
switch (self.val) {
|
||||
IrVal.KnownType => |typeof| return typeof,
|
||||
IrVal.KnownValue => |value| return value.typeof,
|
||||
IrVal.Unknown => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setGenerated(base: *Instruction) void {
|
||||
base.is_generated = true;
|
||||
}
|
||||
@ -88,10 +140,18 @@ pub const Instruction = struct {
|
||||
pub fn isNoReturn(base: *const Instruction) bool {
|
||||
switch (base.val) {
|
||||
IrVal.Unknown => return false,
|
||||
IrVal.Known => |x| return x.typeof.id == Type.Id.NoReturn,
|
||||
IrVal.KnownValue => |x| return x.typeof.id == Type.Id.NoReturn,
|
||||
IrVal.KnownType => |typeof| return typeof.id == Type.Id.NoReturn,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn linkToParent(self: *Instruction, parent: *Instruction) void {
|
||||
assert(self.parent == null);
|
||||
assert(parent.child == null);
|
||||
self.parent = parent;
|
||||
parent.child = self;
|
||||
}
|
||||
|
||||
pub const Id = enum {
|
||||
Return,
|
||||
Const,
|
||||
@ -100,196 +160,231 @@ pub const Instruction = struct {
|
||||
CheckVoidStmt,
|
||||
Phi,
|
||||
Br,
|
||||
AddImplicitReturnType,
|
||||
};
|
||||
|
||||
pub const Const = struct {
|
||||
base: Instruction,
|
||||
params: Params,
|
||||
|
||||
pub fn buildBool(irb: *Builder, scope: *Scope, val: bool) !*Instruction {
|
||||
const inst = try irb.arena().create(Const{
|
||||
.base = Instruction{
|
||||
.id = Instruction.Id.Const,
|
||||
.is_generated = false,
|
||||
.scope = scope,
|
||||
.debug_id = irb.next_debug_id,
|
||||
.val = IrVal{ .Known = &Value.Bool.get(irb.module, val).base },
|
||||
},
|
||||
});
|
||||
irb.next_debug_id += 1;
|
||||
try irb.current_basic_block.instruction_list.append(&inst.base);
|
||||
return &inst.base;
|
||||
const Params = struct {};
|
||||
|
||||
// Use Builder.buildConst* methods, or, after building a Const instruction,
|
||||
// manually set the ir_val field.
|
||||
const ir_val_init = IrVal.Init.Unknown;
|
||||
|
||||
pub fn dump(self: *const Const) void {
|
||||
self.base.val.KnownValue.dump();
|
||||
}
|
||||
|
||||
pub fn buildVoid(irb: *Builder, scope: *Scope, is_generated: bool) !*Instruction {
|
||||
const inst = try irb.arena().create(Const{
|
||||
.base = Instruction{
|
||||
.id = Instruction.Id.Const,
|
||||
.is_generated = is_generated,
|
||||
.scope = scope,
|
||||
.debug_id = irb.next_debug_id,
|
||||
.val = IrVal{ .Known = &Value.Void.get(irb.module).base },
|
||||
},
|
||||
});
|
||||
irb.next_debug_id += 1;
|
||||
try irb.current_basic_block.instruction_list.append(&inst.base);
|
||||
return &inst.base;
|
||||
pub fn hasSideEffects(self: *const Const) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn dump(inst: *const Const) void {
|
||||
inst.base.val.Known.dump();
|
||||
pub fn analyze(self: *const Const, ira: *Analyze) !*Instruction {
|
||||
const new_inst = try ira.irb.build(Const, self.base.scope, self.base.span, Params{});
|
||||
new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() };
|
||||
return new_inst;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Return = struct {
|
||||
base: Instruction,
|
||||
return_value: *Instruction,
|
||||
params: Params,
|
||||
|
||||
pub fn build(irb: *Builder, scope: *Scope, return_value: *Instruction) !*Instruction {
|
||||
const inst = try irb.arena().create(Return{
|
||||
.base = Instruction{
|
||||
.id = Instruction.Id.Return,
|
||||
.is_generated = false,
|
||||
.scope = scope,
|
||||
.debug_id = irb.next_debug_id,
|
||||
.val = IrVal{ .Known = &Value.Void.get(irb.module).base },
|
||||
},
|
||||
.return_value = return_value,
|
||||
});
|
||||
irb.next_debug_id += 1;
|
||||
try irb.current_basic_block.instruction_list.append(&inst.base);
|
||||
return &inst.base;
|
||||
const Params = struct {
|
||||
return_value: *Instruction,
|
||||
};
|
||||
|
||||
const ir_val_init = IrVal.Init.NoReturn;
|
||||
|
||||
pub fn dump(self: *const Return) void {
|
||||
std.debug.warn("#{}", self.params.return_value.debug_id);
|
||||
}
|
||||
|
||||
pub fn dump(inst: *const Return) void {
|
||||
std.debug.warn("#{}", inst.return_value.debug_id);
|
||||
pub fn hasSideEffects(self: *const Return) bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn analyze(self: *const Return, ira: *Analyze) !*Instruction {
|
||||
const value = try self.params.return_value.getAsParam();
|
||||
const casted_value = try ira.implicitCast(value, ira.explicit_return_type);
|
||||
|
||||
// TODO detect returning local variable address
|
||||
|
||||
return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value });
|
||||
}
|
||||
};
|
||||
|
||||
pub const Ref = struct {
|
||||
base: Instruction,
|
||||
target: *Instruction,
|
||||
mut: Mut,
|
||||
volatility: Volatility,
|
||||
params: Params,
|
||||
|
||||
pub fn build(
|
||||
irb: *Builder,
|
||||
scope: *Scope,
|
||||
const Params = struct {
|
||||
target: *Instruction,
|
||||
mut: Mut,
|
||||
volatility: Volatility,
|
||||
) !*Instruction {
|
||||
const inst = try irb.arena().create(Ref{
|
||||
.base = Instruction{
|
||||
.id = Instruction.Id.Ref,
|
||||
.is_generated = false,
|
||||
.scope = scope,
|
||||
.debug_id = irb.next_debug_id,
|
||||
.val = IrVal.Unknown,
|
||||
},
|
||||
.target = target,
|
||||
.mut = mut,
|
||||
.volatility = volatility,
|
||||
});
|
||||
irb.next_debug_id += 1;
|
||||
try irb.current_basic_block.instruction_list.append(&inst.base);
|
||||
return &inst.base;
|
||||
}
|
||||
mut: Type.Pointer.Mut,
|
||||
volatility: Type.Pointer.Vol,
|
||||
};
|
||||
|
||||
const ir_val_init = IrVal.Init.Unknown;
|
||||
|
||||
pub fn dump(inst: *const Ref) void {}
|
||||
|
||||
pub fn hasSideEffects(inst: *const Ref) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn analyze(self: *const Ref, ira: *Analyze) !*Instruction {
|
||||
const target = try self.params.target.getAsParam();
|
||||
|
||||
if (ira.getCompTimeValOrNullUndefOk(target)) |val| {
|
||||
return ira.getCompTimeRef(
|
||||
val,
|
||||
Value.Ptr.Mut.CompTimeConst,
|
||||
self.params.mut,
|
||||
self.params.volatility,
|
||||
val.typeof.getAbiAlignment(ira.irb.module),
|
||||
);
|
||||
}
|
||||
|
||||
const new_inst = try ira.irb.build(Ref, self.base.scope, self.base.span, Params{
|
||||
.target = target,
|
||||
.mut = self.params.mut,
|
||||
.volatility = self.params.volatility,
|
||||
});
|
||||
const elem_type = target.getKnownType();
|
||||
const ptr_type = Type.Pointer.get(
|
||||
ira.irb.module,
|
||||
elem_type,
|
||||
self.params.mut,
|
||||
self.params.volatility,
|
||||
Type.Pointer.Size.One,
|
||||
elem_type.getAbiAlignment(ira.irb.module),
|
||||
);
|
||||
// TODO: potentially set the hint that this is a stack pointer. But it might not be - this
|
||||
// could be a ref of a global, for example
|
||||
new_inst.val = IrVal{ .KnownType = &ptr_type.base };
|
||||
// TODO potentially add an alloca entry here
|
||||
return new_inst;
|
||||
}
|
||||
};
|
||||
|
||||
pub const DeclVar = struct {
|
||||
base: Instruction,
|
||||
params: Params,
|
||||
|
||||
const Params = struct {
|
||||
variable: *Variable,
|
||||
};
|
||||
|
||||
const ir_val_init = IrVal.Init.Unknown;
|
||||
|
||||
pub fn dump(inst: *const DeclVar) void {}
|
||||
|
||||
pub fn hasSideEffects(inst: *const DeclVar) bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn analyze(self: *const DeclVar, ira: *Analyze) !*Instruction {
|
||||
return error.Unimplemented; // TODO
|
||||
}
|
||||
};
|
||||
|
||||
pub const CheckVoidStmt = struct {
|
||||
base: Instruction,
|
||||
target: *Instruction,
|
||||
params: Params,
|
||||
|
||||
pub fn build(
|
||||
irb: *Builder,
|
||||
scope: *Scope,
|
||||
const Params = struct {
|
||||
target: *Instruction,
|
||||
) !*Instruction {
|
||||
const inst = try irb.arena().create(CheckVoidStmt{
|
||||
.base = Instruction{
|
||||
.id = Instruction.Id.CheckVoidStmt,
|
||||
.is_generated = true,
|
||||
.scope = scope,
|
||||
.debug_id = irb.next_debug_id,
|
||||
.val = IrVal{ .Known = &Value.Void.get(irb.module).base },
|
||||
},
|
||||
.target = target,
|
||||
});
|
||||
irb.next_debug_id += 1;
|
||||
try irb.current_basic_block.instruction_list.append(&inst.base);
|
||||
return &inst.base;
|
||||
}
|
||||
};
|
||||
|
||||
const ir_val_init = IrVal.Init.Unknown;
|
||||
|
||||
pub fn dump(inst: *const CheckVoidStmt) void {}
|
||||
|
||||
pub fn hasSideEffects(inst: *const CheckVoidStmt) bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Instruction {
|
||||
return error.Unimplemented; // TODO
|
||||
}
|
||||
};
|
||||
|
||||
pub const Phi = struct {
|
||||
base: Instruction,
|
||||
incoming_blocks: []*BasicBlock,
|
||||
incoming_values: []*Instruction,
|
||||
params: Params,
|
||||
|
||||
pub fn build(
|
||||
irb: *Builder,
|
||||
scope: *Scope,
|
||||
const Params = struct {
|
||||
incoming_blocks: []*BasicBlock,
|
||||
incoming_values: []*Instruction,
|
||||
) !*Instruction {
|
||||
const inst = try irb.arena().create(Phi{
|
||||
.base = Instruction{
|
||||
.id = Instruction.Id.Phi,
|
||||
.is_generated = false,
|
||||
.scope = scope,
|
||||
.debug_id = irb.next_debug_id,
|
||||
.val = IrVal.Unknown,
|
||||
},
|
||||
.incoming_blocks = incoming_blocks,
|
||||
.incoming_values = incoming_values,
|
||||
});
|
||||
irb.next_debug_id += 1;
|
||||
try irb.current_basic_block.instruction_list.append(&inst.base);
|
||||
return &inst.base;
|
||||
}
|
||||
};
|
||||
|
||||
const ir_val_init = IrVal.Init.Unknown;
|
||||
|
||||
pub fn dump(inst: *const Phi) void {}
|
||||
|
||||
pub fn hasSideEffects(inst: *const Phi) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn analyze(self: *const Phi, ira: *Analyze) !*Instruction {
|
||||
return error.Unimplemented; // TODO
|
||||
}
|
||||
};
|
||||
|
||||
pub const Br = struct {
|
||||
base: Instruction,
|
||||
dest_block: *BasicBlock,
|
||||
is_comptime: *Instruction,
|
||||
params: Params,
|
||||
|
||||
pub fn build(
|
||||
irb: *Builder,
|
||||
scope: *Scope,
|
||||
const Params = struct {
|
||||
dest_block: *BasicBlock,
|
||||
is_comptime: *Instruction,
|
||||
) !*Instruction {
|
||||
const inst = try irb.arena().create(Br{
|
||||
.base = Instruction{
|
||||
.id = Instruction.Id.Br,
|
||||
.is_generated = false,
|
||||
.scope = scope,
|
||||
.debug_id = irb.next_debug_id,
|
||||
.val = IrVal{ .Known = &Value.NoReturn.get(irb.module).base },
|
||||
},
|
||||
.dest_block = dest_block,
|
||||
.is_comptime = is_comptime,
|
||||
});
|
||||
irb.next_debug_id += 1;
|
||||
try irb.current_basic_block.instruction_list.append(&inst.base);
|
||||
return &inst.base;
|
||||
}
|
||||
};
|
||||
|
||||
const ir_val_init = IrVal.Init.NoReturn;
|
||||
|
||||
pub fn dump(inst: *const Br) void {}
|
||||
|
||||
pub fn hasSideEffects(inst: *const Br) bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn analyze(self: *const Br, ira: *Analyze) !*Instruction {
|
||||
return error.Unimplemented; // TODO
|
||||
}
|
||||
};
|
||||
|
||||
pub const AddImplicitReturnType = struct {
|
||||
base: Instruction,
|
||||
params: Params,
|
||||
|
||||
pub const Params = struct {
|
||||
target: *Instruction,
|
||||
};
|
||||
|
||||
const ir_val_init = IrVal.Init.Unknown;
|
||||
|
||||
pub fn dump(inst: *const AddImplicitReturnType) void {
|
||||
std.debug.warn("#{}", inst.params.target.debug_id);
|
||||
}
|
||||
|
||||
pub fn hasSideEffects(inst: *const AddImplicitReturnType) bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Instruction {
|
||||
const target = try self.params.target.getAsParam();
|
||||
|
||||
try ira.src_implicit_return_type_list.append(target);
|
||||
|
||||
return ira.irb.build(
|
||||
AddImplicitReturnType,
|
||||
self.base.scope,
|
||||
self.base.span,
|
||||
Params{ .target = target },
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@ -303,16 +398,31 @@ pub const BasicBlock = struct {
|
||||
debug_id: usize,
|
||||
scope: *Scope,
|
||||
instruction_list: std.ArrayList(*Instruction),
|
||||
ref_instruction: ?*Instruction,
|
||||
|
||||
/// the basic block that is derived from this one in analysis
|
||||
child: ?*BasicBlock,
|
||||
|
||||
/// the basic block that this one derives from in analysis
|
||||
parent: ?*BasicBlock,
|
||||
|
||||
pub fn ref(self: *BasicBlock) void {
|
||||
self.ref_count += 1;
|
||||
}
|
||||
|
||||
pub fn linkToParent(self: *BasicBlock, parent: *BasicBlock) void {
|
||||
assert(self.parent == null);
|
||||
assert(parent.child == null);
|
||||
self.parent = parent;
|
||||
parent.child = self;
|
||||
}
|
||||
};
|
||||
|
||||
/// Stuff that survives longer than Builder
|
||||
pub const Code = struct {
|
||||
basic_block_list: std.ArrayList(*BasicBlock),
|
||||
arena: std.heap.ArenaAllocator,
|
||||
return_type: ?*Type,
|
||||
|
||||
/// allocator is module.a()
|
||||
pub fn destroy(self: *Code, allocator: *Allocator) void {
|
||||
@ -341,15 +451,13 @@ pub const Builder = struct {
|
||||
parsed_file: *ParsedFile,
|
||||
is_comptime: bool,
|
||||
|
||||
pub const Error = error{
|
||||
OutOfMemory,
|
||||
Unimplemented,
|
||||
};
|
||||
pub const Error = Analyze.Error;
|
||||
|
||||
pub fn init(module: *Module, parsed_file: *ParsedFile) !Builder {
|
||||
const code = try module.a().create(Code{
|
||||
.basic_block_list = undefined,
|
||||
.arena = std.heap.ArenaAllocator.init(module.a()),
|
||||
.return_type = null,
|
||||
});
|
||||
code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
|
||||
errdefer code.destroy(module.a());
|
||||
@ -381,6 +489,9 @@ pub const Builder = struct {
|
||||
.debug_id = self.next_debug_id,
|
||||
.scope = scope,
|
||||
.instruction_list = std.ArrayList(*Instruction).init(self.arena()),
|
||||
.child = null,
|
||||
.parent = null,
|
||||
.ref_instruction = null,
|
||||
});
|
||||
self.next_debug_id += 1;
|
||||
return basic_block;
|
||||
@ -490,14 +601,18 @@ pub const Builder = struct {
|
||||
|
||||
if (block.statements.len == 0) {
|
||||
// {}
|
||||
return Instruction.Const.buildVoid(irb, child_scope, false);
|
||||
return irb.buildConstVoid(child_scope, Span.token(block.lbrace), false);
|
||||
}
|
||||
|
||||
if (block.label) |label| {
|
||||
block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena());
|
||||
block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena());
|
||||
block_scope.end_block = try irb.createBasicBlock(parent_scope, "BlockEnd");
|
||||
block_scope.is_comptime = try Instruction.Const.buildBool(irb, parent_scope, irb.isCompTime(parent_scope));
|
||||
block_scope.is_comptime = try irb.buildConstBool(
|
||||
parent_scope,
|
||||
Span.token(block.lbrace),
|
||||
irb.isCompTime(parent_scope),
|
||||
);
|
||||
}
|
||||
|
||||
var is_continuation_unreachable = false;
|
||||
@ -530,10 +645,15 @@ pub const Builder = struct {
|
||||
|
||||
if (statement_value.cast(Instruction.DeclVar)) |decl_var| {
|
||||
// variable declarations start a new scope
|
||||
child_scope = decl_var.variable.child_scope;
|
||||
child_scope = decl_var.params.variable.child_scope;
|
||||
} else if (!is_continuation_unreachable) {
|
||||
// this statement's value must be void
|
||||
_ = Instruction.CheckVoidStmt.build(irb, child_scope, statement_value);
|
||||
_ = irb.build(
|
||||
Instruction.CheckVoidStmt,
|
||||
child_scope,
|
||||
statement_value.span,
|
||||
Instruction.CheckVoidStmt.Params{ .target = statement_value },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -544,37 +664,34 @@ pub const Builder = struct {
|
||||
}
|
||||
|
||||
try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
|
||||
return Instruction.Phi.build(
|
||||
irb,
|
||||
parent_scope,
|
||||
block_scope.incoming_blocks.toOwnedSlice(),
|
||||
block_scope.incoming_values.toOwnedSlice(),
|
||||
);
|
||||
return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{
|
||||
.incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
|
||||
.incoming_values = block_scope.incoming_values.toOwnedSlice(),
|
||||
});
|
||||
}
|
||||
|
||||
if (block.label) |label| {
|
||||
try block_scope.incoming_blocks.append(irb.current_basic_block);
|
||||
try block_scope.incoming_values.append(
|
||||
try Instruction.Const.buildVoid(irb, parent_scope, true),
|
||||
try irb.buildConstVoid(parent_scope, Span.token(block.rbrace), true),
|
||||
);
|
||||
_ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit);
|
||||
(try Instruction.Br.build(
|
||||
irb,
|
||||
parent_scope,
|
||||
block_scope.end_block,
|
||||
block_scope.is_comptime,
|
||||
)).setGenerated();
|
||||
|
||||
_ = try irb.buildGen(Instruction.Br, parent_scope, Span.token(block.rbrace), Instruction.Br.Params{
|
||||
.dest_block = block_scope.end_block,
|
||||
.is_comptime = block_scope.is_comptime,
|
||||
});
|
||||
|
||||
try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
|
||||
return Instruction.Phi.build(
|
||||
irb,
|
||||
parent_scope,
|
||||
block_scope.incoming_blocks.toOwnedSlice(),
|
||||
block_scope.incoming_values.toOwnedSlice(),
|
||||
);
|
||||
|
||||
return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{
|
||||
.incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
|
||||
.incoming_values = block_scope.incoming_values.toOwnedSlice(),
|
||||
});
|
||||
}
|
||||
|
||||
_ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit);
|
||||
return try Instruction.Const.buildVoid(irb, child_scope, true);
|
||||
return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true);
|
||||
}
|
||||
|
||||
fn genDefersForBlock(
|
||||
@ -603,7 +720,12 @@ pub const Builder = struct {
|
||||
if (instruction.isNoReturn()) {
|
||||
is_noreturn = true;
|
||||
} else {
|
||||
_ = Instruction.CheckVoidStmt.build(irb, &defer_expr_scope.base, instruction);
|
||||
_ = try irb.build(
|
||||
Instruction.CheckVoidStmt,
|
||||
&defer_expr_scope.base,
|
||||
Span.token(defer_expr_scope.expr_node.lastToken()),
|
||||
Instruction.CheckVoidStmt.Params{ .target = instruction },
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -626,7 +748,11 @@ pub const Builder = struct {
|
||||
LVal.Ptr => {
|
||||
// We needed a pointer to a value, but we got a value. So we create
|
||||
// an instruction which just makes a const pointer of it.
|
||||
return Instruction.Ref.build(irb, scope, instruction, Mut.Const, Volatility.NonVolatile);
|
||||
return irb.build(Instruction.Ref, scope, instruction.span, Instruction.Ref.Params{
|
||||
.target = instruction,
|
||||
.mut = Type.Pointer.Mut.Const,
|
||||
.volatility = Type.Pointer.Vol.Non,
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -634,9 +760,218 @@ pub const Builder = struct {
|
||||
fn arena(self: *Builder) *Allocator {
|
||||
return &self.code.arena.allocator;
|
||||
}
|
||||
|
||||
fn buildExtra(
|
||||
self: *Builder,
|
||||
comptime I: type,
|
||||
scope: *Scope,
|
||||
span: Span,
|
||||
params: I.Params,
|
||||
is_generated: bool,
|
||||
) !*Instruction {
|
||||
const inst = try self.arena().create(I{
|
||||
.base = Instruction{
|
||||
.id = Instruction.typeToId(I),
|
||||
.is_generated = is_generated,
|
||||
.scope = scope,
|
||||
.debug_id = self.next_debug_id,
|
||||
.val = switch (I.ir_val_init) {
|
||||
IrVal.Init.Unknown => IrVal.Unknown,
|
||||
IrVal.Init.NoReturn => IrVal{ .KnownValue = &Value.NoReturn.get(self.module).base },
|
||||
IrVal.Init.Void => IrVal{ .KnownValue = &Value.Void.get(self.module).base },
|
||||
},
|
||||
.ref_count = 0,
|
||||
.span = span,
|
||||
.child = null,
|
||||
.parent = null,
|
||||
},
|
||||
.params = params,
|
||||
});
|
||||
|
||||
// Look at the params and ref() other instructions
|
||||
comptime var i = 0;
|
||||
inline while (i < @memberCount(I.Params)) : (i += 1) {
|
||||
const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i)));
|
||||
switch (FieldType) {
|
||||
*Instruction => @field(inst.params, @memberName(I.Params, i)).ref_count += 1,
|
||||
?*Instruction => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref_count += 1,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
self.next_debug_id += 1;
|
||||
try self.current_basic_block.instruction_list.append(&inst.base);
|
||||
return &inst.base;
|
||||
}
|
||||
|
||||
fn build(
|
||||
self: *Builder,
|
||||
comptime I: type,
|
||||
scope: *Scope,
|
||||
span: Span,
|
||||
params: I.Params,
|
||||
) !*Instruction {
|
||||
return self.buildExtra(I, scope, span, params, false);
|
||||
}
|
||||
|
||||
fn buildGen(
|
||||
self: *Builder,
|
||||
comptime I: type,
|
||||
scope: *Scope,
|
||||
span: Span,
|
||||
params: I.Params,
|
||||
) !*Instruction {
|
||||
return self.buildExtra(I, scope, span, params, true);
|
||||
}
|
||||
|
||||
fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Instruction {
|
||||
const inst = try self.build(Instruction.Const, scope, span, Instruction.Const.Params{});
|
||||
inst.val = IrVal{ .KnownValue = &Value.Bool.get(self.module, x).base };
|
||||
return inst;
|
||||
}
|
||||
|
||||
fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Instruction {
|
||||
const inst = try self.buildExtra(Instruction.Const, scope, span, Instruction.Const.Params{}, is_generated);
|
||||
inst.val = IrVal{ .KnownValue = &Value.Void.get(self.module).base };
|
||||
return inst;
|
||||
}
|
||||
};
|
||||
|
||||
pub async fn gen(module: *Module, body_node: *ast.Node, scope: *Scope, parsed_file: *ParsedFile) !*Code {
|
||||
const Analyze = struct {
|
||||
irb: Builder,
|
||||
old_bb_index: usize,
|
||||
const_predecessor_bb: ?*BasicBlock,
|
||||
parent_basic_block: *BasicBlock,
|
||||
instruction_index: usize,
|
||||
src_implicit_return_type_list: std.ArrayList(*Instruction),
|
||||
explicit_return_type: ?*Type,
|
||||
|
||||
pub const Error = error{
|
||||
/// This is only for when we have already reported a compile error. It is the poison value.
|
||||
SemanticAnalysisFailed,
|
||||
|
||||
/// This is a placeholder - it is useful to use instead of panicking but once the compiler is
|
||||
/// done this error code will be removed.
|
||||
Unimplemented,
|
||||
|
||||
OutOfMemory,
|
||||
};
|
||||
|
||||
pub fn init(module: *Module, parsed_file: *ParsedFile, explicit_return_type: ?*Type) !Analyze {
|
||||
var irb = try Builder.init(module, parsed_file);
|
||||
errdefer irb.abort();
|
||||
|
||||
return Analyze{
|
||||
.irb = irb,
|
||||
.old_bb_index = 0,
|
||||
.const_predecessor_bb = null,
|
||||
.parent_basic_block = undefined, // initialized with startBasicBlock
|
||||
.instruction_index = undefined, // initialized with startBasicBlock
|
||||
.src_implicit_return_type_list = std.ArrayList(*Instruction).init(irb.arena()),
|
||||
.explicit_return_type = explicit_return_type,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn abort(self: *Analyze) void {
|
||||
self.irb.abort();
|
||||
}
|
||||
|
||||
pub fn getNewBasicBlock(self: *Analyze, old_bb: *BasicBlock, ref_old_instruction: ?*Instruction) !*BasicBlock {
|
||||
if (old_bb.child) |child| {
|
||||
if (ref_old_instruction == null or child.ref_instruction != ref_old_instruction)
|
||||
return child;
|
||||
}
|
||||
|
||||
const new_bb = try self.irb.createBasicBlock(old_bb.scope, old_bb.name_hint);
|
||||
new_bb.linkToParent(old_bb);
|
||||
new_bb.ref_instruction = ref_old_instruction;
|
||||
return new_bb;
|
||||
}
|
||||
|
||||
pub fn startBasicBlock(self: *Analyze, old_bb: *BasicBlock, const_predecessor_bb: ?*BasicBlock) void {
|
||||
self.instruction_index = 0;
|
||||
self.parent_basic_block = old_bb;
|
||||
self.const_predecessor_bb = const_predecessor_bb;
|
||||
}
|
||||
|
||||
pub fn finishBasicBlock(ira: *Analyze, old_code: *Code) !void {
|
||||
try ira.irb.code.basic_block_list.append(ira.irb.current_basic_block);
|
||||
ira.instruction_index += 1;
|
||||
|
||||
while (ira.instruction_index < ira.parent_basic_block.instruction_list.len) {
|
||||
const next_instruction = ira.parent_basic_block.instruction_list.at(ira.instruction_index);
|
||||
|
||||
if (!next_instruction.is_generated) {
|
||||
try ira.addCompileError(next_instruction.span, "unreachable code");
|
||||
break;
|
||||
}
|
||||
ira.instruction_index += 1;
|
||||
}
|
||||
|
||||
ira.old_bb_index += 1;
|
||||
|
||||
var need_repeat = true;
|
||||
while (true) {
|
||||
while (ira.old_bb_index < old_code.basic_block_list.len) {
|
||||
const old_bb = old_code.basic_block_list.at(ira.old_bb_index);
|
||||
const new_bb = old_bb.child orelse {
|
||||
ira.old_bb_index += 1;
|
||||
continue;
|
||||
};
|
||||
if (new_bb.instruction_list.len != 0) {
|
||||
ira.old_bb_index += 1;
|
||||
continue;
|
||||
}
|
||||
ira.irb.current_basic_block = new_bb;
|
||||
|
||||
ira.startBasicBlock(old_bb, null);
|
||||
return;
|
||||
}
|
||||
if (!need_repeat)
|
||||
return;
|
||||
need_repeat = false;
|
||||
ira.old_bb_index = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void {
|
||||
return self.irb.module.addCompileError(self.irb.parsed_file, span, fmt, args);
|
||||
}
|
||||
|
||||
fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Instruction) Analyze.Error!*Type {
|
||||
// TODO actual implementation
|
||||
return &Type.Void.get(self.irb.module).base;
|
||||
}
|
||||
|
||||
fn implicitCast(self: *Analyze, target: *Instruction, optional_dest_type: ?*Type) Analyze.Error!*Instruction {
|
||||
const dest_type = optional_dest_type orelse return target;
|
||||
@panic("TODO implicitCast");
|
||||
}
|
||||
|
||||
fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Instruction) ?*Value {
|
||||
@panic("TODO getCompTimeValOrNullUndefOk");
|
||||
}
|
||||
|
||||
fn getCompTimeRef(
|
||||
self: *Analyze,
|
||||
value: *Value,
|
||||
ptr_mut: Value.Ptr.Mut,
|
||||
mut: Type.Pointer.Mut,
|
||||
volatility: Type.Pointer.Vol,
|
||||
ptr_align: u32,
|
||||
) Analyze.Error!*Instruction {
|
||||
@panic("TODO getCompTimeRef");
|
||||
}
|
||||
};
|
||||
|
||||
pub async fn gen(
|
||||
module: *Module,
|
||||
body_node: *ast.Node,
|
||||
scope: *Scope,
|
||||
end_span: Span,
|
||||
parsed_file: *ParsedFile,
|
||||
) !*Code {
|
||||
var irb = try Builder.init(module, parsed_file);
|
||||
errdefer irb.abort();
|
||||
|
||||
@ -646,8 +981,61 @@ pub async fn gen(module: *Module, body_node: *ast.Node, scope: *Scope, parsed_fi
|
||||
|
||||
const result = try irb.genNode(body_node, scope, LVal.None);
|
||||
if (!result.isNoReturn()) {
|
||||
(try Instruction.Return.build(&irb, scope, result)).setGenerated();
|
||||
_ = irb.buildGen(
|
||||
Instruction.AddImplicitReturnType,
|
||||
scope,
|
||||
end_span,
|
||||
Instruction.AddImplicitReturnType.Params{ .target = result },
|
||||
);
|
||||
_ = irb.buildGen(
|
||||
Instruction.Return,
|
||||
scope,
|
||||
end_span,
|
||||
Instruction.Return.Params{ .return_value = result },
|
||||
);
|
||||
}
|
||||
|
||||
return irb.finish();
|
||||
}
|
||||
|
||||
pub async fn analyze(module: *Module, parsed_file: *ParsedFile, old_code: *Code, expected_type: ?*Type) !*Code {
|
||||
var ira = try Analyze.init(module, parsed_file, expected_type);
|
||||
errdefer ira.abort();
|
||||
|
||||
const old_entry_bb = old_code.basic_block_list.at(0);
|
||||
|
||||
const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);
|
||||
new_entry_bb.ref();
|
||||
|
||||
ira.irb.current_basic_block = new_entry_bb;
|
||||
|
||||
ira.startBasicBlock(old_entry_bb, null);
|
||||
|
||||
while (ira.old_bb_index < old_code.basic_block_list.len) {
|
||||
const old_instruction = ira.parent_basic_block.instruction_list.at(ira.instruction_index);
|
||||
|
||||
if (old_instruction.ref_count == 0 and !old_instruction.hasSideEffects()) {
|
||||
ira.instruction_index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const return_inst = try old_instruction.analyze(&ira);
|
||||
// Note: if we ever modify the above to handle error.CompileError by continuing analysis,
|
||||
// then here we want to check if ira.isCompTime() and return early if true
|
||||
|
||||
if (return_inst.isNoReturn()) {
|
||||
try ira.finishBasicBlock(old_code);
|
||||
continue;
|
||||
}
|
||||
|
||||
ira.instruction_index += 1;
|
||||
}
|
||||
|
||||
if (ira.src_implicit_return_type_list.len == 0) {
|
||||
ira.irb.code.return_type = &Type.NoReturn.get(module).base;
|
||||
return ira.irb.finish();
|
||||
}
|
||||
|
||||
ira.irb.code.return_type = try ira.resolvePeerTypes(expected_type, ira.src_implicit_return_type_list.toSliceConst());
|
||||
return ira.irb.finish();
|
||||
}
|
||||
|
@ -497,7 +497,7 @@ async fn processBuildEvents(module: *Module, color: errmsg.Color) void {
|
||||
},
|
||||
Module.Event.Error => |err| {
|
||||
std.debug.warn("build failed: {}\n", @errorName(err));
|
||||
@panic("TODO error return trace");
|
||||
os.exit(1);
|
||||
},
|
||||
Module.Event.Fail => |msgs| {
|
||||
for (msgs) |msg| {
|
||||
|
@ -24,6 +24,7 @@ const Visib = @import("visib.zig").Visib;
|
||||
const ParsedFile = @import("parsed_file.zig").ParsedFile;
|
||||
const Value = @import("value.zig").Value;
|
||||
const Type = Value.Type;
|
||||
const Span = errmsg.Span;
|
||||
|
||||
pub const Module = struct {
|
||||
loop: *event.Loop,
|
||||
@ -148,13 +149,14 @@ pub const Module = struct {
|
||||
Overflow,
|
||||
NotSupported,
|
||||
BufferTooSmall,
|
||||
Unimplemented,
|
||||
Unimplemented, // TODO remove this one
|
||||
SemanticAnalysisFailed, // TODO remove this one
|
||||
};
|
||||
|
||||
pub const Event = union(enum) {
|
||||
Ok,
|
||||
Fail: []*errmsg.Msg,
|
||||
Error: BuildError,
|
||||
Fail: []*errmsg.Msg,
|
||||
};
|
||||
|
||||
pub const DarwinVersionMin = union(enum) {
|
||||
@ -413,21 +415,32 @@ pub const Module = struct {
|
||||
while (true) {
|
||||
// TODO directly awaiting async should guarantee memory allocation elision
|
||||
// TODO also async before suspending should guarantee memory allocation elision
|
||||
(await (async self.addRootSrc() catch unreachable)) catch |err| {
|
||||
await (async self.events.put(Event{ .Error = err }) catch unreachable);
|
||||
return;
|
||||
};
|
||||
const build_result = await (async self.addRootSrc() catch unreachable);
|
||||
|
||||
// this makes a handy error return trace and stack trace in debug mode
|
||||
if (std.debug.runtime_safety) {
|
||||
build_result catch unreachable;
|
||||
}
|
||||
|
||||
const compile_errors = blk: {
|
||||
const held = await (async self.compile_errors.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
break :blk held.value.toOwnedSlice();
|
||||
};
|
||||
|
||||
if (build_result) |_| {
|
||||
if (compile_errors.len == 0) {
|
||||
await (async self.events.put(Event.Ok) catch unreachable);
|
||||
} else {
|
||||
await (async self.events.put(Event{ .Fail = compile_errors }) catch unreachable);
|
||||
}
|
||||
} else |err| {
|
||||
// if there's an error then the compile errors have dangling references
|
||||
self.a().free(compile_errors);
|
||||
|
||||
await (async self.events.put(Event{ .Error = err }) catch unreachable);
|
||||
}
|
||||
|
||||
// for now we stop after 1
|
||||
return;
|
||||
}
|
||||
@ -477,7 +490,7 @@ pub const Module = struct {
|
||||
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
|
||||
|
||||
const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
|
||||
try self.addCompileError(parsed_file, errmsg.Span{
|
||||
try self.addCompileError(parsed_file, Span{
|
||||
.first = fn_proto.fn_token,
|
||||
.last = fn_proto.fn_token + 1,
|
||||
}, "missing function name");
|
||||
@ -518,27 +531,23 @@ pub const Module = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn addCompileError(self: *Module, parsed_file: *ParsedFile, span: errmsg.Span, comptime fmt: []const u8, args: ...) !void {
|
||||
fn addCompileError(self: *Module, parsed_file: *ParsedFile, span: Span, comptime fmt: []const u8, args: ...) !void {
|
||||
const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args);
|
||||
errdefer self.loop.allocator.free(text);
|
||||
|
||||
try self.build_group.call(addCompileErrorAsync, self, parsed_file, span.first, span.last, text);
|
||||
try self.build_group.call(addCompileErrorAsync, self, parsed_file, span, text);
|
||||
}
|
||||
|
||||
async fn addCompileErrorAsync(
|
||||
self: *Module,
|
||||
parsed_file: *ParsedFile,
|
||||
first_token: ast.TokenIndex,
|
||||
last_token: ast.TokenIndex,
|
||||
span: Span,
|
||||
text: []u8,
|
||||
) !void {
|
||||
const msg = try self.loop.allocator.create(errmsg.Msg{
|
||||
.path = parsed_file.realpath,
|
||||
.text = text,
|
||||
.span = errmsg.Span{
|
||||
.first = first_token,
|
||||
.last = last_token,
|
||||
},
|
||||
.span = span,
|
||||
.tree = &parsed_file.tree,
|
||||
});
|
||||
errdefer self.loop.allocator.destroy(msg);
|
||||
@ -624,6 +633,7 @@ pub async fn resolveDecl(module: *Module, decl: *Decl) !void {
|
||||
if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {
|
||||
decl.resolution.data = await (async generateDecl(module, decl) catch unreachable);
|
||||
decl.resolution.resolve();
|
||||
return decl.resolution.data;
|
||||
} else {
|
||||
return (await (async decl.resolution.get() catch unreachable)).*;
|
||||
}
|
||||
@ -655,12 +665,41 @@ async fn generateDeclFn(module: *Module, fn_decl: *Decl.Fn) !void {
|
||||
|
||||
fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };
|
||||
|
||||
const code = try await (async ir.gen(
|
||||
const unanalyzed_code = (await (async ir.gen(
|
||||
module,
|
||||
body_node,
|
||||
&fndef_scope.base,
|
||||
Span.token(body_node.lastToken()),
|
||||
fn_decl.base.parsed_file,
|
||||
) catch unreachable);
|
||||
//code.dump();
|
||||
//try await (async irAnalyze(module, func) catch unreachable);
|
||||
) catch unreachable)) catch |err| switch (err) {
|
||||
// This poison value should not cause the errdefers to run. It simply means
|
||||
// that self.compile_errors is populated.
|
||||
error.SemanticAnalysisFailed => return {},
|
||||
else => return err,
|
||||
};
|
||||
defer unanalyzed_code.destroy(module.a());
|
||||
|
||||
if (module.verbose_ir) {
|
||||
std.debug.warn("unanalyzed:\n");
|
||||
unanalyzed_code.dump();
|
||||
}
|
||||
|
||||
const analyzed_code = (await (async ir.analyze(
|
||||
module,
|
||||
fn_decl.base.parsed_file,
|
||||
unanalyzed_code,
|
||||
null,
|
||||
) catch unreachable)) catch |err| switch (err) {
|
||||
// This poison value should not cause the errdefers to run. It simply means
|
||||
// that self.compile_errors is populated.
|
||||
error.SemanticAnalysisFailed => return {},
|
||||
else => return err,
|
||||
};
|
||||
defer analyzed_code.destroy(module.a());
|
||||
|
||||
if (module.verbose_ir) {
|
||||
std.debug.warn("analyzed:\n");
|
||||
analyzed_code.dump();
|
||||
}
|
||||
// TODO now render to LLVM module
|
||||
}
|
||||
|
@ -39,6 +39,14 @@ pub const Type = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dump(base: *const Type) void {
|
||||
std.debug.warn("{}", @tagName(base.id));
|
||||
}
|
||||
|
||||
pub fn getAbiAlignment(base: *Type, module: *Module) u32 {
|
||||
@panic("TODO getAbiAlignment");
|
||||
}
|
||||
|
||||
pub const Struct = struct {
|
||||
base: Type,
|
||||
decls: *Scope.Decls,
|
||||
@ -143,10 +151,35 @@ pub const Type = struct {
|
||||
};
|
||||
pub const Pointer = struct {
|
||||
base: Type,
|
||||
mut: Mut,
|
||||
vol: Vol,
|
||||
size: Size,
|
||||
alignment: u32,
|
||||
|
||||
pub const Mut = enum {
|
||||
Mut,
|
||||
Const,
|
||||
};
|
||||
pub const Vol = enum {
|
||||
Non,
|
||||
Volatile,
|
||||
};
|
||||
pub const Size = builtin.TypeInfo.Pointer.Size;
|
||||
|
||||
pub fn destroy(self: *Pointer, module: *Module) void {
|
||||
module.a().destroy(self);
|
||||
}
|
||||
|
||||
pub fn get(
|
||||
module: *Module,
|
||||
elem_type: *Type,
|
||||
mut: Mut,
|
||||
vol: Vol,
|
||||
size: Size,
|
||||
alignment: u32,
|
||||
) *Pointer {
|
||||
@panic("TODO get pointer");
|
||||
}
|
||||
};
|
||||
pub const Array = struct {
|
||||
base: Type,
|
||||
|
@ -24,10 +24,16 @@ pub const Value = struct {
|
||||
Id.Void => @fieldParentPtr(Void, "base", base).destroy(module),
|
||||
Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(module),
|
||||
Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(module),
|
||||
Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(module),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getRef(base: *Value) *Value {
|
||||
base.ref();
|
||||
return base;
|
||||
}
|
||||
|
||||
pub fn dump(base: *const Value) void {
|
||||
std.debug.warn("{}", @tagName(base.id));
|
||||
}
|
||||
@ -38,6 +44,7 @@ pub const Value = struct {
|
||||
Void,
|
||||
Bool,
|
||||
NoReturn,
|
||||
Ptr,
|
||||
};
|
||||
|
||||
pub const Type = @import("type.zig").Type;
|
||||
@ -122,4 +129,18 @@ pub const Value = struct {
|
||||
module.a().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Ptr = struct {
|
||||
base: Value,
|
||||
|
||||
pub const Mut = enum {
|
||||
CompTimeConst,
|
||||
CompTimeVar,
|
||||
RunTime,
|
||||
};
|
||||
|
||||
pub fn destroy(self: *Ptr, module: *Module) void {
|
||||
module.a().destroy(self);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -382,6 +382,21 @@ pub const Loop = struct {
|
||||
return async<self.allocator> S.asyncFunc(self, &handle, args);
|
||||
}
|
||||
|
||||
/// Awaiting a yield lets the event loop run, starting any unstarted async operations.
|
||||
/// Note that async operations automatically start when a function yields for any other reason,
|
||||
/// for example, when async I/O is performed. This function is intended to be used only when
|
||||
/// CPU bound tasks would be waiting in the event loop but never get started because no async I/O
|
||||
/// is performed.
|
||||
pub async fn yield(self: *Loop) void {
|
||||
suspend |p| {
|
||||
var my_tick_node = Loop.NextTickNode{
|
||||
.next = undefined,
|
||||
.data = p,
|
||||
};
|
||||
loop.onNextTick(&my_tick_node);
|
||||
}
|
||||
}
|
||||
|
||||
fn workerRun(self: *Loop) void {
|
||||
start_over: while (true) {
|
||||
if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user