self-hosted: implement Decl lookup

* Take advantage of coercing anonymous struct literals to struct types.
 * Reworks Module to favor Zig source as the primary use case.
   Breaks ZIR compilation, which will have to be restored in a future commit.
 * Decl uses src_index rather then src, pointing to an AST Decl node
   index, or ZIR Module Decl index, rather than a byte offset.
 * ZIR instructions have an `analyzed_inst` field instead of Module
   having a hash table.
 * Module.Fn loses the `fn_type` field since it is redundant with
   its `owner_decl` `TypedValue` type.
 * Implement Type and Value copying. A ZIR Const instruction's TypedValue
   is copied to the Decl arena during analysis, which allows freeing the
   ZIR text instructions post-analysis.
 * Don't flush the ELF file if there are compilation errors.
 * Function return types allow arbitrarily complex expressions.
 * AST->ZIR for function calls and return statements.
This commit is contained in:
Andrew Kelley 2020-06-17 04:29:54 -04:00
parent b4eac0414a
commit 7e58c56ca7
10 changed files with 866 additions and 429 deletions

View File

@ -2260,6 +2260,8 @@ pub const Node = struct {
}
};
/// TODO break this into separate Break, Continue, Return AST Nodes to save memory.
/// Could be further broken into LabeledBreak, LabeledContinue, and ReturnVoid to save even more.
pub const ControlFlowExpression = struct {
base: Node = Node{ .id = .ControlFlowExpression },
ltoken: TokenIndex,

File diff suppressed because it is too large Load Diff

View File

@ -21,3 +21,11 @@ pub const Managed = struct {
self.* = undefined;
}
};
/// Assumes arena allocation. Does a recursive copy.
pub fn copy(self: TypedValue, allocator: *Allocator) error{OutOfMemory}!TypedValue {
return TypedValue{
.ty = try self.ty.copy(allocator),
.val = try self.val.copy(allocator),
};
}

View File

@ -178,6 +178,7 @@ const Function = struct {
.ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
.bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
.ret => return self.genRet(inst.cast(ir.Inst.Ret).?),
.retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?),
.cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?),
.condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?),
.isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?),
@ -213,7 +214,7 @@ const Function = struct {
try self.code.resize(self.code.items.len + 7);
self.code.items[self.code.items.len - 7 ..][0..3].* = [3]u8{ 0xff, 0x14, 0x25 };
mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], got_addr);
const return_type = func.fn_type.fnReturnType();
const return_type = func.owner_decl.typed_value.most_recent.typed_value.ty.fnReturnType();
switch (return_type.zigTypeTag()) {
.Void => return MCValue{ .none = {} },
.NoReturn => return MCValue{ .unreach = {} },
@ -230,16 +231,28 @@ const Function = struct {
}
}
fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue {
fn ret(self: *Function, src: usize, mcv: MCValue) !MCValue {
if (mcv != .none) {
return self.fail(src, "TODO implement return with non-void operand", .{});
}
switch (self.target.cpu.arch) {
.i386, .x86_64 => {
try self.code.append(0xc3); // ret
},
else => return self.fail(inst.base.src, "TODO implement return for {}", .{self.target.cpu.arch}),
else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
}
return .unreach;
}
fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue {
const operand = try self.resolveInst(inst.args.operand);
return self.ret(inst.base.src, operand);
}
fn genRetVoid(self: *Function, inst: *ir.Inst.RetVoid) !MCValue {
return self.ret(inst.base.src, .none);
}
fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue {
switch (self.target.cpu.arch) {
else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),

View File

@ -26,6 +26,7 @@ pub const Inst = struct {
isnull,
ptrtoint,
ret,
retvoid,
unreach,
};
@ -146,6 +147,14 @@ pub const Inst = struct {
pub const Ret = struct {
pub const base_tag = Tag.ret;
base: Inst,
args: struct {
operand: *Inst,
},
};
pub const RetVoid = struct {
pub const base_tag = Tag.retvoid;
base: Inst,
args: void,
};

View File

@ -956,10 +956,10 @@ pub const ElfFile = struct {
try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
if (self.local_symbol_free_list.popOrNull()) |i| {
//std.debug.warn("reusing symbol index {} for {}\n", .{i, decl.name});
std.debug.warn("reusing symbol index {} for {}\n", .{i, decl.name});
decl.link.local_sym_index = i;
} else {
//std.debug.warn("allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
std.debug.warn("allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
_ = self.local_symbols.addOneAssumeCapacity();
}
@ -1002,7 +1002,7 @@ pub const ElfFile = struct {
defer code_buffer.deinit();
const typed_value = decl.typed_value.most_recent.typed_value;
const code = switch (try codegen.generateSymbol(self, decl.src, typed_value, &code_buffer)) {
const code = switch (try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer)) {
.externally_managed => |x| x,
.appended => code_buffer.items,
.fail => |em| {
@ -1027,11 +1027,11 @@ pub const ElfFile = struct {
!mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
if (need_realloc) {
const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
//std.debug.warn("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
std.debug.warn("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
if (vaddr != local_sym.st_value) {
local_sym.st_value = vaddr;
//std.debug.warn(" (writing new offset table entry)\n", .{});
std.debug.warn(" (writing new offset table entry)\n", .{});
self.offset_table.items[decl.link.offset_table_index] = vaddr;
try self.writeOffsetTableEntry(decl.link.offset_table_index);
}
@ -1049,7 +1049,7 @@ pub const ElfFile = struct {
const decl_name = mem.spanZ(decl.name);
const name_str_index = try self.makeString(decl_name);
const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
//std.debug.warn("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
std.debug.warn("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
errdefer self.freeTextBlock(&decl.link);
local_sym.* = .{

View File

@ -54,6 +54,7 @@ pub const Type = extern union {
.@"undefined" => return .Undefined,
.fn_noreturn_no_args => return .Fn,
.fn_void_no_args => return .Fn,
.fn_naked_noreturn_no_args => return .Fn,
.fn_ccc_void_no_args => return .Fn,
@ -163,6 +164,77 @@ pub const Type = extern union {
}
}
pub fn copy(self: Type, allocator: *Allocator) error{OutOfMemory}!Type {
if (self.tag_if_small_enough < Tag.no_payload_count) {
return Type{ .tag_if_small_enough = self.tag_if_small_enough };
} else switch (self.ptr_otherwise.tag) {
.u8,
.i8,
.isize,
.usize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.c_longdouble,
.c_void,
.f16,
.f32,
.f64,
.f128,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
=> unreachable,
.array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0),
.array => {
const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.Array);
new_payload.* = .{
.base = payload.base,
.len = payload.len,
.elem_type = try payload.elem_type.copy(allocator),
};
return Type{ .ptr_otherwise = &new_payload.base };
},
.single_const_pointer => {
const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.SingleConstPointer);
new_payload.* = .{
.base = payload.base,
.pointee_type = try payload.pointee_type.copy(allocator),
};
return Type{ .ptr_otherwise = &new_payload.base };
},
.int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),
.int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),
}
}
fn copyPayloadShallow(self: Type, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Type {
const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
const new_payload = try allocator.create(T);
new_payload.* = payload.*;
return Type{ .ptr_otherwise = &new_payload.base };
}
pub fn format(
self: Type,
comptime fmt: []const u8,
@ -206,6 +278,7 @@ pub const Type = extern union {
.const_slice_u8 => return out_stream.writeAll("[]const u8"),
.fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
.fn_void_no_args => return out_stream.writeAll("fn() void"),
.fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
.fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"),
.single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
@ -269,6 +342,7 @@ pub const Type = extern union {
.@"null" => return Value.initTag(.null_type),
.@"undefined" => return Value.initTag(.undefined_type),
.fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type),
.fn_void_no_args => return Value.initTag(.fn_void_no_args_type),
.fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
.fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),
.single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
@ -303,6 +377,7 @@ pub const Type = extern union {
.bool,
.anyerror,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer_to_comptime_int,
@ -333,6 +408,7 @@ pub const Type = extern union {
.i8,
.bool,
.fn_noreturn_no_args, // represents machine code; not a pointer
.fn_void_no_args, // represents machine code; not a pointer
.fn_naked_noreturn_no_args, // represents machine code; not a pointer
.fn_ccc_void_no_args, // represents machine code; not a pointer
.array_u8_sentinel_0,
@ -420,6 +496,7 @@ pub const Type = extern union {
.array_u8_sentinel_0,
.const_slice_u8,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.int_unsigned,
@ -466,6 +543,7 @@ pub const Type = extern union {
.single_const_pointer,
.single_const_pointer_to_comptime_int,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.int_unsigned,
@ -509,6 +587,7 @@ pub const Type = extern union {
.array,
.array_u8_sentinel_0,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.int_unsigned,
@ -553,6 +632,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.int_unsigned,
@ -597,6 +677,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer,
@ -642,6 +723,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer,
@ -675,6 +757,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.array,
@ -721,6 +804,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.array,
@ -777,6 +861,7 @@ pub const Type = extern union {
pub fn fnParamLen(self: Type) usize {
return switch (self.tag()) {
.fn_noreturn_no_args => 0,
.fn_void_no_args => 0,
.fn_naked_noreturn_no_args => 0,
.fn_ccc_void_no_args => 0,
@ -823,6 +908,7 @@ pub const Type = extern union {
pub fn fnParamTypes(self: Type, types: []Type) void {
switch (self.tag()) {
.fn_noreturn_no_args => return,
.fn_void_no_args => return,
.fn_naked_noreturn_no_args => return,
.fn_ccc_void_no_args => return,
@ -869,7 +955,10 @@ pub const Type = extern union {
return switch (self.tag()) {
.fn_noreturn_no_args => Type.initTag(.noreturn),
.fn_naked_noreturn_no_args => Type.initTag(.noreturn),
.fn_ccc_void_no_args => Type.initTag(.void),
.fn_void_no_args,
.fn_ccc_void_no_args,
=> Type.initTag(.void),
.f16,
.f32,
@ -913,6 +1002,7 @@ pub const Type = extern union {
pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {
return switch (self.tag()) {
.fn_noreturn_no_args => .Unspecified,
.fn_void_no_args => .Unspecified,
.fn_naked_noreturn_no_args => .Naked,
.fn_ccc_void_no_args => .C,
@ -958,6 +1048,7 @@ pub const Type = extern union {
pub fn fnIsVarArgs(self: Type) bool {
return switch (self.tag()) {
.fn_noreturn_no_args => false,
.fn_void_no_args => false,
.fn_naked_noreturn_no_args => false,
.fn_ccc_void_no_args => false,
@ -1033,6 +1124,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.array,
@ -1070,6 +1162,7 @@ pub const Type = extern union {
.type,
.anyerror,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer_to_comptime_int,
@ -1126,6 +1219,7 @@ pub const Type = extern union {
.type,
.anyerror,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer_to_comptime_int,
@ -1180,6 +1274,7 @@ pub const Type = extern union {
@"null",
@"undefined",
fn_noreturn_no_args,
fn_void_no_args,
fn_naked_noreturn_no_args,
fn_ccc_void_no_args,
single_const_pointer_to_comptime_int,

View File

@ -49,6 +49,7 @@ pub const Value = extern union {
null_type,
undefined_type,
fn_noreturn_no_args_type,
fn_void_no_args_type,
fn_naked_noreturn_no_args_type,
fn_ccc_void_no_args_type,
single_const_pointer_to_comptime_int_type,
@ -107,6 +108,109 @@ pub const Value = extern union {
return @fieldParentPtr(T, "base", self.ptr_otherwise);
}
pub fn copy(self: Value, allocator: *Allocator) error{OutOfMemory}!Value {
if (self.tag_if_small_enough < Tag.no_payload_count) {
return Value{ .tag_if_small_enough = self.tag_if_small_enough };
} else switch (self.ptr_otherwise.tag) {
.u8_type,
.i8_type,
.isize_type,
.usize_type,
.c_short_type,
.c_ushort_type,
.c_int_type,
.c_uint_type,
.c_long_type,
.c_ulong_type,
.c_longlong_type,
.c_ulonglong_type,
.c_longdouble_type,
.f16_type,
.f32_type,
.f64_type,
.f128_type,
.c_void_type,
.bool_type,
.void_type,
.type_type,
.anyerror_type,
.comptime_int_type,
.comptime_float_type,
.noreturn_type,
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.undef,
.zero,
.the_one_possible_value,
.null_value,
.bool_true,
.bool_false,
=> unreachable,
.ty => {
const payload = @fieldParentPtr(Payload.Ty, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.Ty);
new_payload.* = .{
.base = payload.base,
.ty = try payload.ty.copy(allocator),
};
return Value{ .ptr_otherwise = &new_payload.base };
},
.int_u64 => return self.copyPayloadShallow(allocator, Payload.Int_u64),
.int_i64 => return self.copyPayloadShallow(allocator, Payload.Int_i64),
.int_big_positive => {
@panic("TODO implement copying of big ints");
},
.int_big_negative => {
@panic("TODO implement copying of big ints");
},
.function => return self.copyPayloadShallow(allocator, Payload.Function),
.ref_val => {
const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.RefVal);
new_payload.* = .{
.base = payload.base,
.val = try payload.val.copy(allocator),
};
return Value{ .ptr_otherwise = &new_payload.base };
},
.decl_ref => return self.copyPayloadShallow(allocator, Payload.DeclRef),
.elem_ptr => {
const payload = @fieldParentPtr(Payload.ElemPtr, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.ElemPtr);
new_payload.* = .{
.base = payload.base,
.array_ptr = try payload.array_ptr.copy(allocator),
.index = payload.index,
};
return Value{ .ptr_otherwise = &new_payload.base };
},
.bytes => return self.copyPayloadShallow(allocator, Payload.Bytes),
.repeated => {
const payload = @fieldParentPtr(Payload.Repeated, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.Repeated);
new_payload.* = .{
.base = payload.base,
.val = try payload.val.copy(allocator),
};
return Value{ .ptr_otherwise = &new_payload.base };
},
}
}
fn copyPayloadShallow(self: Value, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Value {
const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
const new_payload = try allocator.create(T);
new_payload.* = payload.*;
return Value{ .ptr_otherwise = &new_payload.base };
}
pub fn format(
self: Value,
comptime fmt: []const u8,
@ -144,6 +248,7 @@ pub const Value = extern union {
.null_type => return out_stream.writeAll("@TypeOf(null)"),
.undefined_type => return out_stream.writeAll("@TypeOf(undefined)"),
.fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),
.fn_void_no_args_type => return out_stream.writeAll("fn() void"),
.fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
.fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
.single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
@ -229,6 +334,7 @@ pub const Value = extern union {
.null_type => Type.initTag(.@"null"),
.undefined_type => Type.initTag(.@"undefined"),
.fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args),
.fn_void_no_args_type => Type.initTag(.fn_void_no_args),
.fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
.fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),
.single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
@ -286,6 +392,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -345,6 +452,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -405,6 +513,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -470,6 +579,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -564,6 +674,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -620,6 +731,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -721,6 +833,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -783,6 +896,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -862,6 +976,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -929,11 +1044,6 @@ pub const Value = extern union {
len: u64,
};
pub const SingleConstPtrType = struct {
base: Payload = Payload{ .tag = .single_const_ptr_type },
elem_type: *Type,
};
/// Represents a pointer to another immutable value.
pub const RefVal = struct {
base: Payload = Payload{ .tag = .ref_val },

View File

@ -25,6 +25,9 @@ pub const Inst = struct {
/// Hash of slice into the source of the part after the = and before the next instruction.
contents_hash: std.zig.SrcHash = undefined,
/// Pre-allocated field for mapping ZIR text instructions to post-analysis instructions.
analyzed_inst: *ir.Inst = undefined,
/// These names are used directly as the instruction names in the text format.
pub const Tag = enum {
breakpoint,
@ -37,6 +40,8 @@ pub const Inst = struct {
/// The syntax `@foo` is equivalent to `declval("foo")`.
/// declval is equivalent to declref followed by deref.
declval,
/// Same as declval but the parameter is a `*Module.Decl` rather than a name.
declval_in_module,
str,
int,
ptrtoint,
@ -46,6 +51,7 @@ pub const Inst = struct {
@"asm",
@"unreachable",
@"return",
returnvoid,
@"fn",
fntype,
@"export",
@ -67,6 +73,7 @@ pub const Inst = struct {
.call => Call,
.declref => DeclRef,
.declval => DeclVal,
.declval_in_module => DeclValInModule,
.compileerror => CompileError,
.@"const" => Const,
.str => Str,
@ -78,6 +85,7 @@ pub const Inst = struct {
.@"asm" => Asm,
.@"unreachable" => Unreachable,
.@"return" => Return,
.returnvoid => ReturnVoid,
.@"fn" => Fn,
.@"export" => Export,
.primitive => Primitive,
@ -142,6 +150,16 @@ pub const Inst = struct {
kw_args: struct {},
};
pub const DeclValInModule = struct {
pub const base_tag = Tag.declval_in_module;
base: Inst,
positionals: struct {
decl: *IrModule.Decl,
},
kw_args: struct {},
};
pub const CompileError = struct {
pub const base_tag = Tag.compileerror;
base: Inst,
@ -253,6 +271,16 @@ pub const Inst = struct {
pub const base_tag = Tag.@"return";
base: Inst,
positionals: struct {
operand: *Inst,
},
kw_args: struct {},
};
pub const ReturnVoid = struct {
pub const base_tag = Tag.returnvoid;
base: Inst,
positionals: struct {},
kw_args: struct {},
};
@ -492,11 +520,19 @@ pub const Module = struct {
const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize });
const DeclAndIndex = struct {
decl: *Inst,
index: usize,
};
/// TODO Look into making a table to speed this up.
pub fn findDecl(self: Module, name: []const u8) ?*Inst {
for (self.decls) |decl| {
pub fn findDecl(self: Module, name: []const u8) ?DeclAndIndex {
for (self.decls) |decl, i| {
if (mem.eql(u8, decl.name, name)) {
return decl;
return DeclAndIndex{
.decl = decl,
.index = i,
};
}
}
return null;
@ -540,6 +576,7 @@ pub const Module = struct {
.call => return self.writeInstToStreamGeneric(stream, .call, decl, inst_table),
.declref => return self.writeInstToStreamGeneric(stream, .declref, decl, inst_table),
.declval => return self.writeInstToStreamGeneric(stream, .declval, decl, inst_table),
.declval_in_module => return self.writeInstToStreamGeneric(stream, .declval_in_module, decl, inst_table),
.compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, decl, inst_table),
.@"const" => return self.writeInstToStreamGeneric(stream, .@"const", decl, inst_table),
.str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
@ -551,6 +588,7 @@ pub const Module = struct {
.@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table),
.@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table),
.@"return" => return self.writeInstToStreamGeneric(stream, .@"return", decl, inst_table),
.returnvoid => return self.writeInstToStreamGeneric(stream, .returnvoid, decl, inst_table),
.@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table),
.@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
.ref => return self.writeInstToStreamGeneric(stream, .ref, decl, inst_table),
@ -636,6 +674,7 @@ pub const Module = struct {
[]u8, []const u8 => return std.zig.renderStringLiteral(param, stream),
BigIntConst => return stream.print("{}", .{param}),
TypedValue => unreachable, // this is a special case
*IrModule.Decl => unreachable, // this is a special case
else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
}
}
@ -649,6 +688,8 @@ pub const Module = struct {
}
} else if (inst.cast(Inst.DeclVal)) |decl_val| {
try stream.print("@{}", .{decl_val.positionals.name});
} else if (inst.cast(Inst.DeclValInModule)) |decl_val| {
try stream.print("@{}", .{decl_val.positionals.decl.name});
} else {
//try stream.print("?", .{});
unreachable;
@ -996,6 +1037,7 @@ const Parser = struct {
[]u8, []const u8 => return self.parseStringLiteral(),
BigIntConst => return self.parseIntegerLiteral(),
TypedValue => return self.fail("'const' is a special instruction; not legal in ZIR text", .{}),
*IrModule.Decl => return self.fail("'declval_in_module' is a special instruction; not legal in ZIR text", .{}),
else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
}
return self.fail("TODO parse parameter {}", .{@typeName(T)});
@ -1105,7 +1147,7 @@ const EmitZIR = struct {
}
std.sort.sort(*IrModule.Decl, src_decls.items, {}, (struct {
fn lessThan(context: void, a: *IrModule.Decl, b: *IrModule.Decl) bool {
return a.src < b.src;
return a.src_index < b.src_index;
}
}).lessThan);
@ -1113,7 +1155,7 @@ const EmitZIR = struct {
for (src_decls.items) |ir_decl| {
if (self.old_module.export_owners.getValue(ir_decl)) |exports| {
for (exports) |module_export| {
const declval = try self.emitDeclVal(ir_decl.src, mem.spanZ(module_export.exported_decl.name));
const declval = try self.emitDeclVal(ir_decl.src(), mem.spanZ(module_export.exported_decl.name));
const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name);
const export_inst = try self.arena.allocator.create(Inst.Export);
export_inst.* = .{
@ -1131,7 +1173,7 @@ const EmitZIR = struct {
try self.decls.append(self.allocator, &export_inst.base);
}
} else {
const new_decl = try self.emitTypedValue(ir_decl.src, ir_decl.typed_value.most_recent.typed_value);
const new_decl = try self.emitTypedValue(ir_decl.src(), ir_decl.typed_value.most_recent.typed_value);
new_decl.name = try self.arena.allocator.dupe(u8, mem.spanZ(ir_decl.name));
}
}
@ -1301,7 +1343,7 @@ const EmitZIR = struct {
},
}
const fn_type = try self.emitType(src, module_fn.fn_type);
const fn_type = try self.emitType(src, typed_value.ty);
const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);
mem.copy(*Inst, arena_instrs, instructions.items);
@ -1399,7 +1441,23 @@ const EmitZIR = struct {
break :blk &new_inst.base;
},
.unreach => try self.emitTrivial(inst.src, Inst.Unreachable),
.ret => try self.emitTrivial(inst.src, Inst.Return),
.ret => blk: {
const old_inst = inst.cast(ir.Inst.Ret).?;
const new_inst = try self.arena.allocator.create(Inst.Return);
new_inst.* = .{
.base = .{
.name = try self.autoName(),
.src = inst.src,
.tag = Inst.Return.base_tag,
},
.positionals = .{
.operand = try self.resolveInst(inst_table, old_inst.args.operand),
},
.kw_args = .{},
};
break :blk &new_inst.base;
},
.retvoid => try self.emitTrivial(inst.src, Inst.ReturnVoid),
.constant => unreachable, // excluded from function bodies
.assembly => blk: {
const old_inst = inst.cast(ir.Inst.Assembly).?;

View File

@ -7473,6 +7473,12 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n
continue;
}
ZigValue *field_val = const_val->data.x_struct.fields[i];
if (field_val == nullptr) {
add_node_error(g, type_struct_field->decl_node,
buf_sprintf("compiler bug: generating const value for struct field '%s'",
buf_ptr(type_struct_field->name)));
codegen_report_errors_and_exit(g);
}
ZigType *field_type = field_val->type;
assert(field_type != nullptr);
if ((err = ensure_const_val_repr(nullptr, g, nullptr, field_val, field_type))) {