rework types and values data layout

master
Andrew Kelley 2020-04-18 19:41:45 -04:00
parent f92ccf365b
commit 018daa028e
3 changed files with 200 additions and 1087 deletions

View File

@ -2,6 +2,7 @@ const std = @import("std");
const mem = std.mem;
const Allocator = std.mem.Allocator;
const Value = @import("value.zig").Value;
const Type = @import("type.zig").Type;
const assert = std.debug.assert;
pub const Inst = struct {
@ -17,6 +18,7 @@ pub const Inst = struct {
Fn,
};
/// These names are used for the IR text format.
pub const Tag = enum {
constant,
ptrtoint,
@ -32,9 +34,11 @@ pub const Inst = struct {
/// a memory location for the value to survive after a const instruction.
pub const Constant = struct {
base: Inst = Inst{ .tag = .constant },
value: *Value,
ty: Type,
positionals: struct {},
positionals: struct {
value: Value,
},
kw_args: struct {},
};
@ -241,6 +245,31 @@ fn parseInstructionGeneric(ctx: *ParseContext, comptime fn_name: []const u8, com
}
fn parseParameterGeneric(ctx: *ParseContext, comptime T: type) !T {
if (@typeInfo(T) == .Enum) {
const start = ctx.i;
while (ctx.i < ctx.source.len) : (ctx.i += 1) switch (ctx.source[ctx.i]) {
' ', '\n', ',', ')' => {
const enum_name = ctx.source[start..ctx.i];
ctx.i += 1;
return std.meta.stringToEnum(T, enum_name) orelse {
return parseError(ctx, "tag '{}' not a member of enum '{}'", .{ enum_name, @typeName(T) });
};
},
else => continue,
};
return parseError(ctx, "unexpected EOF in enum parameter", .{});
}
switch (T) {
Inst.Fn.Body => {
var instructions = std.ArrayList(*Inst).init(ctx.allocator);
try requireEatBytes(ctx, "{");
return T{
.instructions = instructions.toOwnedSlice(),
};
},
Value => return parseError(ctx, "TODO implement parseParameterGeneric for type Value", .{}),
else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
}
return parseError(ctx, "TODO parse parameter {}", .{@typeName(T)});
}
@ -261,12 +290,18 @@ fn parseStringLiteralConst(ctx: *ParseContext) !*Inst {
},
else => |e| return e,
};
const bytes_val = try ctx.allocator.create(Value.Bytes);
bytes_val.* = .{ .data = parsed };
const bytes_payload = try ctx.allocator.create(Value.Payload.Bytes);
errdefer ctx.allocator.destroy(bytes_payload);
bytes_payload.* = .{ .data = parsed };
const ty_payload = try ctx.allocator.create(Type.Payload.Array_u8_Sentinel0);
errdefer ctx.allocator.destroy(ty_payload);
ty_payload.* = .{ .len = parsed.len };
const const_inst = try ctx.allocator.create(Inst.Constant);
const_inst.* = .{
.value = &bytes_val.base,
.positionals = .{},
.ty = Type.initPayload(&ty_payload.base),
.positionals = .{ .value = Value.initPayload(&bytes_payload.base) },
.kw_args = .{},
};
return &const_inst.base;

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,35 @@
const std = @import("std");
const Type = @import("type.zig").Type;
const log2 = std.math.log2;
const assert = std.debug.assert;
/// This is the raw data, with no bookkeeping, no memory awareness,
/// no de-duplication, and no type system awareness.
/// It's important for this struct to be small.
/// It is not copyable since it may contain references to its inner data.
pub const Value = struct {
tag: Tag,
/// This union takes advantage of the fact that the first page of memory
/// is unmapped, giving us 4096 possible enum tags that have no payload.
pub const Value = extern union {
/// If the tag value is less than Tag.no_payload_count, then no pointer
/// dereference is needed.
tag_if_small_enough: usize,
ptr_otherwise: *Payload,
pub const Tag = enum {
// The first section of this enum are tags that require no payload.
void_type,
noreturn_type,
bool_type,
usize_type,
void_value,
noreturn_value,
bool_true,
bool_false,
// Bump this when adding items above.
pub const last_no_payload_tag = Tag.bool_false;
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
// After this, the tag requires a payload.
array_sentinel_0_u8_type,
single_const_ptr_type,
ty,
int_u64,
int_i64,
function,
@ -28,44 +37,76 @@ pub const Value = struct {
bytes,
};
pub const Int_u64 = struct {
base: Value = Value{ .tag = .int_u64 },
int: u64,
};
pub fn initTag(comptime tag: Tag) Value {
comptime assert(@enumToInt(tag) < Tag.no_payload_count);
return .{ .tag_if_small_enough = @enumToInt(tag) };
}
pub const Int_i64 = struct {
base: Value = Value{ .tag = .int_i64 },
int: i64,
};
pub fn initPayload(payload: *Payload) Value {
assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
return .{ .ptr_otherwise = payload };
}
pub const Function = struct {
base: Value = Value{ .tag = .function },
};
pub fn tag(self: Value) Tag {
if (self.tag_if_small_enough < Tag.no_payload_count) {
return @intToEnum(self.tag_if_small_enough);
} else {
return self.ptr_otherwise.tag;
}
}
pub const ArraySentinel0_u8_Type = struct {
base: Value = Value{ .tag = .array_sentinel_0_u8_type },
len: u64,
};
/// This type is not copyable since it may contain pointers to its inner data.
pub const Payload = struct {
tag: Tag,
pub const SingleConstPtrType = struct {
base: Value = Value{ .tag = .single_const_ptr_type },
elem_type: *Value,
};
pub const Int_u64 = struct {
base: Payload = Payload{ .tag = .int_u64 },
int: u64,
};
pub const Ref = struct {
base: Value = Value{ .tag = .ref },
pointee: *MemoryCell,
};
pub const Int_i64 = struct {
base: Payload = Payload{ .tag = .int_i64 },
int: i64,
};
pub const Bytes = struct {
base: Value = Value{ .tag = .bytes },
data: []u8,
pub const Function = struct {
base: Payload = Payload{ .tag = .function },
};
pub const ArraySentinel0_u8_Type = struct {
base: Payload = Payload{ .tag = .array_sentinel_0_u8_type },
len: u64,
};
pub const SingleConstPtrType = struct {
base: Payload = Payload{ .tag = .single_const_ptr_type },
elem_type: *Type,
};
pub const Ref = struct {
base: Payload = Payload{ .tag = .ref },
pointee: *MemoryCell,
};
pub const Bytes = struct {
base: Payload = Payload{ .tag = .bytes },
data: []u8,
};
pub const Ty = struct {
base: Payload = Payload{ .tag = .fully_qualified_type },
ptr: *Type,
};
};
};
/// This is the heart of resource management of the Zig compiler. The Zig compiler uses
/// stop-the-world mark-and-sweep garbage collection during compilation to manage the resources
/// associated with evaluating compile-time code and semantic analysis. Each `MemoryCell` represents
/// a root.
pub const MemoryCell = struct {
parent: Parent,
contents: *Value,
contents: Value,
pub const Parent = union(enum) {
none,