| ... | @@ -221,57 +221,267 @@ pub const Type = struct { | ... | @@ -221,57 +221,267 @@ pub const Type = struct { |
| 221 | | 221 | |
| 222 | pub const Fn = struct { | 222 | pub const Fn = struct { |
| 223 | base: Type, | 223 | base: Type, |
| 224 | return_type: *Type, | 224 | key: Key, |
| 225 | params: []Param, | 225 | garbage_node: std.atomic.Stack(*Fn).Node, |
| 226 | is_var_args: bool, | 226 | |
| | 227 | pub const Key = struct { |
| | 228 | data: Data, |
| | 229 | alignment: ?u32, |
| | 230 | |
| | 231 | pub const Data = union(enum) { |
| | 232 | Generic: Generic, |
| | 233 | Normal: Normal, |
| | 234 | }; |
| | 235 | |
| | 236 | pub fn hash(self: *const Key) u32 { |
| | 237 | var result: u32 = 0; |
| | 238 | result +%= hashAny(self.alignment, 0); |
| | 239 | switch (self.data) { |
| | 240 | Data.Generic => |generic| { |
| | 241 | result +%= hashAny(generic.param_count, 1); |
| | 242 | switch (generic.cc) { |
| | 243 | CallingConvention.Async => |allocator_type| result +%= hashAny(allocator_type, 2), |
| | 244 | else => result +%= hashAny(CallingConvention(generic.cc), 3), |
| | 245 | } |
| | 246 | }, |
| | 247 | Data.Normal => |normal| { |
| | 248 | result +%= hashAny(normal.return_type, 4); |
| | 249 | result +%= hashAny(normal.is_var_args, 5); |
| | 250 | result +%= hashAny(normal.cc, 6); |
| | 251 | for (normal.params) |param| { |
| | 252 | result +%= hashAny(param.is_noalias, 7); |
| | 253 | result +%= hashAny(param.typ, 8); |
| | 254 | } |
| | 255 | }, |
| | 256 | } |
| | 257 | return result; |
| | 258 | } |
| | 259 | |
| | 260 | pub fn eql(self: *const Key, other: *const Key) bool { |
| | 261 | if ((self.alignment == null) != (other.alignment == null)) return false; |
| | 262 | if (self.alignment) |self_align| { |
| | 263 | if (self_align != other.alignment.?) return false; |
| | 264 | } |
| | 265 | if (@TagType(Data)(self.data) != @TagType(Data)(other.data)) return false; |
| | 266 | switch (self.data) { |
| | 267 | Data.Generic => |*self_generic| { |
| | 268 | const other_generic = &other.data.Generic; |
| | 269 | if (self_generic.param_count != other_generic.param_count) return false; |
| | 270 | if (CallingConvention(self_generic.cc) != CallingConvention(other_generic.cc)) return false; |
| | 271 | switch (self_generic.cc) { |
| | 272 | CallingConvention.Async => |self_allocator_type| { |
| | 273 | const other_allocator_type = other_generic.cc.Async; |
| | 274 | if (self_allocator_type != other_allocator_type) return false; |
| | 275 | }, |
| | 276 | else => {}, |
| | 277 | } |
| | 278 | }, |
| | 279 | Data.Normal => |*self_normal| { |
| | 280 | const other_normal = &other.data.Normal; |
| | 281 | if (self_normal.cc != other_normal.cc) return false; |
| | 282 | if (self_normal.is_var_args != other_normal.is_var_args) return false; |
| | 283 | if (self_normal.return_type != other_normal.return_type) return false; |
| | 284 | for (self_normal.params) |*self_param, i| { |
| | 285 | const other_param = &other_normal.params[i]; |
| | 286 | if (self_param.is_noalias != other_param.is_noalias) return false; |
| | 287 | if (self_param.typ != other_param.typ) return false; |
| | 288 | } |
| | 289 | }, |
| | 290 | } |
| | 291 | return true; |
| | 292 | } |
| | 293 | |
| | 294 | pub fn deref(key: Key, comp: *Compilation) void { |
| | 295 | switch (key.data) { |
| | 296 | Key.Data.Generic => |generic| { |
| | 297 | switch (generic.cc) { |
| | 298 | CallingConvention.Async => |allocator_type| allocator_type.base.deref(comp), |
| | 299 | else => {}, |
| | 300 | } |
| | 301 | }, |
| | 302 | Key.Data.Normal => |normal| { |
| | 303 | normal.return_type.base.deref(comp); |
| | 304 | for (normal.params) |param| { |
| | 305 | param.typ.base.deref(comp); |
| | 306 | } |
| | 307 | }, |
| | 308 | } |
| | 309 | } |
| | 310 | |
| | 311 | pub fn ref(key: Key) void { |
| | 312 | switch (key.data) { |
| | 313 | Key.Data.Generic => |generic| { |
| | 314 | switch (generic.cc) { |
| | 315 | CallingConvention.Async => |allocator_type| allocator_type.base.ref(), |
| | 316 | else => {}, |
| | 317 | } |
| | 318 | }, |
| | 319 | Key.Data.Normal => |normal| { |
| | 320 | normal.return_type.base.ref(); |
| | 321 | for (normal.params) |param| { |
| | 322 | param.typ.base.ref(); |
| | 323 | } |
| | 324 | }, |
| | 325 | } |
| | 326 | } |
| | 327 | }; |
| | 328 | |
| | 329 | pub const Normal = struct { |
| | 330 | params: []Param, |
| | 331 | return_type: *Type, |
| | 332 | is_var_args: bool, |
| | 333 | cc: CallingConvention, |
| | 334 | }; |
| | 335 | |
| | 336 | pub const Generic = struct { |
| | 337 | param_count: usize, |
| | 338 | cc: CC, |
| | 339 | |
| | 340 | pub const CC = union(CallingConvention) { |
| | 341 | Auto, |
| | 342 | C, |
| | 343 | Cold, |
| | 344 | Naked, |
| | 345 | Stdcall, |
| | 346 | Async: *Type, // allocator type |
| | 347 | }; |
| | 348 | }; |
| | 349 | |
| | 350 | pub const CallingConvention = enum { |
| | 351 | Auto, |
| | 352 | C, |
| | 353 | Cold, |
| | 354 | Naked, |
| | 355 | Stdcall, |
| | 356 | Async, |
| | 357 | }; |
| 227 | | 358 | |
| 228 | pub const Param = struct { | 359 | pub const Param = struct { |
| 229 | is_noalias: bool, | 360 | is_noalias: bool, |
| 230 | typ: *Type, | 361 | typ: *Type, |
| 231 | }; | 362 | }; |
| 232 | | 363 | |
| 233 | pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn { | 364 | fn ccFnTypeStr(cc: CallingConvention) []const u8 { |
| 234 | const result = try comp.gpa().create(Fn{ | 365 | return switch (cc) { |
| | 366 | CallingConvention.Auto => "", |
| | 367 | CallingConvention.C => "extern ", |
| | 368 | CallingConvention.Cold => "coldcc ", |
| | 369 | CallingConvention.Naked => "nakedcc ", |
| | 370 | CallingConvention.Stdcall => "stdcallcc ", |
| | 371 | CallingConvention.Async => unreachable, |
| | 372 | }; |
| | 373 | } |
| | 374 | |
| | 375 | pub fn paramCount(self: *Fn) usize { |
| | 376 | return switch (self.key.data) { |
| | 377 | Key.Data.Generic => |generic| generic.param_count, |
| | 378 | Key.Data.Normal => |normal| normal.params.len, |
| | 379 | }; |
| | 380 | } |
| | 381 | |
| | 382 | /// takes ownership of key.Normal.params on success |
| | 383 | pub async fn get(comp: *Compilation, key: Key) !*Fn { |
| | 384 | { |
| | 385 | const held = await (async comp.fn_type_table.acquire() catch unreachable); |
| | 386 | defer held.release(); |
| | 387 | |
| | 388 | if (held.value.get(&key)) |entry| { |
| | 389 | entry.value.base.base.ref(); |
| | 390 | return entry.value; |
| | 391 | } |
| | 392 | } |
| | 393 | |
| | 394 | key.ref(); |
| | 395 | errdefer key.deref(comp); |
| | 396 | |
| | 397 | const self = try comp.gpa().create(Fn{ |
| 235 | .base = undefined, | 398 | .base = undefined, |
| 236 | .return_type = return_type, | 399 | .key = key, |
| 237 | .params = params, | 400 | .garbage_node = undefined, |
| 238 | .is_var_args = is_var_args, | | |
| 239 | }); | 401 | }); |
| 240 | errdefer comp.gpa().destroy(result); | 402 | errdefer comp.gpa().destroy(self); |
| 241 | | 403 | |
| 242 | result.base.init(comp, Id.Fn, "TODO fn type name"); | 404 | var name_buf = try std.Buffer.initSize(comp.gpa(), 0); |
| | 405 | defer name_buf.deinit(); |
| | 406 | |
| | 407 | const name_stream = &std.io.BufferOutStream.init(&name_buf).stream; |
| | 408 | |
| | 409 | switch (key.data) { |
| | 410 | Key.Data.Generic => |generic| { |
| | 411 | switch (generic.cc) { |
| | 412 | CallingConvention.Async => |async_allocator_type| { |
| | 413 | try name_stream.print("async<{}> ", async_allocator_type.name); |
| | 414 | }, |
| | 415 | else => { |
| | 416 | const cc_str = ccFnTypeStr(generic.cc); |
| | 417 | try name_stream.write(cc_str); |
| | 418 | }, |
| | 419 | } |
| | 420 | try name_stream.write("fn("); |
| | 421 | var param_i: usize = 0; |
| | 422 | while (param_i < generic.param_count) : (param_i += 1) { |
| | 423 | const arg = if (param_i == 0) "var" else ", var"; |
| | 424 | try name_stream.write(arg); |
| | 425 | } |
| | 426 | try name_stream.write(")"); |
| | 427 | if (key.alignment) |alignment| { |
| | 428 | try name_stream.print(" align<{}>", alignment); |
| | 429 | } |
| | 430 | try name_stream.write(" var"); |
| | 431 | }, |
| | 432 | Key.Data.Normal => |normal| { |
| | 433 | const cc_str = ccFnTypeStr(normal.cc); |
| | 434 | try name_stream.print("{}fn(", cc_str); |
| | 435 | for (normal.params) |param, i| { |
| | 436 | if (i != 0) try name_stream.write(", "); |
| | 437 | if (param.is_noalias) try name_stream.write("noalias "); |
| | 438 | try name_stream.write(param.typ.name); |
| | 439 | } |
| | 440 | if (normal.is_var_args) { |
| | 441 | if (normal.params.len != 0) try name_stream.write(", "); |
| | 442 | try name_stream.write("..."); |
| | 443 | } |
| | 444 | try name_stream.write(")"); |
| | 445 | if (key.alignment) |alignment| { |
| | 446 | try name_stream.print(" align<{}>", alignment); |
| | 447 | } |
| | 448 | try name_stream.print(" {}", normal.return_type.name); |
| | 449 | }, |
| | 450 | } |
| | 451 | |
| | 452 | self.base.init(comp, Id.Fn, name_buf.toOwnedSlice()); |
| 243 | | 453 | |
| 244 | result.return_type.base.ref(); | 454 | { |
| 245 | for (result.params) |param| { | 455 | const held = await (async comp.fn_type_table.acquire() catch unreachable); |
| 246 | param.typ.base.ref(); | 456 | defer held.release(); |
| | 457 | |
| | 458 | _ = try held.value.put(&self.key, self); |
| 247 | } | 459 | } |
| 248 | return result; | 460 | return self; |
| 249 | } | 461 | } |
| 250 | | 462 | |
| 251 | pub fn destroy(self: *Fn, comp: *Compilation) void { | 463 | pub fn destroy(self: *Fn, comp: *Compilation) void { |
| 252 | self.return_type.base.deref(comp); | 464 | self.key.deref(comp); |
| 253 | for (self.params) |param| { | | |
| 254 | param.typ.base.deref(comp); | | |
| 255 | } | | |
| 256 | comp.gpa().destroy(self); | 465 | comp.gpa().destroy(self); |
| 257 | } | 466 | } |
| 258 | | 467 | |
| 259 | pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { | 468 | pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { |
| 260 | const llvm_return_type = switch (self.return_type.id) { | 469 | const normal = &self.key.data.Normal; |
| | 470 | const llvm_return_type = switch (normal.return_type.id) { |
| 261 | Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory, | 471 | Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory, |
| 262 | else => try self.return_type.getLlvmType(allocator, llvm_context), | 472 | else => try normal.return_type.getLlvmType(allocator, llvm_context), |
| 263 | }; | 473 | }; |
| 264 | const llvm_param_types = try allocator.alloc(llvm.TypeRef, self.params.len); | 474 | const llvm_param_types = try allocator.alloc(llvm.TypeRef, normal.params.len); |
| 265 | defer allocator.free(llvm_param_types); | 475 | defer allocator.free(llvm_param_types); |
| 266 | for (llvm_param_types) |*llvm_param_type, i| { | 476 | for (llvm_param_types) |*llvm_param_type, i| { |
| 267 | llvm_param_type.* = try self.params[i].typ.getLlvmType(allocator, llvm_context); | 477 | llvm_param_type.* = try normal.params[i].typ.getLlvmType(allocator, llvm_context); |
| 268 | } | 478 | } |
| 269 | | 479 | |
| 270 | return llvm.FunctionType( | 480 | return llvm.FunctionType( |
| 271 | llvm_return_type, | 481 | llvm_return_type, |
| 272 | llvm_param_types.ptr, | 482 | llvm_param_types.ptr, |
| 273 | @intCast(c_uint, llvm_param_types.len), | 483 | @intCast(c_uint, llvm_param_types.len), |
| 274 | @boolToInt(self.is_var_args), | 484 | @boolToInt(normal.is_var_args), |
| 275 | ) orelse error.OutOfMemory; | 485 | ) orelse error.OutOfMemory; |
| 276 | } | 486 | } |
| 277 | }; | 487 | }; |
| ... | @@ -347,8 +557,10 @@ pub const Type = struct { | ... | @@ -347,8 +557,10 @@ pub const Type = struct { |
| 347 | is_signed: bool, | 557 | is_signed: bool, |
| 348 | | 558 | |
| 349 | pub fn hash(self: *const Key) u32 { | 559 | pub fn hash(self: *const Key) u32 { |
| 350 | const rands = [2]u32{ 0xa4ba6498, 0x75fc5af7 }; | 560 | var result: u32 = 0; |
| 351 | return rands[@boolToInt(self.is_signed)] *% self.bit_count; | 561 | result +%= hashAny(self.is_signed, 0); |
| | 562 | result +%= hashAny(self.bit_count, 1); |
| | 563 | return result; |
| 352 | } | 564 | } |
| 353 | | 565 | |
| 354 | pub fn eql(self: *const Key, other: *const Key) bool { | 566 | pub fn eql(self: *const Key, other: *const Key) bool { |
| ... | @@ -443,15 +655,16 @@ pub const Type = struct { | ... | @@ -443,15 +655,16 @@ pub const Type = struct { |
| 443 | alignment: Align, | 655 | alignment: Align, |
| 444 | | 656 | |
| 445 | pub fn hash(self: *const Key) u32 { | 657 | pub fn hash(self: *const Key) u32 { |
| 446 | const align_hash = switch (self.alignment) { | 658 | var result: u32 = 0; |
| | 659 | result +%= switch (self.alignment) { |
| 447 | Align.Abi => 0xf201c090, | 660 | Align.Abi => 0xf201c090, |
| 448 | Align.Override => |x| x, | 661 | Align.Override => |x| hashAny(x, 0), |
| 449 | }; | 662 | }; |
| 450 | return hash_usize(@ptrToInt(self.child_type)) *% | 663 | result +%= hashAny(self.child_type, 1); |
| 451 | hash_enum(self.mut) *% | 664 | result +%= hashAny(self.mut, 2); |
| 452 | hash_enum(self.vol) *% | 665 | result +%= hashAny(self.vol, 3); |
| 453 | hash_enum(self.size) *% | 666 | result +%= hashAny(self.size, 4); |
| 454 | align_hash; | 667 | return result; |
| 455 | } | 668 | } |
| 456 | | 669 | |
| 457 | pub fn eql(self: *const Key, other: *const Key) bool { | 670 | pub fn eql(self: *const Key, other: *const Key) bool { |
| ... | @@ -605,7 +818,10 @@ pub const Type = struct { | ... | @@ -605,7 +818,10 @@ pub const Type = struct { |
| 605 | len: usize, | 818 | len: usize, |
| 606 | | 819 | |
| 607 | pub fn hash(self: *const Key) u32 { | 820 | pub fn hash(self: *const Key) u32 { |
| 608 | return hash_usize(@ptrToInt(self.elem_type)) *% hash_usize(self.len); | 821 | var result: u32 = 0; |
| | 822 | result +%= hashAny(self.elem_type, 0); |
| | 823 | result +%= hashAny(self.len, 1); |
| | 824 | return result; |
| 609 | } | 825 | } |
| 610 | | 826 | |
| 611 | pub fn eql(self: *const Key, other: *const Key) bool { | 827 | pub fn eql(self: *const Key, other: *const Key) bool { |
| ... | @@ -818,27 +1034,37 @@ pub const Type = struct { | ... | @@ -818,27 +1034,37 @@ pub const Type = struct { |
| 818 | }; | 1034 | }; |
| 819 | }; | 1035 | }; |
| 820 | | 1036 | |
| 821 | fn hash_usize(x: usize) u32 { | 1037 | fn hashAny(x: var, comptime seed: u64) u32 { |
| 822 | return switch (@sizeOf(usize)) { | 1038 | switch (@typeInfo(@typeOf(x))) { |
| 823 | 4 => x, | 1039 | builtin.TypeId.Int => |info| { |
| 824 | 8 => @truncate(u32, x *% 0xad44ee2d8e3fc13d), | 1040 | comptime var rng = comptime std.rand.DefaultPrng.init(seed); |
| 825 | else => @compileError("implement this hash function"), | 1041 | const unsigned_x = @bitCast(@IntType(false, info.bits), x); |
| 826 | }; | 1042 | if (info.bits <= 32) { |
| 827 | } | 1043 | return u32(unsigned_x) *% comptime rng.random.scalar(u32); |
| 828 | | 1044 | } else { |
| 829 | fn hash_enum(x: var) u32 { | 1045 | return @truncate(u32, unsigned_x *% comptime rng.random.scalar(@typeOf(unsigned_x))); |
| 830 | const rands = []u32{ | 1046 | } |
| 831 | 0x85ebf64f, | 1047 | }, |
| 832 | 0x3fcb3211, | 1048 | builtin.TypeId.Pointer => |info| { |
| 833 | 0x240a4e8e, | 1049 | switch (info.size) { |
| 834 | 0x40bb0e3c, | 1050 | builtin.TypeInfo.Pointer.Size.One => return hashAny(@ptrToInt(x), seed), |
| 835 | 0x78be45af, | 1051 | builtin.TypeInfo.Pointer.Size.Many => @compileError("implement hash function"), |
| 836 | 0x1ca98e37, | 1052 | builtin.TypeInfo.Pointer.Size.Slice => @compileError("implement hash function"), |
| 837 | 0xec56053a, | 1053 | } |
| 838 | 0x906adc48, | 1054 | }, |
| 839 | 0xd4fe9763, | 1055 | builtin.TypeId.Enum => return hashAny(@enumToInt(x), seed), |
| 840 | 0x54c80dac, | 1056 | builtin.TypeId.Bool => { |
| 841 | }; | 1057 | comptime var rng = comptime std.rand.DefaultPrng.init(seed); |
| 842 | comptime assert(@memberCount(@typeOf(x)) < rands.len); | 1058 | const vals = comptime [2]u32{ rng.random.scalar(u32), rng.random.scalar(u32) }; |
| 843 | return rands[@enumToInt(x)]; | 1059 | return vals[@boolToInt(x)]; |
| | 1060 | }, |
| | 1061 | builtin.TypeId.Optional => { |
| | 1062 | if (x) |non_opt| { |
| | 1063 | return hashAny(non_opt, seed); |
| | 1064 | } else { |
| | 1065 | return hashAny(u32(1), seed); |
| | 1066 | } |
| | 1067 | }, |
| | 1068 | else => @compileError("implement hash function for " ++ @typeName(@typeOf(x))), |
| | 1069 | } |
| 844 | } | 1070 | } |