rework types and values data layout
parent
f92ccf365b
commit
018daa028e
|
@ -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
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue