self-hosted: generate zig IR for simple function
no tests for this yet. I think the quickest path to testing will be creating the .o files and linking with libc, executing, and then comparing output.
This commit is contained in:
parent
ce11d6d16c
commit
687bd92f9c
96
src-self-hosted/decl.zig
Normal file
96
src-self-hosted/decl.zig
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const Allocator = mem.Allocator;
|
||||||
|
const mem = std.mem;
|
||||||
|
const ast = std.zig.ast;
|
||||||
|
const Visib = @import("visib.zig").Visib;
|
||||||
|
const ParsedFile = @import("parsed_file.zig").ParsedFile;
|
||||||
|
const event = std.event;
|
||||||
|
const Value = @import("value.zig").Value;
|
||||||
|
const Token = std.zig.Token;
|
||||||
|
const errmsg = @import("errmsg.zig");
|
||||||
|
const Scope = @import("scope.zig").Scope;
|
||||||
|
const Module = @import("module.zig").Module;
|
||||||
|
|
||||||
|
pub const Decl = struct {
|
||||||
|
id: Id,
|
||||||
|
name: []const u8,
|
||||||
|
visib: Visib,
|
||||||
|
resolution: event.Future(Module.BuildError!void),
|
||||||
|
resolution_in_progress: u8,
|
||||||
|
parsed_file: *ParsedFile,
|
||||||
|
parent_scope: *Scope,
|
||||||
|
|
||||||
|
pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
|
||||||
|
|
||||||
|
pub fn isExported(base: *const Decl, tree: *ast.Tree) bool {
|
||||||
|
switch (base.id) {
|
||||||
|
Id.Fn => {
|
||||||
|
const fn_decl = @fieldParentPtr(Fn, "base", base);
|
||||||
|
return fn_decl.isExported(tree);
|
||||||
|
},
|
||||||
|
else => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getSpan(base: *const Decl) errmsg.Span {
|
||||||
|
switch (base.id) {
|
||||||
|
Id.Fn => {
|
||||||
|
const fn_decl = @fieldParentPtr(Fn, "base", base);
|
||||||
|
const fn_proto = fn_decl.fn_proto;
|
||||||
|
const start = fn_proto.fn_token;
|
||||||
|
const end = fn_proto.name_token orelse start;
|
||||||
|
return errmsg.Span{
|
||||||
|
.first = start,
|
||||||
|
.last = end + 1,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
else => @panic("TODO"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Id = enum {
|
||||||
|
Var,
|
||||||
|
Fn,
|
||||||
|
CompTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Var = struct {
|
||||||
|
base: Decl,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Fn = struct {
|
||||||
|
base: Decl,
|
||||||
|
value: Val,
|
||||||
|
fn_proto: *const ast.Node.FnProto,
|
||||||
|
|
||||||
|
// TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
|
||||||
|
pub const Val = union {
|
||||||
|
Unresolved: void,
|
||||||
|
Ok: *Value.Fn,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
|
||||||
|
return if (self.fn_proto.extern_export_inline_token) |tok_index| x: {
|
||||||
|
const token = tree.tokens.at(tok_index);
|
||||||
|
break :x switch (token.id) {
|
||||||
|
Token.Id.Extern => tree.tokenSlicePtr(token),
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
} else null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn isExported(self: Fn, tree: *ast.Tree) bool {
|
||||||
|
if (self.fn_proto.extern_export_inline_token) |tok_index| {
|
||||||
|
const token = tree.tokens.at(tok_index);
|
||||||
|
return token.id == Token.Id.Keyword_export;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const CompTime = struct {
|
||||||
|
base: Decl,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -1,111 +1,656 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const Module = @import("module.zig").Module;
|
||||||
const Scope = @import("scope.zig").Scope;
|
const Scope = @import("scope.zig").Scope;
|
||||||
|
const ast = std.zig.ast;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const Value = @import("value.zig").Value;
|
||||||
|
const Type = Value.Type;
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const Token = std.zig.Token;
|
||||||
|
const ParsedFile = @import("parsed_file.zig").ParsedFile;
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
pub fn dump(self: IrVal) void {
|
||||||
|
switch (self) {
|
||||||
|
IrVal.Unknown => std.debug.warn("Unknown"),
|
||||||
|
IrVal.Known => |value| {
|
||||||
|
std.debug.warn("Known(");
|
||||||
|
value.dump();
|
||||||
|
std.debug.warn(")");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const Instruction = struct {
|
pub const Instruction = struct {
|
||||||
id: Id,
|
id: Id,
|
||||||
scope: *Scope,
|
scope: *Scope,
|
||||||
|
debug_id: usize,
|
||||||
|
val: IrVal,
|
||||||
|
|
||||||
|
/// true if this instruction was generated by zig and not from user code
|
||||||
|
is_generated: bool,
|
||||||
|
|
||||||
|
pub fn cast(base: *Instruction, comptime T: type) ?*T {
|
||||||
|
if (base.id == comptime typeToId(T)) {
|
||||||
|
return @fieldParentPtr(T, "base", base);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn typeToId(comptime T: type) Id {
|
||||||
|
comptime var i = 0;
|
||||||
|
inline while (i < @memberCount(Id)) : (i += 1) {
|
||||||
|
if (T == @field(Instruction, @memberName(Id, i))) {
|
||||||
|
return @field(Id, @memberName(Id, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dump(base: *const Instruction) void {
|
||||||
|
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));
|
||||||
|
std.debug.warn("#{} = {}(", base.debug_id, @tagName(base.id));
|
||||||
|
@fieldParentPtr(T, "base", base).dump();
|
||||||
|
std.debug.warn(")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setGenerated(base: *Instruction) void {
|
||||||
|
base.is_generated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn isNoReturn(base: *const Instruction) bool {
|
||||||
|
switch (base.val) {
|
||||||
|
IrVal.Unknown => return false,
|
||||||
|
IrVal.Known => |x| return x.typeof.id == Type.Id.NoReturn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const Id = enum {
|
pub const Id = enum {
|
||||||
Br,
|
|
||||||
CondBr,
|
|
||||||
SwitchBr,
|
|
||||||
SwitchVar,
|
|
||||||
SwitchTarget,
|
|
||||||
Phi,
|
|
||||||
UnOp,
|
|
||||||
BinOp,
|
|
||||||
DeclVar,
|
|
||||||
LoadPtr,
|
|
||||||
StorePtr,
|
|
||||||
FieldPtr,
|
|
||||||
StructFieldPtr,
|
|
||||||
UnionFieldPtr,
|
|
||||||
ElemPtr,
|
|
||||||
VarPtr,
|
|
||||||
Call,
|
|
||||||
Const,
|
|
||||||
Return,
|
Return,
|
||||||
Cast,
|
Const,
|
||||||
ContainerInitList,
|
|
||||||
ContainerInitFields,
|
|
||||||
StructInit,
|
|
||||||
UnionInit,
|
|
||||||
Unreachable,
|
|
||||||
TypeOf,
|
|
||||||
ToPtrType,
|
|
||||||
PtrTypeChild,
|
|
||||||
SetRuntimeSafety,
|
|
||||||
SetFloatMode,
|
|
||||||
ArrayType,
|
|
||||||
SliceType,
|
|
||||||
Asm,
|
|
||||||
SizeOf,
|
|
||||||
TestNonNull,
|
|
||||||
UnwrapMaybe,
|
|
||||||
MaybeWrap,
|
|
||||||
UnionTag,
|
|
||||||
Clz,
|
|
||||||
Ctz,
|
|
||||||
Import,
|
|
||||||
CImport,
|
|
||||||
CInclude,
|
|
||||||
CDefine,
|
|
||||||
CUndef,
|
|
||||||
ArrayLen,
|
|
||||||
Ref,
|
Ref,
|
||||||
MinValue,
|
DeclVar,
|
||||||
MaxValue,
|
CheckVoidStmt,
|
||||||
CompileErr,
|
Phi,
|
||||||
CompileLog,
|
Br,
|
||||||
ErrName,
|
};
|
||||||
EmbedFile,
|
|
||||||
Cmpxchg,
|
pub const Const = struct {
|
||||||
Fence,
|
base: Instruction,
|
||||||
Truncate,
|
|
||||||
IntType,
|
pub fn buildBool(irb: *Builder, scope: *Scope, val: bool) !*Instruction {
|
||||||
BoolNot,
|
const inst = try irb.arena().create(Const{
|
||||||
Memset,
|
.base = Instruction{
|
||||||
Memcpy,
|
.id = Instruction.Id.Const,
|
||||||
Slice,
|
.is_generated = false,
|
||||||
MemberCount,
|
.scope = scope,
|
||||||
MemberType,
|
.debug_id = irb.next_debug_id,
|
||||||
MemberName,
|
.val = IrVal{ .Known = &Value.Bool.get(irb.module, val).base },
|
||||||
Breakpoint,
|
},
|
||||||
ReturnAddress,
|
});
|
||||||
FrameAddress,
|
irb.next_debug_id += 1;
|
||||||
AlignOf,
|
try irb.current_basic_block.instruction_list.append(&inst.base);
|
||||||
OverflowOp,
|
return &inst.base;
|
||||||
TestErr,
|
}
|
||||||
UnwrapErrCode,
|
|
||||||
UnwrapErrPayload,
|
pub fn buildVoid(irb: *Builder, scope: *Scope, is_generated: bool) !*Instruction {
|
||||||
ErrWrapCode,
|
const inst = try irb.arena().create(Const{
|
||||||
ErrWrapPayload,
|
.base = Instruction{
|
||||||
FnProto,
|
.id = Instruction.Id.Const,
|
||||||
TestComptime,
|
.is_generated = is_generated,
|
||||||
PtrCast,
|
.scope = scope,
|
||||||
BitCast,
|
.debug_id = irb.next_debug_id,
|
||||||
WidenOrShorten,
|
.val = IrVal{ .Known = &Value.Void.get(irb.module).base },
|
||||||
IntToPtr,
|
},
|
||||||
PtrToInt,
|
});
|
||||||
IntToEnum,
|
irb.next_debug_id += 1;
|
||||||
IntToErr,
|
try irb.current_basic_block.instruction_list.append(&inst.base);
|
||||||
ErrToInt,
|
return &inst.base;
|
||||||
CheckSwitchProngs,
|
}
|
||||||
CheckStatementIsVoid,
|
|
||||||
TypeName,
|
pub fn dump(inst: *const Const) void {
|
||||||
CanImplicitCast,
|
inst.base.val.Known.dump();
|
||||||
DeclRef,
|
}
|
||||||
Panic,
|
};
|
||||||
TagName,
|
|
||||||
TagType,
|
pub const Return = struct {
|
||||||
FieldParentPtr,
|
base: Instruction,
|
||||||
OffsetOf,
|
return_value: *Instruction,
|
||||||
TypeId,
|
|
||||||
SetEvalBranchQuota,
|
pub fn build(irb: *Builder, scope: *Scope, return_value: *Instruction) !*Instruction {
|
||||||
PtrTypeOf,
|
const inst = try irb.arena().create(Return{
|
||||||
AlignCast,
|
.base = Instruction{
|
||||||
OpaqueType,
|
.id = Instruction.Id.Return,
|
||||||
SetAlignStack,
|
.is_generated = false,
|
||||||
ArgType,
|
.scope = scope,
|
||||||
Export,
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dump(inst: *const Return) void {
|
||||||
|
std.debug.warn("#{}", inst.return_value.debug_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Ref = struct {
|
||||||
|
base: Instruction,
|
||||||
|
target: *Instruction,
|
||||||
|
mut: Mut,
|
||||||
|
volatility: Volatility,
|
||||||
|
|
||||||
|
pub fn build(
|
||||||
|
irb: *Builder,
|
||||||
|
scope: *Scope,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dump(inst: *const Ref) void {}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DeclVar = struct {
|
||||||
|
base: Instruction,
|
||||||
|
variable: *Variable,
|
||||||
|
|
||||||
|
pub fn dump(inst: *const DeclVar) void {}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const CheckVoidStmt = struct {
|
||||||
|
base: Instruction,
|
||||||
|
target: *Instruction,
|
||||||
|
|
||||||
|
pub fn build(
|
||||||
|
irb: *Builder,
|
||||||
|
scope: *Scope,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dump(inst: *const CheckVoidStmt) void {}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Phi = struct {
|
||||||
|
base: Instruction,
|
||||||
|
incoming_blocks: []*BasicBlock,
|
||||||
|
incoming_values: []*Instruction,
|
||||||
|
|
||||||
|
pub fn build(
|
||||||
|
irb: *Builder,
|
||||||
|
scope: *Scope,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dump(inst: *const Phi) void {}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Br = struct {
|
||||||
|
base: Instruction,
|
||||||
|
dest_block: *BasicBlock,
|
||||||
|
is_comptime: *Instruction,
|
||||||
|
|
||||||
|
pub fn build(
|
||||||
|
irb: *Builder,
|
||||||
|
scope: *Scope,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dump(inst: *const Br) void {}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Variable = struct {
|
||||||
|
child_scope: *Scope,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const BasicBlock = struct {
|
||||||
|
ref_count: usize,
|
||||||
|
name_hint: []const u8,
|
||||||
|
debug_id: usize,
|
||||||
|
scope: *Scope,
|
||||||
|
instruction_list: std.ArrayList(*Instruction),
|
||||||
|
|
||||||
|
pub fn ref(self: *BasicBlock) void {
|
||||||
|
self.ref_count += 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Stuff that survives longer than Builder
|
||||||
|
pub const Code = struct {
|
||||||
|
basic_block_list: std.ArrayList(*BasicBlock),
|
||||||
|
arena: std.heap.ArenaAllocator,
|
||||||
|
|
||||||
|
/// allocator is module.a()
|
||||||
|
pub fn destroy(self: *Code, allocator: *Allocator) void {
|
||||||
|
self.arena.deinit();
|
||||||
|
allocator.destroy(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dump(self: *Code) void {
|
||||||
|
var bb_i: usize = 0;
|
||||||
|
for (self.basic_block_list.toSliceConst()) |bb| {
|
||||||
|
std.debug.warn("{}_{}:\n", bb.name_hint, bb.debug_id);
|
||||||
|
for (bb.instruction_list.toSliceConst()) |instr| {
|
||||||
|
std.debug.warn(" ");
|
||||||
|
instr.dump();
|
||||||
|
std.debug.warn("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Builder = struct {
|
||||||
|
module: *Module,
|
||||||
|
code: *Code,
|
||||||
|
current_basic_block: *BasicBlock,
|
||||||
|
next_debug_id: usize,
|
||||||
|
parsed_file: *ParsedFile,
|
||||||
|
is_comptime: bool,
|
||||||
|
|
||||||
|
pub const Error = error{
|
||||||
|
OutOfMemory,
|
||||||
|
Unimplemented,
|
||||||
|
};
|
||||||
|
|
||||||
|
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()),
|
||||||
|
});
|
||||||
|
code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
|
||||||
|
errdefer code.destroy(module.a());
|
||||||
|
|
||||||
|
return Builder{
|
||||||
|
.module = module,
|
||||||
|
.parsed_file = parsed_file,
|
||||||
|
.current_basic_block = undefined,
|
||||||
|
.code = code,
|
||||||
|
.next_debug_id = 0,
|
||||||
|
.is_comptime = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn abort(self: *Builder) void {
|
||||||
|
self.code.destroy(self.module.a());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call code.destroy() when done
|
||||||
|
pub fn finish(self: *Builder) *Code {
|
||||||
|
return self.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// No need to clean up resources thanks to the arena allocator.
|
||||||
|
pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: []const u8) !*BasicBlock {
|
||||||
|
const basic_block = try self.arena().create(BasicBlock{
|
||||||
|
.ref_count = 0,
|
||||||
|
.name_hint = name_hint,
|
||||||
|
.debug_id = self.next_debug_id,
|
||||||
|
.scope = scope,
|
||||||
|
.instruction_list = std.ArrayList(*Instruction).init(self.arena()),
|
||||||
|
});
|
||||||
|
self.next_debug_id += 1;
|
||||||
|
return basic_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setCursorAtEndAndAppendBlock(self: *Builder, basic_block: *BasicBlock) !void {
|
||||||
|
try self.code.basic_block_list.append(basic_block);
|
||||||
|
self.setCursorAtEnd(basic_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setCursorAtEnd(self: *Builder, basic_block: *BasicBlock) void {
|
||||||
|
self.current_basic_block = basic_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Instruction {
|
||||||
|
switch (node.id) {
|
||||||
|
ast.Node.Id.Root => unreachable,
|
||||||
|
ast.Node.Id.Use => unreachable,
|
||||||
|
ast.Node.Id.TestDecl => unreachable,
|
||||||
|
ast.Node.Id.VarDecl => @panic("TODO"),
|
||||||
|
ast.Node.Id.Defer => @panic("TODO"),
|
||||||
|
ast.Node.Id.InfixOp => @panic("TODO"),
|
||||||
|
ast.Node.Id.PrefixOp => @panic("TODO"),
|
||||||
|
ast.Node.Id.SuffixOp => @panic("TODO"),
|
||||||
|
ast.Node.Id.Switch => @panic("TODO"),
|
||||||
|
ast.Node.Id.While => @panic("TODO"),
|
||||||
|
ast.Node.Id.For => @panic("TODO"),
|
||||||
|
ast.Node.Id.If => @panic("TODO"),
|
||||||
|
ast.Node.Id.ControlFlowExpression => return error.Unimplemented,
|
||||||
|
ast.Node.Id.Suspend => @panic("TODO"),
|
||||||
|
ast.Node.Id.VarType => @panic("TODO"),
|
||||||
|
ast.Node.Id.ErrorType => @panic("TODO"),
|
||||||
|
ast.Node.Id.FnProto => @panic("TODO"),
|
||||||
|
ast.Node.Id.PromiseType => @panic("TODO"),
|
||||||
|
ast.Node.Id.IntegerLiteral => @panic("TODO"),
|
||||||
|
ast.Node.Id.FloatLiteral => @panic("TODO"),
|
||||||
|
ast.Node.Id.StringLiteral => @panic("TODO"),
|
||||||
|
ast.Node.Id.MultilineStringLiteral => @panic("TODO"),
|
||||||
|
ast.Node.Id.CharLiteral => @panic("TODO"),
|
||||||
|
ast.Node.Id.BoolLiteral => @panic("TODO"),
|
||||||
|
ast.Node.Id.NullLiteral => @panic("TODO"),
|
||||||
|
ast.Node.Id.UndefinedLiteral => @panic("TODO"),
|
||||||
|
ast.Node.Id.ThisLiteral => @panic("TODO"),
|
||||||
|
ast.Node.Id.Unreachable => @panic("TODO"),
|
||||||
|
ast.Node.Id.Identifier => @panic("TODO"),
|
||||||
|
ast.Node.Id.GroupedExpression => {
|
||||||
|
const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node);
|
||||||
|
return irb.genNode(grouped_expr.expr, scope, lval);
|
||||||
|
},
|
||||||
|
ast.Node.Id.BuiltinCall => @panic("TODO"),
|
||||||
|
ast.Node.Id.ErrorSetDecl => @panic("TODO"),
|
||||||
|
ast.Node.Id.ContainerDecl => @panic("TODO"),
|
||||||
|
ast.Node.Id.Asm => @panic("TODO"),
|
||||||
|
ast.Node.Id.Comptime => @panic("TODO"),
|
||||||
|
ast.Node.Id.Block => {
|
||||||
|
const block = @fieldParentPtr(ast.Node.Block, "base", node);
|
||||||
|
return irb.lvalWrap(scope, try irb.genBlock(block, scope), lval);
|
||||||
|
},
|
||||||
|
ast.Node.Id.DocComment => @panic("TODO"),
|
||||||
|
ast.Node.Id.SwitchCase => @panic("TODO"),
|
||||||
|
ast.Node.Id.SwitchElse => @panic("TODO"),
|
||||||
|
ast.Node.Id.Else => @panic("TODO"),
|
||||||
|
ast.Node.Id.Payload => @panic("TODO"),
|
||||||
|
ast.Node.Id.PointerPayload => @panic("TODO"),
|
||||||
|
ast.Node.Id.PointerIndexPayload => @panic("TODO"),
|
||||||
|
ast.Node.Id.StructField => @panic("TODO"),
|
||||||
|
ast.Node.Id.UnionTag => @panic("TODO"),
|
||||||
|
ast.Node.Id.EnumTag => @panic("TODO"),
|
||||||
|
ast.Node.Id.ErrorTag => @panic("TODO"),
|
||||||
|
ast.Node.Id.AsmInput => @panic("TODO"),
|
||||||
|
ast.Node.Id.AsmOutput => @panic("TODO"),
|
||||||
|
ast.Node.Id.AsyncAttribute => @panic("TODO"),
|
||||||
|
ast.Node.Id.ParamDecl => @panic("TODO"),
|
||||||
|
ast.Node.Id.FieldInitializer => @panic("TODO"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn isCompTime(irb: *Builder, target_scope: *Scope) bool {
|
||||||
|
if (irb.is_comptime)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var scope = target_scope;
|
||||||
|
while (true) {
|
||||||
|
switch (scope.id) {
|
||||||
|
Scope.Id.CompTime => return true,
|
||||||
|
Scope.Id.FnDef => return false,
|
||||||
|
Scope.Id.Decls => unreachable,
|
||||||
|
Scope.Id.Block,
|
||||||
|
Scope.Id.Defer,
|
||||||
|
Scope.Id.DeferExpr,
|
||||||
|
=> scope = scope.parent orelse return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Instruction {
|
||||||
|
const block_scope = try Scope.Block.create(irb.module, parent_scope);
|
||||||
|
|
||||||
|
const outer_block_scope = &block_scope.base;
|
||||||
|
var child_scope = outer_block_scope;
|
||||||
|
|
||||||
|
if (parent_scope.findFnDef()) |fndef_scope| {
|
||||||
|
if (fndef_scope.fn_val.child_scope == parent_scope) {
|
||||||
|
fndef_scope.fn_val.block_scope = block_scope;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block.statements.len == 0) {
|
||||||
|
// {}
|
||||||
|
return Instruction.Const.buildVoid(irb, child_scope, 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
var is_continuation_unreachable = false;
|
||||||
|
var noreturn_return_value: ?*Instruction = null;
|
||||||
|
|
||||||
|
var stmt_it = block.statements.iterator(0);
|
||||||
|
while (stmt_it.next()) |statement_node_ptr| {
|
||||||
|
const statement_node = statement_node_ptr.*;
|
||||||
|
|
||||||
|
if (statement_node.cast(ast.Node.Defer)) |defer_node| {
|
||||||
|
// defer starts a new scope
|
||||||
|
const defer_token = irb.parsed_file.tree.tokens.at(defer_node.defer_token);
|
||||||
|
const kind = switch (defer_token.id) {
|
||||||
|
Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,
|
||||||
|
Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
const defer_expr_scope = try Scope.DeferExpr.create(irb.module, parent_scope, defer_node.expr);
|
||||||
|
const defer_child_scope = try Scope.Defer.create(irb.module, parent_scope, kind, defer_expr_scope);
|
||||||
|
child_scope = &defer_child_scope.base;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const statement_value = try irb.genNode(statement_node, child_scope, LVal.None);
|
||||||
|
|
||||||
|
is_continuation_unreachable = statement_value.isNoReturn();
|
||||||
|
if (is_continuation_unreachable) {
|
||||||
|
// keep the last noreturn statement value around in case we need to return it
|
||||||
|
noreturn_return_value = statement_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statement_value.cast(Instruction.DeclVar)) |decl_var| {
|
||||||
|
// variable declarations start a new scope
|
||||||
|
child_scope = decl_var.variable.child_scope;
|
||||||
|
} else if (!is_continuation_unreachable) {
|
||||||
|
// this statement's value must be void
|
||||||
|
_ = Instruction.CheckVoidStmt.build(irb, child_scope, statement_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_continuation_unreachable) {
|
||||||
|
assert(noreturn_return_value != null);
|
||||||
|
if (block.label == null or block_scope.incoming_blocks.len == 0) {
|
||||||
|
return noreturn_return_value.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
|
||||||
|
return Instruction.Phi.build(
|
||||||
|
irb,
|
||||||
|
parent_scope,
|
||||||
|
block_scope.incoming_blocks.toOwnedSlice(),
|
||||||
|
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.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.setCursorAtEndAndAppendBlock(block_scope.end_block);
|
||||||
|
return Instruction.Phi.build(
|
||||||
|
irb,
|
||||||
|
parent_scope,
|
||||||
|
block_scope.incoming_blocks.toOwnedSlice(),
|
||||||
|
block_scope.incoming_values.toOwnedSlice(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit);
|
||||||
|
const result = try Instruction.Const.buildVoid(irb, child_scope, false);
|
||||||
|
result.setGenerated();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn genDefersForBlock(
|
||||||
|
irb: *Builder,
|
||||||
|
inner_scope: *Scope,
|
||||||
|
outer_scope: *Scope,
|
||||||
|
gen_kind: Scope.Defer.Kind,
|
||||||
|
) !bool {
|
||||||
|
var scope = inner_scope;
|
||||||
|
var is_noreturn = false;
|
||||||
|
while (true) {
|
||||||
|
switch (scope.id) {
|
||||||
|
Scope.Id.Defer => {
|
||||||
|
const defer_scope = @fieldParentPtr(Scope.Defer, "base", scope);
|
||||||
|
const generate = switch (defer_scope.kind) {
|
||||||
|
Scope.Defer.Kind.ScopeExit => true,
|
||||||
|
Scope.Defer.Kind.ErrorExit => gen_kind == Scope.Defer.Kind.ErrorExit,
|
||||||
|
};
|
||||||
|
if (generate) {
|
||||||
|
const defer_expr_scope = defer_scope.defer_expr_scope;
|
||||||
|
const instruction = try irb.genNode(
|
||||||
|
defer_expr_scope.expr_node,
|
||||||
|
&defer_expr_scope.base,
|
||||||
|
LVal.None,
|
||||||
|
);
|
||||||
|
if (instruction.isNoReturn()) {
|
||||||
|
is_noreturn = true;
|
||||||
|
} else {
|
||||||
|
_ = Instruction.CheckVoidStmt.build(irb, &defer_expr_scope.base, instruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Scope.Id.FnDef,
|
||||||
|
Scope.Id.Decls,
|
||||||
|
=> return is_noreturn,
|
||||||
|
|
||||||
|
Scope.Id.CompTime,
|
||||||
|
Scope.Id.Block,
|
||||||
|
=> scope = scope.parent orelse return is_noreturn,
|
||||||
|
|
||||||
|
Scope.Id.DeferExpr => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Instruction, lval: LVal) !*Instruction {
|
||||||
|
switch (lval) {
|
||||||
|
LVal.None => return instruction,
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arena(self: *Builder) *Allocator {
|
||||||
|
return &self.code.arena.allocator;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn gen(module: *Module, body_node: *ast.Node, scope: *Scope, parsed_file: *ParsedFile) !*Code {
|
||||||
|
var irb = try Builder.init(module, parsed_file);
|
||||||
|
errdefer irb.abort();
|
||||||
|
|
||||||
|
const entry_block = try irb.createBasicBlock(scope, "Entry");
|
||||||
|
entry_block.ref(); // Entry block gets a reference because we enter it to begin.
|
||||||
|
try irb.setCursorAtEndAndAppendBlock(entry_block);
|
||||||
|
|
||||||
|
const result = try irb.genNode(body_node, scope, LVal.None);
|
||||||
|
if (!result.isNoReturn()) {
|
||||||
|
const void_inst = try Instruction.Const.buildVoid(&irb, scope, false);
|
||||||
|
(try Instruction.Return.build(&irb, scope, void_inst)).setGenerated();
|
||||||
|
}
|
||||||
|
|
||||||
|
return irb.finish();
|
||||||
|
}
|
||||||
|
@ -15,12 +15,21 @@ const errmsg = @import("errmsg.zig");
|
|||||||
const ast = std.zig.ast;
|
const ast = std.zig.ast;
|
||||||
const event = std.event;
|
const event = std.event;
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
const AtomicRmwOp = builtin.AtomicRmwOp;
|
||||||
|
const AtomicOrder = builtin.AtomicOrder;
|
||||||
|
const Scope = @import("scope.zig").Scope;
|
||||||
|
const Decl = @import("decl.zig").Decl;
|
||||||
|
const ir = @import("ir.zig");
|
||||||
|
const Visib = @import("visib.zig").Visib;
|
||||||
|
const ParsedFile = @import("parsed_file.zig").ParsedFile;
|
||||||
|
const Value = @import("value.zig").Value;
|
||||||
|
const Type = Value.Type;
|
||||||
|
|
||||||
pub const Module = struct {
|
pub const Module = struct {
|
||||||
loop: *event.Loop,
|
loop: *event.Loop,
|
||||||
name: Buffer,
|
name: Buffer,
|
||||||
root_src_path: ?[]const u8,
|
root_src_path: ?[]const u8,
|
||||||
module: llvm.ModuleRef,
|
llvm_module: llvm.ModuleRef,
|
||||||
context: llvm.ContextRef,
|
context: llvm.ContextRef,
|
||||||
builder: llvm.BuilderRef,
|
builder: llvm.BuilderRef,
|
||||||
target: Target,
|
target: Target,
|
||||||
@ -91,6 +100,16 @@ pub const Module = struct {
|
|||||||
|
|
||||||
compile_errors: event.Locked(CompileErrList),
|
compile_errors: event.Locked(CompileErrList),
|
||||||
|
|
||||||
|
meta_type: *Type.MetaType,
|
||||||
|
void_type: *Type.Void,
|
||||||
|
bool_type: *Type.Bool,
|
||||||
|
noreturn_type: *Type.NoReturn,
|
||||||
|
|
||||||
|
void_value: *Value.Void,
|
||||||
|
true_value: *Value.Bool,
|
||||||
|
false_value: *Value.Bool,
|
||||||
|
noreturn_value: *Value.NoReturn,
|
||||||
|
|
||||||
const CompileErrList = std.ArrayList(*errmsg.Msg);
|
const CompileErrList = std.ArrayList(*errmsg.Msg);
|
||||||
|
|
||||||
// TODO handle some of these earlier and report them in a way other than error codes
|
// TODO handle some of these earlier and report them in a way other than error codes
|
||||||
@ -129,6 +148,7 @@ pub const Module = struct {
|
|||||||
Overflow,
|
Overflow,
|
||||||
NotSupported,
|
NotSupported,
|
||||||
BufferTooSmall,
|
BufferTooSmall,
|
||||||
|
Unimplemented,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Event = union(enum) {
|
pub const Event = union(enum) {
|
||||||
@ -180,8 +200,8 @@ pub const Module = struct {
|
|||||||
const context = c.LLVMContextCreate() orelse return error.OutOfMemory;
|
const context = c.LLVMContextCreate() orelse return error.OutOfMemory;
|
||||||
errdefer c.LLVMContextDispose(context);
|
errdefer c.LLVMContextDispose(context);
|
||||||
|
|
||||||
const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) orelse return error.OutOfMemory;
|
const llvm_module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) orelse return error.OutOfMemory;
|
||||||
errdefer c.LLVMDisposeModule(module);
|
errdefer c.LLVMDisposeModule(llvm_module);
|
||||||
|
|
||||||
const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory;
|
const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory;
|
||||||
errdefer c.LLVMDisposeBuilder(builder);
|
errdefer c.LLVMDisposeBuilder(builder);
|
||||||
@ -189,12 +209,12 @@ pub const Module = struct {
|
|||||||
const events = try event.Channel(Event).create(loop, 0);
|
const events = try event.Channel(Event).create(loop, 0);
|
||||||
errdefer events.destroy();
|
errdefer events.destroy();
|
||||||
|
|
||||||
return loop.allocator.create(Module{
|
const module = try loop.allocator.create(Module{
|
||||||
.loop = loop,
|
.loop = loop,
|
||||||
.events = events,
|
.events = events,
|
||||||
.name = name_buffer,
|
.name = name_buffer,
|
||||||
.root_src_path = root_src_path,
|
.root_src_path = root_src_path,
|
||||||
.module = module,
|
.llvm_module = llvm_module,
|
||||||
.context = context,
|
.context = context,
|
||||||
.builder = builder,
|
.builder = builder,
|
||||||
.target = target.*,
|
.target = target.*,
|
||||||
@ -248,7 +268,109 @@ pub const Module = struct {
|
|||||||
.exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),
|
.exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),
|
||||||
.build_group = event.Group(BuildError!void).init(loop),
|
.build_group = event.Group(BuildError!void).init(loop),
|
||||||
.compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)),
|
.compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)),
|
||||||
|
|
||||||
|
.meta_type = undefined,
|
||||||
|
.void_type = undefined,
|
||||||
|
.void_value = undefined,
|
||||||
|
.bool_type = undefined,
|
||||||
|
.true_value = undefined,
|
||||||
|
.false_value = undefined,
|
||||||
|
.noreturn_type = undefined,
|
||||||
|
.noreturn_value = undefined,
|
||||||
});
|
});
|
||||||
|
try module.initTypes();
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initTypes(module: *Module) !void {
|
||||||
|
module.meta_type = try module.a().create(Type.MetaType{
|
||||||
|
.base = Type{
|
||||||
|
.base = Value{
|
||||||
|
.id = Value.Id.Type,
|
||||||
|
.typeof = undefined,
|
||||||
|
.ref_count = 3, // 3 because it references itself twice
|
||||||
|
},
|
||||||
|
.id = builtin.TypeId.Type,
|
||||||
|
},
|
||||||
|
.value = undefined,
|
||||||
|
});
|
||||||
|
module.meta_type.value = &module.meta_type.base;
|
||||||
|
module.meta_type.base.base.typeof = &module.meta_type.base;
|
||||||
|
errdefer module.a().destroy(module.meta_type);
|
||||||
|
|
||||||
|
module.void_type = try module.a().create(Type.Void{
|
||||||
|
.base = Type{
|
||||||
|
.base = Value{
|
||||||
|
.id = Value.Id.Type,
|
||||||
|
.typeof = &Type.MetaType.get(module).base,
|
||||||
|
.ref_count = 1,
|
||||||
|
},
|
||||||
|
.id = builtin.TypeId.Void,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
errdefer module.a().destroy(module.void_type);
|
||||||
|
|
||||||
|
module.noreturn_type = try module.a().create(Type.NoReturn{
|
||||||
|
.base = Type{
|
||||||
|
.base = Value{
|
||||||
|
.id = Value.Id.Type,
|
||||||
|
.typeof = &Type.MetaType.get(module).base,
|
||||||
|
.ref_count = 1,
|
||||||
|
},
|
||||||
|
.id = builtin.TypeId.NoReturn,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
errdefer module.a().destroy(module.noreturn_type);
|
||||||
|
|
||||||
|
module.bool_type = try module.a().create(Type.Bool{
|
||||||
|
.base = Type{
|
||||||
|
.base = Value{
|
||||||
|
.id = Value.Id.Type,
|
||||||
|
.typeof = &Type.MetaType.get(module).base,
|
||||||
|
.ref_count = 1,
|
||||||
|
},
|
||||||
|
.id = builtin.TypeId.Bool,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
errdefer module.a().destroy(module.bool_type);
|
||||||
|
|
||||||
|
module.void_value = try module.a().create(Value.Void{
|
||||||
|
.base = Value{
|
||||||
|
.id = Value.Id.Void,
|
||||||
|
.typeof = &Type.Void.get(module).base,
|
||||||
|
.ref_count = 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
errdefer module.a().destroy(module.void_value);
|
||||||
|
|
||||||
|
module.true_value = try module.a().create(Value.Bool{
|
||||||
|
.base = Value{
|
||||||
|
.id = Value.Id.Bool,
|
||||||
|
.typeof = &Type.Bool.get(module).base,
|
||||||
|
.ref_count = 1,
|
||||||
|
},
|
||||||
|
.x = true,
|
||||||
|
});
|
||||||
|
errdefer module.a().destroy(module.true_value);
|
||||||
|
|
||||||
|
module.false_value = try module.a().create(Value.Bool{
|
||||||
|
.base = Value{
|
||||||
|
.id = Value.Id.Bool,
|
||||||
|
.typeof = &Type.Bool.get(module).base,
|
||||||
|
.ref_count = 1,
|
||||||
|
},
|
||||||
|
.x = false,
|
||||||
|
});
|
||||||
|
errdefer module.a().destroy(module.false_value);
|
||||||
|
|
||||||
|
module.noreturn_value = try module.a().create(Value.NoReturn{
|
||||||
|
.base = Value{
|
||||||
|
.id = Value.Id.NoReturn,
|
||||||
|
.typeof = &Type.NoReturn.get(module).base,
|
||||||
|
.ref_count = 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
errdefer module.a().destroy(module.noreturn_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump(self: *Module) void {
|
fn dump(self: *Module) void {
|
||||||
@ -256,9 +378,17 @@ pub const Module = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy(self: *Module) void {
|
pub fn destroy(self: *Module) void {
|
||||||
|
self.noreturn_value.base.deref(self);
|
||||||
|
self.void_value.base.deref(self);
|
||||||
|
self.false_value.base.deref(self);
|
||||||
|
self.true_value.base.deref(self);
|
||||||
|
self.noreturn_type.base.base.deref(self);
|
||||||
|
self.void_type.base.base.deref(self);
|
||||||
|
self.meta_type.base.base.deref(self);
|
||||||
|
|
||||||
self.events.destroy();
|
self.events.destroy();
|
||||||
c.LLVMDisposeBuilder(self.builder);
|
c.LLVMDisposeBuilder(self.builder);
|
||||||
c.LLVMDisposeModule(self.module);
|
c.LLVMDisposeModule(self.llvm_module);
|
||||||
c.LLVMContextDispose(self.context);
|
c.LLVMContextDispose(self.context);
|
||||||
self.name.deinit();
|
self.name.deinit();
|
||||||
|
|
||||||
@ -331,8 +461,8 @@ pub const Module = struct {
|
|||||||
const tree = &parsed_file.tree;
|
const tree = &parsed_file.tree;
|
||||||
|
|
||||||
// create empty struct for it
|
// create empty struct for it
|
||||||
const decls = try Scope.Decls.create(self.a(), null);
|
const decls = try Scope.Decls.create(self, null);
|
||||||
errdefer decls.destroy();
|
defer decls.base.deref(self);
|
||||||
|
|
||||||
var decl_group = event.Group(BuildError!void).init(self.loop);
|
var decl_group = event.Group(BuildError!void).init(self.loop);
|
||||||
errdefer decl_group.cancelAll();
|
errdefer decl_group.cancelAll();
|
||||||
@ -359,14 +489,17 @@ pub const Module = struct {
|
|||||||
.id = Decl.Id.Fn,
|
.id = Decl.Id.Fn,
|
||||||
.name = name,
|
.name = name,
|
||||||
.visib = parseVisibToken(tree, fn_proto.visib_token),
|
.visib = parseVisibToken(tree, fn_proto.visib_token),
|
||||||
.resolution = Decl.Resolution.Unresolved,
|
.resolution = event.Future(BuildError!void).init(self.loop),
|
||||||
|
.resolution_in_progress = 0,
|
||||||
|
.parsed_file = parsed_file,
|
||||||
|
.parent_scope = &decls.base,
|
||||||
},
|
},
|
||||||
.value = Decl.Fn.Val{ .Unresolved = {} },
|
.value = Decl.Fn.Val{ .Unresolved = {} },
|
||||||
.fn_proto = fn_proto,
|
.fn_proto = fn_proto,
|
||||||
});
|
});
|
||||||
errdefer self.a().destroy(fn_decl);
|
errdefer self.a().destroy(fn_decl);
|
||||||
|
|
||||||
try decl_group.call(addTopLevelDecl, self, parsed_file, &fn_decl.base);
|
try decl_group.call(addTopLevelDecl, self, &fn_decl.base);
|
||||||
},
|
},
|
||||||
ast.Node.Id.TestDecl => @panic("TODO"),
|
ast.Node.Id.TestDecl => @panic("TODO"),
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
@ -376,12 +509,12 @@ pub const Module = struct {
|
|||||||
try await (async self.build_group.wait() catch unreachable);
|
try await (async self.build_group.wait() catch unreachable);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn addTopLevelDecl(self: *Module, parsed_file: *ParsedFile, decl: *Decl) !void {
|
async fn addTopLevelDecl(self: *Module, decl: *Decl) !void {
|
||||||
const is_export = decl.isExported(&parsed_file.tree);
|
const is_export = decl.isExported(&decl.parsed_file.tree);
|
||||||
|
|
||||||
if (is_export) {
|
if (is_export) {
|
||||||
try self.build_group.call(verifyUniqueSymbol, self, parsed_file, decl);
|
try self.build_group.call(verifyUniqueSymbol, self, decl);
|
||||||
try self.build_group.call(generateDecl, self, parsed_file, decl);
|
try self.build_group.call(resolveDecl, self, decl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,36 +549,21 @@ pub const Module = struct {
|
|||||||
try compile_errors.value.append(msg);
|
try compile_errors.value.append(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn verifyUniqueSymbol(self: *Module, parsed_file: *ParsedFile, decl: *Decl) !void {
|
async fn verifyUniqueSymbol(self: *Module, decl: *Decl) !void {
|
||||||
const exported_symbol_names = await (async self.exported_symbol_names.acquire() catch unreachable);
|
const exported_symbol_names = await (async self.exported_symbol_names.acquire() catch unreachable);
|
||||||
defer exported_symbol_names.release();
|
defer exported_symbol_names.release();
|
||||||
|
|
||||||
if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
|
if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
|
||||||
try self.addCompileError(
|
try self.addCompileError(
|
||||||
parsed_file,
|
decl.parsed_file,
|
||||||
decl.getSpan(),
|
decl.getSpan(),
|
||||||
"exported symbol collision: '{}'",
|
"exported symbol collision: '{}'",
|
||||||
decl.name,
|
decl.name,
|
||||||
);
|
);
|
||||||
|
// TODO add error note showing location of other symbol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This declaration has been blessed as going into the final code generation.
|
|
||||||
async fn generateDecl(self: *Module, parsed_file: *ParsedFile, decl: *Decl) void {
|
|
||||||
switch (decl.id) {
|
|
||||||
Decl.Id.Var => @panic("TODO"),
|
|
||||||
Decl.Id.Fn => {
|
|
||||||
const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl);
|
|
||||||
return await (async self.generateDeclFn(parsed_file, fn_decl) catch unreachable);
|
|
||||||
},
|
|
||||||
Decl.Id.CompTime => @panic("TODO"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn generateDeclFn(self: *Module, parsed_file: *ParsedFile, fn_decl: *Decl.Fn) void {
|
|
||||||
fn_decl.value = Decl.Fn.Val{ .Ok = Value.Fn{} };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn link(self: *Module, out_file: ?[]const u8) !void {
|
pub fn link(self: *Module, out_file: ?[]const u8) !void {
|
||||||
warn("TODO link");
|
warn("TODO link");
|
||||||
return error.Todo;
|
return error.Todo;
|
||||||
@ -501,177 +619,48 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Scope = struct {
|
/// This declaration has been blessed as going into the final code generation.
|
||||||
id: Id,
|
pub async fn resolveDecl(module: *Module, decl: *Decl) !void {
|
||||||
parent: ?*Scope,
|
if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {
|
||||||
|
decl.resolution.data = await (async generateDecl(module, decl) catch unreachable);
|
||||||
pub const Id = enum {
|
decl.resolution.resolve();
|
||||||
Decls,
|
} else {
|
||||||
Block,
|
return (await (async decl.resolution.get() catch unreachable)).*;
|
||||||
};
|
|
||||||
|
|
||||||
pub const Decls = struct {
|
|
||||||
base: Scope,
|
|
||||||
table: Decl.Table,
|
|
||||||
|
|
||||||
pub fn create(a: *Allocator, parent: ?*Scope) !*Decls {
|
|
||||||
const self = try a.create(Decls{
|
|
||||||
.base = Scope{
|
|
||||||
.id = Id.Decls,
|
|
||||||
.parent = parent,
|
|
||||||
},
|
|
||||||
.table = undefined,
|
|
||||||
});
|
|
||||||
errdefer a.destroy(self);
|
|
||||||
|
|
||||||
self.table = Decl.Table.init(a);
|
|
||||||
errdefer self.table.deinit();
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn destroy(self: *Decls) void {
|
|
||||||
self.table.deinit();
|
|
||||||
self.table.allocator.destroy(self);
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Block = struct {
|
|
||||||
base: Scope,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Visib = enum {
|
|
||||||
Private,
|
|
||||||
Pub,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Decl = struct {
|
|
||||||
id: Id,
|
|
||||||
name: []const u8,
|
|
||||||
visib: Visib,
|
|
||||||
resolution: Resolution,
|
|
||||||
|
|
||||||
pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
|
|
||||||
|
|
||||||
pub fn isExported(base: *const Decl, tree: *ast.Tree) bool {
|
|
||||||
switch (base.id) {
|
|
||||||
Id.Fn => {
|
|
||||||
const fn_decl = @fieldParentPtr(Fn, "base", base);
|
|
||||||
return fn_decl.isExported(tree);
|
|
||||||
},
|
|
||||||
else => return false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getSpan(base: *const Decl) errmsg.Span {
|
/// The function that actually does the generation.
|
||||||
switch (base.id) {
|
async fn generateDecl(module: *Module, decl: *Decl) !void {
|
||||||
Id.Fn => {
|
switch (decl.id) {
|
||||||
const fn_decl = @fieldParentPtr(Fn, "base", base);
|
Decl.Id.Var => @panic("TODO"),
|
||||||
const fn_proto = fn_decl.fn_proto;
|
Decl.Id.Fn => {
|
||||||
const start = fn_proto.fn_token;
|
const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl);
|
||||||
const end = fn_proto.name_token orelse start;
|
return await (async generateDeclFn(module, fn_decl) catch unreachable);
|
||||||
return errmsg.Span{
|
},
|
||||||
.first = start,
|
Decl.Id.CompTime => @panic("TODO"),
|
||||||
.last = end + 1,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
else => @panic("TODO"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const Resolution = enum {
|
async fn generateDeclFn(module: *Module, fn_decl: *Decl.Fn) !void {
|
||||||
Unresolved,
|
const body_node = fn_decl.fn_proto.body_node orelse @panic("TODO extern fn proto decl");
|
||||||
InProgress,
|
|
||||||
Invalid,
|
|
||||||
Ok,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Id = enum {
|
const fndef_scope = try Scope.FnDef.create(module, fn_decl.base.parent_scope);
|
||||||
Var,
|
defer fndef_scope.base.deref(module);
|
||||||
Fn,
|
|
||||||
CompTime,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Var = struct {
|
const fn_type = try Type.Fn.create(module);
|
||||||
base: Decl,
|
defer fn_type.base.base.deref(module);
|
||||||
};
|
|
||||||
|
|
||||||
pub const Fn = struct {
|
const fn_val = try Value.Fn.create(module, fn_type, fndef_scope);
|
||||||
base: Decl,
|
defer fn_val.base.deref(module);
|
||||||
value: Val,
|
|
||||||
fn_proto: *const ast.Node.FnProto,
|
|
||||||
|
|
||||||
// TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
|
fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };
|
||||||
pub const Val = union {
|
|
||||||
Unresolved: void,
|
|
||||||
Ok: Value.Fn,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
|
const code = try await (async ir.gen(
|
||||||
return if (self.fn_proto.extern_export_inline_token) |tok_index| x: {
|
module,
|
||||||
const token = tree.tokens.at(tok_index);
|
body_node,
|
||||||
break :x switch (token.id) {
|
&fndef_scope.base,
|
||||||
Token.Id.Extern => tree.tokenSlicePtr(token),
|
fn_decl.base.parsed_file,
|
||||||
else => null,
|
) catch unreachable);
|
||||||
};
|
//code.dump();
|
||||||
} else null;
|
//try await (async irAnalyze(module, func) catch unreachable);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn isExported(self: Fn, tree: *ast.Tree) bool {
|
|
||||||
if (self.fn_proto.extern_export_inline_token) |tok_index| {
|
|
||||||
const token = tree.tokens.at(tok_index);
|
|
||||||
return token.id == Token.Id.Keyword_export;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const CompTime = struct {
|
|
||||||
base: Decl,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Value = struct {
|
|
||||||
pub const Fn = struct {};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Type = struct {
|
|
||||||
id: Id,
|
|
||||||
|
|
||||||
pub const Id = enum {
|
|
||||||
Type,
|
|
||||||
Void,
|
|
||||||
Bool,
|
|
||||||
NoReturn,
|
|
||||||
Int,
|
|
||||||
Float,
|
|
||||||
Pointer,
|
|
||||||
Array,
|
|
||||||
Struct,
|
|
||||||
ComptimeFloat,
|
|
||||||
ComptimeInt,
|
|
||||||
Undefined,
|
|
||||||
Null,
|
|
||||||
Optional,
|
|
||||||
ErrorUnion,
|
|
||||||
ErrorSet,
|
|
||||||
Enum,
|
|
||||||
Union,
|
|
||||||
Fn,
|
|
||||||
Opaque,
|
|
||||||
Promise,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Struct = struct {
|
|
||||||
base: Type,
|
|
||||||
decls: *Scope.Decls,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const ParsedFile = struct {
|
|
||||||
tree: ast.Tree,
|
|
||||||
realpath: []const u8,
|
|
||||||
};
|
|
||||||
|
6
src-self-hosted/parsed_file.zig
Normal file
6
src-self-hosted/parsed_file.zig
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
const ast = @import("std").zig.ast;
|
||||||
|
|
||||||
|
pub const ParsedFile = struct {
|
||||||
|
tree: ast.Tree,
|
||||||
|
realpath: []const u8,
|
||||||
|
};
|
@ -1,16 +1,234 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const Allocator = mem.Allocator;
|
||||||
|
const Decl = @import("decl.zig").Decl;
|
||||||
|
const Module = @import("module.zig").Module;
|
||||||
|
const mem = std.mem;
|
||||||
|
const ast = std.zig.ast;
|
||||||
|
const Value = @import("value.zig").Value;
|
||||||
|
const ir = @import("ir.zig");
|
||||||
|
|
||||||
pub const Scope = struct {
|
pub const Scope = struct {
|
||||||
id: Id,
|
id: Id,
|
||||||
parent: *Scope,
|
parent: ?*Scope,
|
||||||
|
ref_count: usize,
|
||||||
|
|
||||||
|
pub fn ref(base: *Scope) void {
|
||||||
|
base.ref_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deref(base: *Scope, module: *Module) void {
|
||||||
|
base.ref_count -= 1;
|
||||||
|
if (base.ref_count == 0) {
|
||||||
|
if (base.parent) |parent| parent.deref(module);
|
||||||
|
switch (base.id) {
|
||||||
|
Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(),
|
||||||
|
Id.Block => @fieldParentPtr(Block, "base", base).destroy(module),
|
||||||
|
Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(module),
|
||||||
|
Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(module),
|
||||||
|
Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(module),
|
||||||
|
Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(module),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn findFnDef(base: *Scope) ?*FnDef {
|
||||||
|
var scope = base;
|
||||||
|
while (true) {
|
||||||
|
switch (scope.id) {
|
||||||
|
Id.FnDef => return @fieldParentPtr(FnDef, "base", base),
|
||||||
|
Id.Decls => return null,
|
||||||
|
|
||||||
|
Id.Block,
|
||||||
|
Id.Defer,
|
||||||
|
Id.DeferExpr,
|
||||||
|
Id.CompTime,
|
||||||
|
=> scope = scope.parent orelse return null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const Id = enum {
|
pub const Id = enum {
|
||||||
Decls,
|
Decls,
|
||||||
Block,
|
Block,
|
||||||
Defer,
|
|
||||||
DeferExpr,
|
|
||||||
VarDecl,
|
|
||||||
CImport,
|
|
||||||
Loop,
|
|
||||||
FnDef,
|
FnDef,
|
||||||
CompTime,
|
CompTime,
|
||||||
|
Defer,
|
||||||
|
DeferExpr,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Decls = struct {
|
||||||
|
base: Scope,
|
||||||
|
table: Decl.Table,
|
||||||
|
|
||||||
|
/// Creates a Decls scope with 1 reference
|
||||||
|
pub fn create(module: *Module, parent: ?*Scope) !*Decls {
|
||||||
|
const self = try module.a().create(Decls{
|
||||||
|
.base = Scope{
|
||||||
|
.id = Id.Decls,
|
||||||
|
.parent = parent,
|
||||||
|
.ref_count = 1,
|
||||||
|
},
|
||||||
|
.table = undefined,
|
||||||
|
});
|
||||||
|
errdefer module.a().destroy(self);
|
||||||
|
|
||||||
|
self.table = Decl.Table.init(module.a());
|
||||||
|
errdefer self.table.deinit();
|
||||||
|
|
||||||
|
if (parent) |p| p.ref();
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *Decls) void {
|
||||||
|
self.table.deinit();
|
||||||
|
self.table.allocator.destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Block = struct {
|
||||||
|
base: Scope,
|
||||||
|
incoming_values: std.ArrayList(*ir.Instruction),
|
||||||
|
incoming_blocks: std.ArrayList(*ir.BasicBlock),
|
||||||
|
end_block: *ir.BasicBlock,
|
||||||
|
is_comptime: *ir.Instruction,
|
||||||
|
|
||||||
|
/// Creates a Block scope with 1 reference
|
||||||
|
pub fn create(module: *Module, parent: ?*Scope) !*Block {
|
||||||
|
const self = try module.a().create(Block{
|
||||||
|
.base = Scope{
|
||||||
|
.id = Id.Block,
|
||||||
|
.parent = parent,
|
||||||
|
.ref_count = 1,
|
||||||
|
},
|
||||||
|
.incoming_values = undefined,
|
||||||
|
.incoming_blocks = undefined,
|
||||||
|
.end_block = undefined,
|
||||||
|
.is_comptime = undefined,
|
||||||
|
});
|
||||||
|
errdefer module.a().destroy(self);
|
||||||
|
|
||||||
|
if (parent) |p| p.ref();
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *Block, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const FnDef = struct {
|
||||||
|
base: Scope,
|
||||||
|
|
||||||
|
/// This reference is not counted so that the scope can get destroyed with the function
|
||||||
|
fn_val: *Value.Fn,
|
||||||
|
|
||||||
|
/// Creates a FnDef scope with 1 reference
|
||||||
|
/// Must set the fn_val later
|
||||||
|
pub fn create(module: *Module, parent: ?*Scope) !*FnDef {
|
||||||
|
const self = try module.a().create(FnDef{
|
||||||
|
.base = Scope{
|
||||||
|
.id = Id.FnDef,
|
||||||
|
.parent = parent,
|
||||||
|
.ref_count = 1,
|
||||||
|
},
|
||||||
|
.fn_val = undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (parent) |p| p.ref();
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *FnDef, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const CompTime = struct {
|
||||||
|
base: Scope,
|
||||||
|
|
||||||
|
/// Creates a CompTime scope with 1 reference
|
||||||
|
pub fn create(module: *Module, parent: ?*Scope) !*CompTime {
|
||||||
|
const self = try module.a().create(CompTime{
|
||||||
|
.base = Scope{
|
||||||
|
.id = Id.CompTime,
|
||||||
|
.parent = parent,
|
||||||
|
.ref_count = 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (parent) |p| p.ref();
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *CompTime, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Defer = struct {
|
||||||
|
base: Scope,
|
||||||
|
defer_expr_scope: *DeferExpr,
|
||||||
|
kind: Kind,
|
||||||
|
|
||||||
|
pub const Kind = enum {
|
||||||
|
ScopeExit,
|
||||||
|
ErrorExit,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Creates a Defer scope with 1 reference
|
||||||
|
pub fn create(
|
||||||
|
module: *Module,
|
||||||
|
parent: ?*Scope,
|
||||||
|
kind: Kind,
|
||||||
|
defer_expr_scope: *DeferExpr,
|
||||||
|
) !*Defer {
|
||||||
|
const self = try module.a().create(Defer{
|
||||||
|
.base = Scope{
|
||||||
|
.id = Id.Defer,
|
||||||
|
.parent = parent,
|
||||||
|
.ref_count = 1,
|
||||||
|
},
|
||||||
|
.defer_expr_scope = defer_expr_scope,
|
||||||
|
.kind = kind,
|
||||||
|
});
|
||||||
|
errdefer module.a().destroy(self);
|
||||||
|
|
||||||
|
defer_expr_scope.base.ref();
|
||||||
|
|
||||||
|
if (parent) |p| p.ref();
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *Defer, module: *Module) void {
|
||||||
|
self.defer_expr_scope.base.deref(module);
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DeferExpr = struct {
|
||||||
|
base: Scope,
|
||||||
|
expr_node: *ast.Node,
|
||||||
|
|
||||||
|
/// Creates a DeferExpr scope with 1 reference
|
||||||
|
pub fn create(module: *Module, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr {
|
||||||
|
const self = try module.a().create(DeferExpr{
|
||||||
|
.base = Scope{
|
||||||
|
.id = Id.DeferExpr,
|
||||||
|
.parent = parent,
|
||||||
|
.ref_count = 1,
|
||||||
|
},
|
||||||
|
.expr_node = expr_node,
|
||||||
|
});
|
||||||
|
errdefer module.a().destroy(self);
|
||||||
|
|
||||||
|
if (parent) |p| p.ref();
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *DeferExpr, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
268
src-self-hosted/type.zig
Normal file
268
src-self-hosted/type.zig
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
const builtin = @import("builtin");
|
||||||
|
const Scope = @import("scope.zig").Scope;
|
||||||
|
const Module = @import("module.zig").Module;
|
||||||
|
const Value = @import("value.zig").Value;
|
||||||
|
|
||||||
|
pub const Type = struct {
|
||||||
|
base: Value,
|
||||||
|
id: Id,
|
||||||
|
|
||||||
|
pub const Id = builtin.TypeId;
|
||||||
|
|
||||||
|
pub fn destroy(base: *Type, module: *Module) void {
|
||||||
|
switch (base.id) {
|
||||||
|
Id.Struct => @fieldParentPtr(Struct, "base", base).destroy(module),
|
||||||
|
Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(module),
|
||||||
|
Id.Type => @fieldParentPtr(MetaType, "base", base).destroy(module),
|
||||||
|
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.Int => @fieldParentPtr(Int, "base", base).destroy(module),
|
||||||
|
Id.Float => @fieldParentPtr(Float, "base", base).destroy(module),
|
||||||
|
Id.Pointer => @fieldParentPtr(Pointer, "base", base).destroy(module),
|
||||||
|
Id.Array => @fieldParentPtr(Array, "base", base).destroy(module),
|
||||||
|
Id.ComptimeFloat => @fieldParentPtr(ComptimeFloat, "base", base).destroy(module),
|
||||||
|
Id.ComptimeInt => @fieldParentPtr(ComptimeInt, "base", base).destroy(module),
|
||||||
|
Id.Undefined => @fieldParentPtr(Undefined, "base", base).destroy(module),
|
||||||
|
Id.Null => @fieldParentPtr(Null, "base", base).destroy(module),
|
||||||
|
Id.Optional => @fieldParentPtr(Optional, "base", base).destroy(module),
|
||||||
|
Id.ErrorUnion => @fieldParentPtr(ErrorUnion, "base", base).destroy(module),
|
||||||
|
Id.ErrorSet => @fieldParentPtr(ErrorSet, "base", base).destroy(module),
|
||||||
|
Id.Enum => @fieldParentPtr(Enum, "base", base).destroy(module),
|
||||||
|
Id.Union => @fieldParentPtr(Union, "base", base).destroy(module),
|
||||||
|
Id.Namespace => @fieldParentPtr(Namespace, "base", base).destroy(module),
|
||||||
|
Id.Block => @fieldParentPtr(Block, "base", base).destroy(module),
|
||||||
|
Id.BoundFn => @fieldParentPtr(BoundFn, "base", base).destroy(module),
|
||||||
|
Id.ArgTuple => @fieldParentPtr(ArgTuple, "base", base).destroy(module),
|
||||||
|
Id.Opaque => @fieldParentPtr(Opaque, "base", base).destroy(module),
|
||||||
|
Id.Promise => @fieldParentPtr(Promise, "base", base).destroy(module),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Struct = struct {
|
||||||
|
base: Type,
|
||||||
|
decls: *Scope.Decls,
|
||||||
|
|
||||||
|
pub fn destroy(self: *Struct, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Fn = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn create(module: *Module) !*Fn {
|
||||||
|
return module.a().create(Fn{
|
||||||
|
.base = Type{
|
||||||
|
.base = Value{
|
||||||
|
.id = Value.Id.Type,
|
||||||
|
.typeof = &MetaType.get(module).base,
|
||||||
|
.ref_count = 1,
|
||||||
|
},
|
||||||
|
.id = builtin.TypeId.Fn,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *Fn, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const MetaType = struct {
|
||||||
|
base: Type,
|
||||||
|
value: *Type,
|
||||||
|
|
||||||
|
/// Adds 1 reference to the resulting type
|
||||||
|
pub fn get(module: *Module) *MetaType {
|
||||||
|
module.meta_type.base.base.ref();
|
||||||
|
return module.meta_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *MetaType, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Void = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
/// Adds 1 reference to the resulting type
|
||||||
|
pub fn get(module: *Module) *Void {
|
||||||
|
module.void_type.base.base.ref();
|
||||||
|
return module.void_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *Void, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Bool = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
/// Adds 1 reference to the resulting type
|
||||||
|
pub fn get(module: *Module) *Bool {
|
||||||
|
module.bool_type.base.base.ref();
|
||||||
|
return module.bool_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *Bool, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const NoReturn = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
/// Adds 1 reference to the resulting type
|
||||||
|
pub fn get(module: *Module) *NoReturn {
|
||||||
|
module.noreturn_type.base.base.ref();
|
||||||
|
return module.noreturn_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *NoReturn, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Int = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *Int, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Float = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *Float, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub const Pointer = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *Pointer, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub const Array = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *Array, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub const ComptimeFloat = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *ComptimeFloat, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub const ComptimeInt = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *ComptimeInt, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub const Undefined = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *Undefined, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub const Null = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *Null, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub const Optional = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *Optional, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub const ErrorUnion = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *ErrorUnion, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub const ErrorSet = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *ErrorSet, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub const Enum = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *Enum, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub const Union = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *Union, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub const Namespace = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *Namespace, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Block = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *Block, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const BoundFn = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *BoundFn, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ArgTuple = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *ArgTuple, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Opaque = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *Opaque, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Promise = struct {
|
||||||
|
base: Type,
|
||||||
|
|
||||||
|
pub fn destroy(self: *Promise, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
125
src-self-hosted/value.zig
Normal file
125
src-self-hosted/value.zig
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const Scope = @import("scope.zig").Scope;
|
||||||
|
const Module = @import("module.zig").Module;
|
||||||
|
|
||||||
|
/// Values are ref-counted, heap-allocated, and copy-on-write
|
||||||
|
/// If there is only 1 ref then write need not copy
|
||||||
|
pub const Value = struct {
|
||||||
|
id: Id,
|
||||||
|
typeof: *Type,
|
||||||
|
ref_count: usize,
|
||||||
|
|
||||||
|
pub fn ref(base: *Value) void {
|
||||||
|
base.ref_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deref(base: *Value, module: *Module) void {
|
||||||
|
base.ref_count -= 1;
|
||||||
|
if (base.ref_count == 0) {
|
||||||
|
base.typeof.base.deref(module);
|
||||||
|
switch (base.id) {
|
||||||
|
Id.Type => @fieldParentPtr(Type, "base", base).destroy(module),
|
||||||
|
Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(module),
|
||||||
|
Id.Void => @fieldParentPtr(Void, "base", base).destroy(module),
|
||||||
|
Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(module),
|
||||||
|
Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(module),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dump(base: *const Value) void {
|
||||||
|
std.debug.warn("{}", @tagName(base.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Id = enum {
|
||||||
|
Type,
|
||||||
|
Fn,
|
||||||
|
Void,
|
||||||
|
Bool,
|
||||||
|
NoReturn,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Type = @import("type.zig").Type;
|
||||||
|
|
||||||
|
pub const Fn = struct {
|
||||||
|
base: Value,
|
||||||
|
|
||||||
|
/// parent should be the top level decls or container decls
|
||||||
|
fndef_scope: *Scope.FnDef,
|
||||||
|
|
||||||
|
/// parent is scope for last parameter
|
||||||
|
child_scope: *Scope,
|
||||||
|
|
||||||
|
/// parent is child_scope
|
||||||
|
block_scope: *Scope.Block,
|
||||||
|
|
||||||
|
/// Creates a Fn value with 1 ref
|
||||||
|
pub fn create(module: *Module, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef) !*Fn {
|
||||||
|
const self = try module.a().create(Fn{
|
||||||
|
.base = Value{
|
||||||
|
.id = Value.Id.Fn,
|
||||||
|
.typeof = &fn_type.base,
|
||||||
|
.ref_count = 1,
|
||||||
|
},
|
||||||
|
.fndef_scope = fndef_scope,
|
||||||
|
.child_scope = &fndef_scope.base,
|
||||||
|
.block_scope = undefined,
|
||||||
|
});
|
||||||
|
fn_type.base.base.ref();
|
||||||
|
fndef_scope.fn_val = self;
|
||||||
|
fndef_scope.base.ref();
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *Fn, module: *Module) void {
|
||||||
|
self.fndef_scope.base.deref(module);
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Void = struct {
|
||||||
|
base: Value,
|
||||||
|
|
||||||
|
pub fn get(module: *Module) *Void {
|
||||||
|
module.void_value.base.ref();
|
||||||
|
return module.void_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *Void, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Bool = struct {
|
||||||
|
base: Value,
|
||||||
|
x: bool,
|
||||||
|
|
||||||
|
pub fn get(module: *Module, x: bool) *Bool {
|
||||||
|
if (x) {
|
||||||
|
module.true_value.base.ref();
|
||||||
|
return module.true_value;
|
||||||
|
} else {
|
||||||
|
module.false_value.base.ref();
|
||||||
|
return module.false_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *Bool, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const NoReturn = struct {
|
||||||
|
base: Value,
|
||||||
|
|
||||||
|
pub fn get(module: *Module) *NoReturn {
|
||||||
|
module.noreturn_value.base.ref();
|
||||||
|
return module.noreturn_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *NoReturn, module: *Module) void {
|
||||||
|
module.a().destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
4
src-self-hosted/visib.zig
Normal file
4
src-self-hosted/visib.zig
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub const Visib = enum {
|
||||||
|
Private,
|
||||||
|
Pub,
|
||||||
|
};
|
@ -57,7 +57,7 @@ test "std.event.Future" {
|
|||||||
const allocator = &da.allocator;
|
const allocator = &da.allocator;
|
||||||
|
|
||||||
var loop: Loop = undefined;
|
var loop: Loop = undefined;
|
||||||
try loop.initSingleThreaded(allocator);
|
try loop.initMultiThreaded(allocator);
|
||||||
defer loop.deinit();
|
defer loop.deinit();
|
||||||
|
|
||||||
const handle = try async<allocator> testFuture(&loop);
|
const handle = try async<allocator> testFuture(&loop);
|
||||||
|
@ -970,14 +970,8 @@ pub const Node = struct {
|
|||||||
pub const Defer = struct {
|
pub const Defer = struct {
|
||||||
base: Node,
|
base: Node,
|
||||||
defer_token: TokenIndex,
|
defer_token: TokenIndex,
|
||||||
kind: Kind,
|
|
||||||
expr: *Node,
|
expr: *Node,
|
||||||
|
|
||||||
const Kind = enum {
|
|
||||||
Error,
|
|
||||||
Unconditional,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn iterate(self: *Defer, index: usize) ?*Node {
|
pub fn iterate(self: *Defer, index: usize) ?*Node {
|
||||||
var i = index;
|
var i = index;
|
||||||
|
|
||||||
|
@ -1041,11 +1041,6 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
|
|||||||
const node = try arena.create(ast.Node.Defer{
|
const node = try arena.create(ast.Node.Defer{
|
||||||
.base = ast.Node{ .id = ast.Node.Id.Defer },
|
.base = ast.Node{ .id = ast.Node.Id.Defer },
|
||||||
.defer_token = token_index,
|
.defer_token = token_index,
|
||||||
.kind = switch (token_ptr.id) {
|
|
||||||
Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional,
|
|
||||||
Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error,
|
|
||||||
else => unreachable,
|
|
||||||
},
|
|
||||||
.expr = undefined,
|
.expr = undefined,
|
||||||
});
|
});
|
||||||
const node_ptr = try block.statements.addOne();
|
const node_ptr = try block.statements.addOne();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user