Commit 2ea08561cf
Changed files (5)
src
src-self-hosted
src/analyze.cpp
@@ -3941,7 +3941,7 @@ AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index) {
return nullptr;
}
-static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, VariableTableEntry **arg_vars) {
+static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry) {
TypeTableEntry *fn_type = fn_table_entry->type_entry;
assert(!fn_type->data.fn.is_generic);
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
@@ -3979,10 +3979,6 @@ static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entr
if (fn_type->data.fn.gen_param_info) {
var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;
}
-
- if (arg_vars) {
- arg_vars[i] = var;
- }
}
}
@@ -4082,7 +4078,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
if (!fn_table_entry->child_scope)
fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base;
- define_local_param_variables(g, fn_table_entry, nullptr);
+ define_local_param_variables(g, fn_table_entry);
TypeTableEntry *fn_type = fn_table_entry->type_entry;
assert(!fn_type->data.fn.is_generic);
src-self-hosted/codegen.zig
@@ -168,6 +168,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
//}
const fn_type = fn_val.base.typ.cast(Type.Fn).?;
+ const fn_type_normal = &fn_type.key.data.Normal;
try addLLVMFnAttr(ofile, llvm_fn, "nounwind");
//add_uwtable_attr(g, fn_table_entry->llvm_value);
@@ -209,7 +210,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
// addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull");
//}
- const cur_ret_ptr = if (fn_type.return_type.handleIsPtr()) llvm.GetParam(llvm_fn, 0) else null;
+ const cur_ret_ptr = if (fn_type_normal.return_type.handleIsPtr()) llvm.GetParam(llvm_fn, 0) else null;
// build all basic blocks
for (code.basic_block_list.toSlice()) |bb| {
src-self-hosted/compilation.zig
@@ -220,12 +220,14 @@ pub const Compilation = struct {
int_type_table: event.Locked(IntTypeTable),
array_type_table: event.Locked(ArrayTypeTable),
ptr_type_table: event.Locked(PtrTypeTable),
+ fn_type_table: event.Locked(FnTypeTable),
c_int_types: [CInt.list.len]*Type.Int,
const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
+ const FnTypeTable = std.HashMap(*const Type.Fn.Key, *Type.Fn, Type.Fn.Key.hash, Type.Fn.Key.eql);
const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8);
const CompileErrList = std.ArrayList(*Msg);
@@ -384,6 +386,7 @@ pub const Compilation = struct {
.int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)),
.array_type_table = event.Locked(ArrayTypeTable).init(loop, ArrayTypeTable.init(loop.allocator)),
.ptr_type_table = event.Locked(PtrTypeTable).init(loop, PtrTypeTable.init(loop.allocator)),
+ .fn_type_table = event.Locked(FnTypeTable).init(loop, FnTypeTable.init(loop.allocator)),
.c_int_types = undefined,
.meta_type = undefined,
@@ -414,6 +417,7 @@ pub const Compilation = struct {
comp.int_type_table.private_data.deinit();
comp.array_type_table.private_data.deinit();
comp.ptr_type_table.private_data.deinit();
+ comp.fn_type_table.private_data.deinit();
comp.arena_allocator.deinit();
comp.loop.allocator.destroy(comp);
}
@@ -1160,10 +1164,47 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
fn_decl.value = Decl.Fn.Val{ .Fn = fn_val };
symbol_name_consumed = true;
+ // Define local parameter variables
+ //for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
+ // FnTypeParamInfo *param_info = &fn_type_id->param_info[i];
+ // AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
+ // Buf *param_name;
+ // bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args;
+ // if (param_decl_node && !is_var_args) {
+ // param_name = param_decl_node->data.param_decl.name;
+ // } else {
+ // param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i);
+ // }
+ // if (param_name == nullptr) {
+ // continue;
+ // }
+
+ // TypeTableEntry *param_type = param_info->type;
+ // bool is_noalias = param_info->is_noalias;
+
+ // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
+ // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
+ // }
+
+ // VariableTableEntry *var = add_variable(g, param_decl_node, fn_table_entry->child_scope,
+ // param_name, true, create_const_runtime(param_type), nullptr);
+ // var->src_arg_index = i;
+ // fn_table_entry->child_scope = var->child_scope;
+ // var->shadowable = var->shadowable || is_var_args;
+
+ // if (type_has_bits(param_type)) {
+ // fn_table_entry->variable_list.append(var);
+ // }
+
+ // if (fn_type->data.fn.gen_param_info) {
+ // var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;
+ // }
+ //}
+
const analyzed_code = try await (async comp.genAndAnalyzeCode(
&fndef_scope.base,
body_node,
- fn_type.return_type,
+ fn_type.key.data.Normal.return_type,
) catch unreachable);
errdefer analyzed_code.destroy(comp.gpa());
@@ -1199,14 +1240,13 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
var params = ArrayList(Type.Fn.Param).init(comp.gpa());
var params_consumed = false;
- defer if (params_consumed) {
+ defer if (!params_consumed) {
for (params.toSliceConst()) |param| {
param.typ.base.deref(comp);
}
params.deinit();
};
- const is_var_args = false;
{
var it = fn_proto.params.iterator(0);
while (it.next()) |param_node_ptr| {
@@ -1219,8 +1259,29 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
});
}
}
- const fn_type = try Type.Fn.create(comp, return_type, params.toOwnedSlice(), is_var_args);
+
+ const key = Type.Fn.Key{
+ .alignment = null,
+ .data = Type.Fn.Key.Data{
+ .Normal = Type.Fn.Normal{
+ .return_type = return_type,
+ .params = params.toOwnedSlice(),
+ .is_var_args = false, // TODO
+ .cc = Type.Fn.CallingConvention.Auto, // TODO
+ },
+ },
+ };
params_consumed = true;
+ var key_consumed = false;
+ defer if (!key_consumed) {
+ for (key.data.Normal.params) |param| {
+ param.typ.base.deref(comp);
+ }
+ comp.gpa().free(key.data.Normal.params);
+ };
+
+ const fn_type = try await (async Type.Fn.get(comp, key) catch unreachable);
+ key_consumed = true;
errdefer fn_type.base.base.deref(comp);
return fn_type;
src-self-hosted/ir.zig
@@ -281,11 +281,13 @@ pub const Inst = struct {
return error.SemanticAnalysisFailed;
};
- if (fn_type.params.len != self.params.args.len) {
+ const fn_type_param_count = fn_type.paramCount();
+
+ if (fn_type_param_count != self.params.args.len) {
try ira.addCompileError(
self.base.span,
"expected {} arguments, found {}",
- fn_type.params.len,
+ fn_type_param_count,
self.params.args.len,
);
return error.SemanticAnalysisFailed;
@@ -299,7 +301,7 @@ pub const Inst = struct {
.fn_ref = fn_ref,
.args = args,
});
- new_inst.val = IrVal{ .KnownType = fn_type.return_type };
+ new_inst.val = IrVal{ .KnownType = fn_type.key.data.Normal.return_type };
return new_inst;
}
src-self-hosted/type.zig
@@ -221,57 +221,267 @@ pub const Type = struct {
pub const Fn = struct {
base: Type,
- return_type: *Type,
- params: []Param,
- is_var_args: bool,
+ key: Key,
+ garbage_node: std.atomic.Stack(*Fn).Node,
+
+ pub const Key = struct {
+ data: Data,
+ alignment: ?u32,
+
+ pub const Data = union(enum) {
+ Generic: Generic,
+ Normal: Normal,
+ };
+
+ pub fn hash(self: *const Key) u32 {
+ var result: u32 = 0;
+ result +%= hashAny(self.alignment, 0);
+ switch (self.data) {
+ Data.Generic => |generic| {
+ result +%= hashAny(generic.param_count, 1);
+ switch (generic.cc) {
+ CallingConvention.Async => |allocator_type| result +%= hashAny(allocator_type, 2),
+ else => result +%= hashAny(CallingConvention(generic.cc), 3),
+ }
+ },
+ Data.Normal => |normal| {
+ result +%= hashAny(normal.return_type, 4);
+ result +%= hashAny(normal.is_var_args, 5);
+ result +%= hashAny(normal.cc, 6);
+ for (normal.params) |param| {
+ result +%= hashAny(param.is_noalias, 7);
+ result +%= hashAny(param.typ, 8);
+ }
+ },
+ }
+ return result;
+ }
+
+ pub fn eql(self: *const Key, other: *const Key) bool {
+ if ((self.alignment == null) != (other.alignment == null)) return false;
+ if (self.alignment) |self_align| {
+ if (self_align != other.alignment.?) return false;
+ }
+ if (@TagType(Data)(self.data) != @TagType(Data)(other.data)) return false;
+ switch (self.data) {
+ Data.Generic => |*self_generic| {
+ const other_generic = &other.data.Generic;
+ if (self_generic.param_count != other_generic.param_count) return false;
+ if (CallingConvention(self_generic.cc) != CallingConvention(other_generic.cc)) return false;
+ switch (self_generic.cc) {
+ CallingConvention.Async => |self_allocator_type| {
+ const other_allocator_type = other_generic.cc.Async;
+ if (self_allocator_type != other_allocator_type) return false;
+ },
+ else => {},
+ }
+ },
+ Data.Normal => |*self_normal| {
+ const other_normal = &other.data.Normal;
+ if (self_normal.cc != other_normal.cc) return false;
+ if (self_normal.is_var_args != other_normal.is_var_args) return false;
+ if (self_normal.return_type != other_normal.return_type) return false;
+ for (self_normal.params) |*self_param, i| {
+ const other_param = &other_normal.params[i];
+ if (self_param.is_noalias != other_param.is_noalias) return false;
+ if (self_param.typ != other_param.typ) return false;
+ }
+ },
+ }
+ return true;
+ }
+
+ pub fn deref(key: Key, comp: *Compilation) void {
+ switch (key.data) {
+ Key.Data.Generic => |generic| {
+ switch (generic.cc) {
+ CallingConvention.Async => |allocator_type| allocator_type.base.deref(comp),
+ else => {},
+ }
+ },
+ Key.Data.Normal => |normal| {
+ normal.return_type.base.deref(comp);
+ for (normal.params) |param| {
+ param.typ.base.deref(comp);
+ }
+ },
+ }
+ }
+
+ pub fn ref(key: Key) void {
+ switch (key.data) {
+ Key.Data.Generic => |generic| {
+ switch (generic.cc) {
+ CallingConvention.Async => |allocator_type| allocator_type.base.ref(),
+ else => {},
+ }
+ },
+ Key.Data.Normal => |normal| {
+ normal.return_type.base.ref();
+ for (normal.params) |param| {
+ param.typ.base.ref();
+ }
+ },
+ }
+ }
+ };
+
+ pub const Normal = struct {
+ params: []Param,
+ return_type: *Type,
+ is_var_args: bool,
+ cc: CallingConvention,
+ };
+
+ pub const Generic = struct {
+ param_count: usize,
+ cc: CC,
+
+ pub const CC = union(CallingConvention) {
+ Auto,
+ C,
+ Cold,
+ Naked,
+ Stdcall,
+ Async: *Type, // allocator type
+ };
+ };
+
+ pub const CallingConvention = enum {
+ Auto,
+ C,
+ Cold,
+ Naked,
+ Stdcall,
+ Async,
+ };
pub const Param = struct {
is_noalias: bool,
typ: *Type,
};
- pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn {
- const result = try comp.gpa().create(Fn{
+ fn ccFnTypeStr(cc: CallingConvention) []const u8 {
+ return switch (cc) {
+ CallingConvention.Auto => "",
+ CallingConvention.C => "extern ",
+ CallingConvention.Cold => "coldcc ",
+ CallingConvention.Naked => "nakedcc ",
+ CallingConvention.Stdcall => "stdcallcc ",
+ CallingConvention.Async => unreachable,
+ };
+ }
+
+ pub fn paramCount(self: *Fn) usize {
+ return switch (self.key.data) {
+ Key.Data.Generic => |generic| generic.param_count,
+ Key.Data.Normal => |normal| normal.params.len,
+ };
+ }
+
+ /// takes ownership of key.Normal.params on success
+ pub async fn get(comp: *Compilation, key: Key) !*Fn {
+ {
+ const held = await (async comp.fn_type_table.acquire() catch unreachable);
+ defer held.release();
+
+ if (held.value.get(&key)) |entry| {
+ entry.value.base.base.ref();
+ return entry.value;
+ }
+ }
+
+ key.ref();
+ errdefer key.deref(comp);
+
+ const self = try comp.gpa().create(Fn{
.base = undefined,
- .return_type = return_type,
- .params = params,
- .is_var_args = is_var_args,
+ .key = key,
+ .garbage_node = undefined,
});
- errdefer comp.gpa().destroy(result);
+ errdefer comp.gpa().destroy(self);
- result.base.init(comp, Id.Fn, "TODO fn type name");
+ var name_buf = try std.Buffer.initSize(comp.gpa(), 0);
+ defer name_buf.deinit();
+
+ const name_stream = &std.io.BufferOutStream.init(&name_buf).stream;
+
+ switch (key.data) {
+ Key.Data.Generic => |generic| {
+ switch (generic.cc) {
+ CallingConvention.Async => |async_allocator_type| {
+ try name_stream.print("async<{}> ", async_allocator_type.name);
+ },
+ else => {
+ const cc_str = ccFnTypeStr(generic.cc);
+ try name_stream.write(cc_str);
+ },
+ }
+ try name_stream.write("fn(");
+ var param_i: usize = 0;
+ while (param_i < generic.param_count) : (param_i += 1) {
+ const arg = if (param_i == 0) "var" else ", var";
+ try name_stream.write(arg);
+ }
+ try name_stream.write(")");
+ if (key.alignment) |alignment| {
+ try name_stream.print(" align<{}>", alignment);
+ }
+ try name_stream.write(" var");
+ },
+ Key.Data.Normal => |normal| {
+ const cc_str = ccFnTypeStr(normal.cc);
+ try name_stream.print("{}fn(", cc_str);
+ for (normal.params) |param, i| {
+ if (i != 0) try name_stream.write(", ");
+ if (param.is_noalias) try name_stream.write("noalias ");
+ try name_stream.write(param.typ.name);
+ }
+ if (normal.is_var_args) {
+ if (normal.params.len != 0) try name_stream.write(", ");
+ try name_stream.write("...");
+ }
+ try name_stream.write(")");
+ if (key.alignment) |alignment| {
+ try name_stream.print(" align<{}>", alignment);
+ }
+ try name_stream.print(" {}", normal.return_type.name);
+ },
+ }
+
+ self.base.init(comp, Id.Fn, name_buf.toOwnedSlice());
- result.return_type.base.ref();
- for (result.params) |param| {
- param.typ.base.ref();
+ {
+ const held = await (async comp.fn_type_table.acquire() catch unreachable);
+ defer held.release();
+
+ _ = try held.value.put(&self.key, self);
}
- return result;
+ return self;
}
pub fn destroy(self: *Fn, comp: *Compilation) void {
- self.return_type.base.deref(comp);
- for (self.params) |param| {
- param.typ.base.deref(comp);
- }
+ self.key.deref(comp);
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
- const llvm_return_type = switch (self.return_type.id) {
+ const normal = &self.key.data.Normal;
+ const llvm_return_type = switch (normal.return_type.id) {
Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory,
- else => try self.return_type.getLlvmType(allocator, llvm_context),
+ else => try normal.return_type.getLlvmType(allocator, llvm_context),
};
- const llvm_param_types = try allocator.alloc(llvm.TypeRef, self.params.len);
+ const llvm_param_types = try allocator.alloc(llvm.TypeRef, normal.params.len);
defer allocator.free(llvm_param_types);
for (llvm_param_types) |*llvm_param_type, i| {
- llvm_param_type.* = try self.params[i].typ.getLlvmType(allocator, llvm_context);
+ llvm_param_type.* = try normal.params[i].typ.getLlvmType(allocator, llvm_context);
}
return llvm.FunctionType(
llvm_return_type,
llvm_param_types.ptr,
@intCast(c_uint, llvm_param_types.len),
- @boolToInt(self.is_var_args),
+ @boolToInt(normal.is_var_args),
) orelse error.OutOfMemory;
}
};
@@ -347,8 +557,10 @@ pub const Type = struct {
is_signed: bool,
pub fn hash(self: *const Key) u32 {
- const rands = [2]u32{ 0xa4ba6498, 0x75fc5af7 };
- return rands[@boolToInt(self.is_signed)] *% self.bit_count;
+ var result: u32 = 0;
+ result +%= hashAny(self.is_signed, 0);
+ result +%= hashAny(self.bit_count, 1);
+ return result;
}
pub fn eql(self: *const Key, other: *const Key) bool {
@@ -443,15 +655,16 @@ pub const Type = struct {
alignment: Align,
pub fn hash(self: *const Key) u32 {
- const align_hash = switch (self.alignment) {
+ var result: u32 = 0;
+ result +%= switch (self.alignment) {
Align.Abi => 0xf201c090,
- Align.Override => |x| x,
+ Align.Override => |x| hashAny(x, 0),
};
- return hash_usize(@ptrToInt(self.child_type)) *%
- hash_enum(self.mut) *%
- hash_enum(self.vol) *%
- hash_enum(self.size) *%
- align_hash;
+ result +%= hashAny(self.child_type, 1);
+ result +%= hashAny(self.mut, 2);
+ result +%= hashAny(self.vol, 3);
+ result +%= hashAny(self.size, 4);
+ return result;
}
pub fn eql(self: *const Key, other: *const Key) bool {
@@ -605,7 +818,10 @@ pub const Type = struct {
len: usize,
pub fn hash(self: *const Key) u32 {
- return hash_usize(@ptrToInt(self.elem_type)) *% hash_usize(self.len);
+ var result: u32 = 0;
+ result +%= hashAny(self.elem_type, 0);
+ result +%= hashAny(self.len, 1);
+ return result;
}
pub fn eql(self: *const Key, other: *const Key) bool {
@@ -818,27 +1034,37 @@ pub const Type = struct {
};
};
-fn hash_usize(x: usize) u32 {
- return switch (@sizeOf(usize)) {
- 4 => x,
- 8 => @truncate(u32, x *% 0xad44ee2d8e3fc13d),
- else => @compileError("implement this hash function"),
- };
-}
-
-fn hash_enum(x: var) u32 {
- const rands = []u32{
- 0x85ebf64f,
- 0x3fcb3211,
- 0x240a4e8e,
- 0x40bb0e3c,
- 0x78be45af,
- 0x1ca98e37,
- 0xec56053a,
- 0x906adc48,
- 0xd4fe9763,
- 0x54c80dac,
- };
- comptime assert(@memberCount(@typeOf(x)) < rands.len);
- return rands[@enumToInt(x)];
+fn hashAny(x: var, comptime seed: u64) u32 {
+ switch (@typeInfo(@typeOf(x))) {
+ builtin.TypeId.Int => |info| {
+ comptime var rng = comptime std.rand.DefaultPrng.init(seed);
+ const unsigned_x = @bitCast(@IntType(false, info.bits), x);
+ if (info.bits <= 32) {
+ return u32(unsigned_x) *% comptime rng.random.scalar(u32);
+ } else {
+ return @truncate(u32, unsigned_x *% comptime rng.random.scalar(@typeOf(unsigned_x)));
+ }
+ },
+ builtin.TypeId.Pointer => |info| {
+ switch (info.size) {
+ builtin.TypeInfo.Pointer.Size.One => return hashAny(@ptrToInt(x), seed),
+ builtin.TypeInfo.Pointer.Size.Many => @compileError("implement hash function"),
+ builtin.TypeInfo.Pointer.Size.Slice => @compileError("implement hash function"),
+ }
+ },
+ builtin.TypeId.Enum => return hashAny(@enumToInt(x), seed),
+ builtin.TypeId.Bool => {
+ comptime var rng = comptime std.rand.DefaultPrng.init(seed);
+ const vals = comptime [2]u32{ rng.random.scalar(u32), rng.random.scalar(u32) };
+ return vals[@boolToInt(x)];
+ },
+ builtin.TypeId.Optional => {
+ if (x) |non_opt| {
+ return hashAny(non_opt, seed);
+ } else {
+ return hashAny(u32(1), seed);
+ }
+ },
+ else => @compileError("implement hash function for " ++ @typeName(@typeOf(x))),
+ }
}