stage2: -femit-zir respects decl names and supports cycles

This commit is contained in:
Andrew Kelley 2020-06-03 21:53:13 -04:00
parent d4d954abd2
commit cf654b52d6
5 changed files with 330 additions and 153 deletions

View File

@ -1097,6 +1097,9 @@ fn resolveDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*De
const decl = kv.value;
try self.reAnalyzeDecl(decl, old_inst);
return decl;
} else if (old_inst.cast(zir.Inst.DeclVal)) |decl_val| {
// This is just a named reference to another decl.
return self.analyzeDeclVal(scope, decl_val);
} else {
const new_decl = blk: {
try self.decl_table.ensureCapacity(self.decl_table.size + 1);
@ -1443,6 +1446,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
.breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(zir.Inst.Breakpoint).?),
.call => return self.analyzeInstCall(scope, old_inst.cast(zir.Inst.Call).?),
.declref => return self.analyzeInstDeclRef(scope, old_inst.cast(zir.Inst.DeclRef).?),
.declval => return self.analyzeInstDeclVal(scope, old_inst.cast(zir.Inst.DeclVal).?),
.str => {
const bytes = old_inst.cast(zir.Inst.Str).?.positionals.bytes;
// The bytes references memory inside the ZIR module, which can get deallocated
@ -1501,6 +1505,24 @@ fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) Inn
return self.analyzeDeclRef(scope, inst.base.src, decl);
}
fn analyzeDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl {
const decl_name = inst.positionals.name;
// This will need to get more fleshed out when there are proper structs & namespaces.
const zir_module = scope.namespace();
const src_decl = zir_module.contents.module.findDecl(decl_name) orelse
return self.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name});
const decl = try self.resolveCompleteDecl(scope, src_decl);
return decl;
}
fn analyzeInstDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst {
const decl = try self.analyzeDeclVal(scope, inst);
const ptr = try self.analyzeDeclRef(scope, inst.base.src, decl);
return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src);
}
fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
const decl_tv = try decl.typedValue();
const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
@ -1621,7 +1643,7 @@ fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) Inn
}
fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst {
return self.constType(scope, primitive.base.src, primitive.positionals.tag.toType());
return self.constInst(scope, primitive.base.src, primitive.positionals.tag.toTypedValue());
}
fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.As) InnerError!*Inst {

View File

@ -51,6 +51,7 @@ pub const Type = extern union {
.comptime_float => return .ComptimeFloat,
.noreturn => return .NoReturn,
.@"null" => return .Null,
.@"undefined" => return .Undefined,
.fn_noreturn_no_args => return .Fn,
.fn_naked_noreturn_no_args => return .Fn,
@ -201,6 +202,7 @@ pub const Type = extern union {
=> return out_stream.writeAll(@tagName(t)),
.@"null" => return out_stream.writeAll("@TypeOf(null)"),
.@"undefined" => return out_stream.writeAll("@TypeOf(undefined)"),
.const_slice_u8 => return out_stream.writeAll("[]const u8"),
.fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
@ -265,6 +267,7 @@ pub const Type = extern union {
.comptime_float => return Value.initTag(.comptime_float_type),
.noreturn => return Value.initTag(.noreturn_type),
.@"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_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),
@ -318,6 +321,7 @@ pub const Type = extern union {
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
=> false,
};
}
@ -378,6 +382,7 @@ pub const Type = extern union {
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
=> unreachable,
};
}
@ -410,6 +415,7 @@ pub const Type = extern union {
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.array_u8_sentinel_0,
.const_slice_u8,
@ -454,6 +460,7 @@ pub const Type = extern union {
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.array_u8_sentinel_0,
.single_const_pointer,
@ -498,6 +505,7 @@ pub const Type = extern union {
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.array_u8_sentinel_0,
.fn_noreturn_no_args,
@ -543,6 +551,7 @@ pub const Type = extern union {
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
@ -586,6 +595,7 @@ pub const Type = extern union {
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
@ -630,6 +640,7 @@ pub const Type = extern union {
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
@ -662,6 +673,7 @@ pub const Type = extern union {
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
@ -707,6 +719,7 @@ pub const Type = extern union {
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
@ -781,6 +794,7 @@ pub const Type = extern union {
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.single_const_pointer,
.single_const_pointer_to_comptime_int,
@ -826,6 +840,7 @@ pub const Type = extern union {
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.single_const_pointer,
.single_const_pointer_to_comptime_int,
@ -870,6 +885,7 @@ pub const Type = extern union {
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.single_const_pointer,
.single_const_pointer_to_comptime_int,
@ -914,6 +930,7 @@ pub const Type = extern union {
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.single_const_pointer,
.single_const_pointer_to_comptime_int,
@ -958,6 +975,7 @@ pub const Type = extern union {
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.single_const_pointer,
.single_const_pointer_to_comptime_int,
@ -1013,6 +1031,7 @@ pub const Type = extern union {
.anyerror,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
@ -1062,6 +1081,7 @@ pub const Type = extern union {
.void,
.noreturn,
.@"null",
.@"undefined",
=> return true,
.int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0,
@ -1115,6 +1135,7 @@ pub const Type = extern union {
.void,
.noreturn,
.@"null",
.@"undefined",
.int_unsigned,
.int_signed,
.array,
@ -1157,6 +1178,7 @@ pub const Type = extern union {
comptime_float,
noreturn,
@"null",
@"undefined",
fn_noreturn_no_args,
fn_naked_noreturn_no_args,
fn_ccc_void_no_args,

View File

@ -47,6 +47,7 @@ pub const Value = extern union {
comptime_float_type,
noreturn_type,
null_type,
undefined_type,
fn_noreturn_no_args_type,
fn_naked_noreturn_no_args_type,
fn_ccc_void_no_args_type,
@ -141,6 +142,7 @@ pub const Value = extern union {
.comptime_float_type => return out_stream.writeAll("comptime_float"),
.noreturn_type => return out_stream.writeAll("noreturn"),
.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_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"),
@ -225,6 +227,7 @@ pub const Value = extern union {
.comptime_float_type => Type.initTag(.comptime_float),
.noreturn_type => Type.initTag(.noreturn),
.null_type => Type.initTag(.@"null"),
.undefined_type => Type.initTag(.@"undefined"),
.fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_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),
@ -281,6 +284,7 @@ pub const Value = extern union {
.comptime_float_type,
.noreturn_type,
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
@ -339,6 +343,7 @@ pub const Value = extern union {
.comptime_float_type,
.noreturn_type,
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
@ -398,6 +403,7 @@ pub const Value = extern union {
.comptime_float_type,
.noreturn_type,
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
@ -462,6 +468,7 @@ pub const Value = extern union {
.comptime_float_type,
.noreturn_type,
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
@ -555,6 +562,7 @@ pub const Value = extern union {
.comptime_float_type,
.noreturn_type,
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
@ -610,6 +618,7 @@ pub const Value = extern union {
.comptime_float_type,
.noreturn_type,
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
@ -710,6 +719,7 @@ pub const Value = extern union {
.comptime_float_type,
.noreturn_type,
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
@ -771,6 +781,7 @@ pub const Value = extern union {
.comptime_float_type,
.noreturn_type,
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
@ -849,6 +860,7 @@ pub const Value = extern union {
.comptime_float_type,
.noreturn_type,
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,

View File

@ -27,9 +27,11 @@ pub const Inst = struct {
pub const Tag = enum {
breakpoint,
call,
/// Represents a reference to a global decl by name.
/// The syntax `@foo` is equivalent to `declref("foo")`.
/// Represents a pointer to a global decl by name.
declref,
/// The syntax `@foo` is equivalent to `declval("foo")`.
/// declval is equivalent to declref followed by deref.
declval,
str,
int,
ptrtoint,
@ -59,6 +61,7 @@ pub const Inst = struct {
.breakpoint => Breakpoint,
.call => Call,
.declref => DeclRef,
.declval => DeclVal,
.str => Str,
.int => Int,
.ptrtoint => PtrToInt,
@ -122,6 +125,16 @@ pub const Inst = struct {
kw_args: struct {},
};
pub const DeclVal = struct {
pub const base_tag = Tag.declval;
base: Inst,
positionals: struct {
name: []const u8,
},
kw_args: struct {},
};
pub const Str = struct {
pub const base_tag = Tag.str;
base: Inst,
@ -254,11 +267,11 @@ pub const Inst = struct {
base: Inst,
positionals: struct {
tag: BuiltinType,
tag: Builtin,
},
kw_args: struct {},
pub const BuiltinType = enum {
pub const Builtin = enum {
isize,
usize,
c_short,
@ -282,32 +295,42 @@ pub const Inst = struct {
anyerror,
comptime_int,
comptime_float,
@"true",
@"false",
@"null",
@"undefined",
void_value,
pub fn toType(self: BuiltinType) Type {
pub fn toTypedValue(self: Builtin) TypedValue {
return switch (self) {
.isize => Type.initTag(.isize),
.usize => Type.initTag(.usize),
.c_short => Type.initTag(.c_short),
.c_ushort => Type.initTag(.c_ushort),
.c_int => Type.initTag(.c_int),
.c_uint => Type.initTag(.c_uint),
.c_long => Type.initTag(.c_long),
.c_ulong => Type.initTag(.c_ulong),
.c_longlong => Type.initTag(.c_longlong),
.c_ulonglong => Type.initTag(.c_ulonglong),
.c_longdouble => Type.initTag(.c_longdouble),
.c_void => Type.initTag(.c_void),
.f16 => Type.initTag(.f16),
.f32 => Type.initTag(.f32),
.f64 => Type.initTag(.f64),
.f128 => Type.initTag(.f128),
.bool => Type.initTag(.bool),
.void => Type.initTag(.void),
.noreturn => Type.initTag(.noreturn),
.type => Type.initTag(.type),
.anyerror => Type.initTag(.anyerror),
.comptime_int => Type.initTag(.comptime_int),
.comptime_float => Type.initTag(.comptime_float),
.isize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.isize_type) },
.usize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type) },
.c_short => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_short_type) },
.c_ushort => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ushort_type) },
.c_int => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_int_type) },
.c_uint => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_uint_type) },
.c_long => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_long_type) },
.c_ulong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ulong_type) },
.c_longlong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_longlong_type) },
.c_ulonglong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ulonglong_type) },
.c_longdouble => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_longdouble_type) },
.c_void => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_void_type) },
.f16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f16_type) },
.f32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f32_type) },
.f64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f64_type) },
.f128 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f128_type) },
.bool => .{ .ty = Type.initTag(.type), .val = Value.initTag(.bool_type) },
.void => .{ .ty = Type.initTag(.type), .val = Value.initTag(.void_type) },
.noreturn => .{ .ty = Type.initTag(.type), .val = Value.initTag(.noreturn_type) },
.type => .{ .ty = Type.initTag(.type), .val = Value.initTag(.type_type) },
.anyerror => .{ .ty = Type.initTag(.type), .val = Value.initTag(.anyerror_type) },
.comptime_int => .{ .ty = Type.initTag(.type), .val = Value.initTag(.comptime_int_type) },
.comptime_float => .{ .ty = Type.initTag(.type), .val = Value.initTag(.comptime_float_type) },
.@"true" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_true) },
.@"false" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_false) },
.@"null" => .{ .ty = Type.initTag(.@"null"), .val = Value.initTag(.null_value) },
.@"undefined" => .{ .ty = Type.initTag(.@"undefined"), .val = Value.initTag(.undef) },
.void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.the_one_possible_value) },
};
}
};
@ -440,7 +463,7 @@ pub const Module = struct {
self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
}
const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Module.Body });
const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize });
/// TODO Look into making a table to speed this up.
pub fn findDecl(self: Module, name: []const u8) ?*Inst {
@ -462,17 +485,17 @@ pub const Module = struct {
try inst_table.ensureCapacity(self.decls.len);
for (self.decls) |decl, decl_i| {
try inst_table.putNoClobber(decl, .{ .index = decl_i, .fn_body = null });
try inst_table.putNoClobber(decl, .{ .inst = decl, .index = null });
if (decl.cast(Inst.Fn)) |fn_inst| {
for (fn_inst.positionals.body.instructions) |inst, inst_i| {
try inst_table.putNoClobber(inst, .{ .index = inst_i, .fn_body = &fn_inst.positionals.body });
try inst_table.putNoClobber(inst, .{ .inst = inst, .index = inst_i });
}
}
}
for (self.decls) |decl, i| {
try stream.print("@{} ", .{i});
try stream.print("@{} ", .{decl.name});
try self.writeInstToStream(stream, decl, &inst_table);
try stream.writeByte('\n');
}
@ -489,6 +512,7 @@ pub const Module = struct {
.breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table),
.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),
.str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
.int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),
.ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
@ -587,9 +611,18 @@ pub const Module = struct {
}
fn writeInstParamToStream(self: Module, stream: var, inst: *Inst, inst_table: *const InstPtrTable) !void {
const info = inst_table.getValue(inst).?;
const prefix = if (info.fn_body == null) "@" else "%";
try stream.print("{}{}", .{ prefix, info.index });
if (inst_table.getValue(inst)) |info| {
if (info.index) |i| {
try stream.print("%{}", .{info.index});
} else {
try stream.print("@{}", .{info.inst.name});
}
} else if (inst.cast(Inst.DeclVal)) |decl_val| {
try stream.print("@{}", .{decl_val.positionals.name});
} else {
//try stream.print("?", .{});
unreachable;
}
}
};
@ -964,47 +997,17 @@ const Parser = struct {
self.i = src;
return self.fail("unrecognized identifier: {}", .{bad_name});
} else {
const name_array = try self.arena.allocator.create(Inst.Str);
name_array.* = .{
const declval = try self.arena.allocator.create(Inst.DeclVal);
declval.* = .{
.base = .{
.name = try self.generateName(),
.src = src,
.tag = Inst.Str.base_tag,
.tag = Inst.DeclVal.base_tag,
},
.positionals = .{ .bytes = ident },
.positionals = .{ .name = ident },
.kw_args = .{},
};
const name = try self.arena.allocator.create(Inst.Ref);
name.* = .{
.base = .{
.name = try self.generateName(),
.src = src,
.tag = Inst.Ref.base_tag,
},
.positionals = .{ .operand = &name_array.base },
.kw_args = .{},
};
const declref = try self.arena.allocator.create(Inst.DeclRef);
declref.* = .{
.base = .{
.name = try self.generateName(),
.src = src,
.tag = Inst.DeclRef.base_tag,
},
.positionals = .{ .name = &name.base },
.kw_args = .{},
};
const deref = try self.arena.allocator.create(Inst.Deref);
deref.* = .{
.base = .{
.name = try self.generateName(),
.src = src,
.tag = Inst.Deref.base_tag,
},
.positionals = .{ .ptr = &declref.base },
.kw_args = .{},
};
return &deref.base;
return &declval.base;
}
};
if (local_ref) {
@ -1025,12 +1028,15 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module {
var ctx: EmitZIR = .{
.allocator = allocator,
.decls = .{},
.decl_table = std.AutoHashMap(*ir.Inst, *Inst).init(allocator),
.arena = std.heap.ArenaAllocator.init(allocator),
.old_module = &old_module,
.next_auto_name = 0,
.names = std.StringHashMap(void).init(allocator),
.primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Inst).init(allocator),
};
defer ctx.decls.deinit(allocator);
defer ctx.decl_table.deinit();
defer ctx.names.deinit();
defer ctx.primitive_table.deinit();
errdefer ctx.arena.deinit();
try ctx.emit();
@ -1046,47 +1052,90 @@ const EmitZIR = struct {
arena: std.heap.ArenaAllocator,
old_module: *const IrModule,
decls: std.ArrayListUnmanaged(*Inst),
decl_table: std.AutoHashMap(*ir.Inst, *Inst),
names: std.StringHashMap(void),
next_auto_name: usize,
primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Inst),
fn emit(self: *EmitZIR) !void {
var it = self.old_module.decl_exports.iterator();
while (it.next()) |kv| {
const decl = kv.key;
const exports = kv.value;
const export_value = try self.emitTypedValue(decl.src, decl.typed_value.most_recent.typed_value);
for (exports) |module_export| {
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.* = .{
.base = .{
.name = try self.autoName(),
.src = module_export.src,
.tag = Inst.Export.base_tag,
},
.positionals = .{
.symbol_name = symbol_name,
.value = export_value,
},
.kw_args = .{},
};
try self.decls.append(self.allocator, &export_inst.base);
// Put all the Decls in a list and sort them by name to avoid nondeterminism introduced
// by the hash table.
var src_decls = std.ArrayList(*IrModule.Decl).init(self.allocator);
defer src_decls.deinit();
try src_decls.ensureCapacity(self.old_module.decl_table.size);
try self.decls.ensureCapacity(self.allocator, self.old_module.decl_table.size);
try self.names.ensureCapacity(self.old_module.decl_table.size);
var decl_it = self.old_module.decl_table.iterator();
while (decl_it.next()) |kv| {
const decl = kv.value;
src_decls.appendAssumeCapacity(decl);
self.names.putAssumeCapacityNoClobber(mem.spanZ(decl.name), {});
}
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;
}
}).lessThan);
// Emit all the decls.
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 symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name);
const export_inst = try self.arena.allocator.create(Inst.Export);
export_inst.* = .{
.base = .{
.name = try self.autoName(),
.src = module_export.src,
.tag = Inst.Export.base_tag,
},
.positionals = .{
.symbol_name = symbol_name,
.value = declval,
},
.kw_args = .{},
};
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);
new_decl.name = try self.arena.allocator.dupe(u8, mem.spanZ(ir_decl.name));
}
}
}
fn resolveInst(self: *EmitZIR, inst_table: *const std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {
fn resolveInst(self: *EmitZIR, inst_table: *std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {
if (inst.cast(ir.Inst.Constant)) |const_inst| {
if (self.decl_table.getValue(inst)) |decl| {
return decl;
}
const new_decl = try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
try self.decl_table.putNoClobber(inst, new_decl);
const new_decl = if (const_inst.val.cast(Value.Payload.Function)) |func_pl| blk: {
const owner_decl = func_pl.func.owner_decl;
break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name));
} else if (const_inst.val.cast(Value.Payload.DeclRef)) |declref| blk: {
break :blk try self.emitDeclRef(inst.src, declref.decl);
} else blk: {
break :blk try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
};
try inst_table.putNoClobber(inst, new_decl);
return new_decl;
} else {
return inst_table.getValue(inst).?;
}
}
fn emitDeclVal(self: *EmitZIR, src: usize, decl_name: []const u8) !*Inst {
const declval = try self.arena.allocator.create(Inst.DeclVal);
declval.* = .{
.base = .{
.name = try self.autoName(),
.src = src,
.tag = Inst.DeclVal.base_tag,
},
.positionals = .{ .name = try self.arena.allocator.dupe(u8, decl_name) },
.kw_args = .{},
};
return &declval.base;
}
fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {
const big_int_space = try self.arena.allocator.create(Value.BigIntSpace);
const int_inst = try self.arena.allocator.create(Inst.Int);
@ -1105,8 +1154,31 @@ const EmitZIR = struct {
return &int_inst.base;
}
fn emitDeclRef(self: *EmitZIR, src: usize, decl: *IrModule.Decl) !*Inst {
const declval = try self.emitDeclVal(src, mem.spanZ(decl.name));
const ref_inst = try self.arena.allocator.create(Inst.Ref);
ref_inst.* = .{
.base = .{
.name = try self.autoName(),
.src = src,
.tag = Inst.Ref.base_tag,
},
.positionals = .{
.operand = declval,
},
.kw_args = .{},
};
try self.decls.append(self.allocator, &ref_inst.base);
return &ref_inst.base;
}
fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Inst {
const allocator = &self.arena.allocator;
if (typed_value.val.cast(Value.Payload.DeclRef)) |decl_ref| {
const decl = decl_ref.decl;
return self.emitDeclRef(src, decl);
}
switch (typed_value.ty.zigTypeTag()) {
.Pointer => {
const ptr_elem_type = typed_value.ty.elemType();
@ -1142,7 +1214,6 @@ const EmitZIR = struct {
},
.kw_args = .{},
};
try self.decls.append(self.allocator, &as_inst.base);
return &as_inst.base;
},
@ -1182,6 +1253,33 @@ const EmitZIR = struct {
try self.decls.append(self.allocator, &fn_inst.base);
return &fn_inst.base;
},
.Array => {
// TODO more checks to make sure this can be emitted as a string literal
//const array_elem_type = ptr_elem_type.elemType();
//if (array_elem_type.eql(Type.initTag(.u8)) and
// ptr_elem_type.hasSentinel(Value.initTag(.zero)))
//{
//}
const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) {
error.AnalysisFail => unreachable,
else => |e| return e,
};
const str_inst = try self.arena.allocator.create(Inst.Str);
str_inst.* = .{
.base = .{
.name = try self.autoName(),
.src = src,
.tag = Inst.Str.base_tag,
},
.positionals = .{
.bytes = bytes,
},
.kw_args = .{},
};
try self.decls.append(self.allocator, &str_inst.base);
return &str_inst.base;
},
.Void => return self.emitPrimitive(src, .void_value),
else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}),
}
}
@ -1395,30 +1493,30 @@ const EmitZIR = struct {
fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {
switch (ty.tag()) {
.isize => return self.emitPrimitiveType(src, .isize),
.usize => return self.emitPrimitiveType(src, .usize),
.c_short => return self.emitPrimitiveType(src, .c_short),
.c_ushort => return self.emitPrimitiveType(src, .c_ushort),
.c_int => return self.emitPrimitiveType(src, .c_int),
.c_uint => return self.emitPrimitiveType(src, .c_uint),
.c_long => return self.emitPrimitiveType(src, .c_long),
.c_ulong => return self.emitPrimitiveType(src, .c_ulong),
.c_longlong => return self.emitPrimitiveType(src, .c_longlong),
.c_ulonglong => return self.emitPrimitiveType(src, .c_ulonglong),
.c_longdouble => return self.emitPrimitiveType(src, .c_longdouble),
.c_void => return self.emitPrimitiveType(src, .c_void),
.f16 => return self.emitPrimitiveType(src, .f16),
.f32 => return self.emitPrimitiveType(src, .f32),
.f64 => return self.emitPrimitiveType(src, .f64),
.f128 => return self.emitPrimitiveType(src, .f128),
.anyerror => return self.emitPrimitiveType(src, .anyerror),
.isize => return self.emitPrimitive(src, .isize),
.usize => return self.emitPrimitive(src, .usize),
.c_short => return self.emitPrimitive(src, .c_short),
.c_ushort => return self.emitPrimitive(src, .c_ushort),
.c_int => return self.emitPrimitive(src, .c_int),
.c_uint => return self.emitPrimitive(src, .c_uint),
.c_long => return self.emitPrimitive(src, .c_long),
.c_ulong => return self.emitPrimitive(src, .c_ulong),
.c_longlong => return self.emitPrimitive(src, .c_longlong),
.c_ulonglong => return self.emitPrimitive(src, .c_ulonglong),
.c_longdouble => return self.emitPrimitive(src, .c_longdouble),
.c_void => return self.emitPrimitive(src, .c_void),
.f16 => return self.emitPrimitive(src, .f16),
.f32 => return self.emitPrimitive(src, .f32),
.f64 => return self.emitPrimitive(src, .f64),
.f128 => return self.emitPrimitive(src, .f128),
.anyerror => return self.emitPrimitive(src, .anyerror),
else => switch (ty.zigTypeTag()) {
.Bool => return self.emitPrimitiveType(src, .bool),
.Void => return self.emitPrimitiveType(src, .void),
.NoReturn => return self.emitPrimitiveType(src, .noreturn),
.Type => return self.emitPrimitiveType(src, .type),
.ComptimeInt => return self.emitPrimitiveType(src, .comptime_int),
.ComptimeFloat => return self.emitPrimitiveType(src, .comptime_float),
.Bool => return self.emitPrimitive(src, .bool),
.Void => return self.emitPrimitive(src, .void),
.NoReturn => return self.emitPrimitive(src, .noreturn),
.Type => return self.emitPrimitive(src, .type),
.ComptimeInt => return self.emitPrimitive(src, .comptime_int),
.ComptimeFloat => return self.emitPrimitive(src, .comptime_float),
.Fn => {
const param_types = try self.allocator.alloc(Type, ty.fnParamLen());
defer self.allocator.free(param_types);
@ -1453,24 +1551,36 @@ const EmitZIR = struct {
}
fn autoName(self: *EmitZIR) ![]u8 {
return std.fmt.allocPrint(&self.arena.allocator, "{}", .{self.decls.items.len});
while (true) {
const proposed_name = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${}", .{self.next_auto_name});
self.next_auto_name += 1;
const gop = try self.names.getOrPut(proposed_name);
if (!gop.found_existing) {
gop.kv.value = {};
return proposed_name;
}
}
}
fn emitPrimitiveType(self: *EmitZIR, src: usize, tag: Inst.Primitive.BuiltinType) !*Inst {
const primitive_inst = try self.arena.allocator.create(Inst.Primitive);
primitive_inst.* = .{
.base = .{
.name = try self.autoName(),
.src = src,
.tag = Inst.Primitive.base_tag,
},
.positionals = .{
.tag = tag,
},
.kw_args = .{},
};
try self.decls.append(self.allocator, &primitive_inst.base);
return &primitive_inst.base;
fn emitPrimitive(self: *EmitZIR, src: usize, tag: Inst.Primitive.Builtin) !*Inst {
const gop = try self.primitive_table.getOrPut(tag);
if (!gop.found_existing) {
const primitive_inst = try self.arena.allocator.create(Inst.Primitive);
primitive_inst.* = .{
.base = .{
.name = try self.autoName(),
.src = src,
.tag = Inst.Primitive.base_tag,
},
.positionals = .{
.tag = tag,
},
.kw_args = .{},
};
try self.decls.append(self.allocator, &primitive_inst.base);
gop.kv.value = &primitive_inst.base;
}
return gop.kv.value;
}
fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst {

View File

@ -21,14 +21,17 @@ pub fn addCases(ctx: *TestContext) void {
\\ %11 = return()
\\})
,
\\@0 = primitive(void)
\\@1 = fntype([], @0, cc=C)
\\@2 = fn(@1, {
\\@void = primitive(void)
\\@fnty = fntype([], @void, cc=C)
\\@9 = str("entry")
\\@10 = ref(@9)
\\@unnamed$6 = str("entry")
\\@unnamed$7 = ref(@unnamed$6)
\\@unnamed$8 = export(@unnamed$7, @entry)
\\@unnamed$10 = fntype([], @void, cc=C)
\\@entry = fn(@unnamed$10, {
\\ %0 = return()
\\})
\\@3 = str("entry")
\\@4 = ref(@3)
\\@5 = export(@4, @2)
\\
);
ctx.addZIRTransform("elemptr, add, cmp, condbr, return, breakpoint", linux_x64,
@ -68,14 +71,22 @@ pub fn addCases(ctx: *TestContext) void {
\\@10 = ref(@9)
\\@11 = export(@10, @entry)
,
\\@0 = primitive(void)
\\@1 = fntype([], @0, cc=C)
\\@2 = fn(@1, {
\\@void = primitive(void)
\\@fnty = fntype([], @void, cc=C)
\\@0 = int(0)
\\@1 = int(1)
\\@2 = int(2)
\\@3 = int(3)
\\@unnamed$7 = fntype([], @void, cc=C)
\\@entry = fn(@unnamed$7, {
\\ %0 = return()
\\})
\\@3 = str("entry")
\\@4 = ref(@3)
\\@5 = export(@4, @2)
\\@a = str("2\x08\x01\n")
\\@9 = str("entry")
\\@10 = ref(@9)
\\@unnamed$14 = str("entry")
\\@unnamed$15 = ref(@unnamed$14)
\\@unnamed$16 = export(@unnamed$15, @entry)
\\
);