| ... | ... | @@ -221,57 +221,267 @@ pub const Type = struct { |
| 221 | 221 | |
| 222 | 222 | pub const Fn = struct { |
| 223 | 223 | base: Type, |
| 224 | | return_type: *Type, |
| 225 | | params: []Param, |
| 226 | | is_var_args: bool, |
| 224 | key: Key, |
| 225 | garbage_node: std.atomic.Stack(*Fn).Node, |
| 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 | 359 | pub const Param = struct { |
| 229 | 360 | is_noalias: bool, |
| 230 | 361 | typ: *Type, |
| 231 | 362 | }; |
| 232 | 363 | |
| 233 | | pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn { |
| 234 | | const result = try comp.gpa().create(Fn{ |
| 364 | fn ccFnTypeStr(cc: CallingConvention) []const u8 { |
| 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 | 398 | .base = undefined, |
| 236 | | .return_type = return_type, |
| 237 | | .params = params, |
| 238 | | .is_var_args = is_var_args, |
| 399 | .key = key, |
| 400 | .garbage_node = undefined, |
| 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(); |
| 245 | | for (result.params) |param| { |
| 246 | | param.typ.base.ref(); |
| 454 | { |
| 455 | const held = await (async comp.fn_type_table.acquire() catch unreachable); |
| 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 | 463 | pub fn destroy(self: *Fn, comp: *Compilation) void { |
| 252 | | self.return_type.base.deref(comp); |
| 253 | | for (self.params) |param| { |
| 254 | | param.typ.base.deref(comp); |
| 255 | | } |
| 464 | self.key.deref(comp); |
| 256 | 465 | comp.gpa().destroy(self); |
| 257 | 466 | } |
| 258 | 467 | |
| 259 | 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 | 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 | 475 | defer allocator.free(llvm_param_types); |
| 266 | 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 | 480 | return llvm.FunctionType( |
| 271 | 481 | llvm_return_type, |
| 272 | 482 | llvm_param_types.ptr, |
| 273 | 483 | @intCast(c_uint, llvm_param_types.len), |
| 274 | | @boolToInt(self.is_var_args), |
| 484 | @boolToInt(normal.is_var_args), |
| 275 | 485 | ) orelse error.OutOfMemory; |
| 276 | 486 | } |
| 277 | 487 | }; |
| ... | ... | @@ -347,8 +557,10 @@ pub const Type = struct { |
| 347 | 557 | is_signed: bool, |
| 348 | 558 | |
| 349 | 559 | pub fn hash(self: *const Key) u32 { |
| 350 | | const rands = [2]u32{ 0xa4ba6498, 0x75fc5af7 }; |
| 351 | | return rands[@boolToInt(self.is_signed)] *% self.bit_count; |
| 560 | var result: u32 = 0; |
| 561 | result +%= hashAny(self.is_signed, 0); |
| 562 | result +%= hashAny(self.bit_count, 1); |
| 563 | return result; |
| 352 | 564 | } |
| 353 | 565 | |
| 354 | 566 | pub fn eql(self: *const Key, other: *const Key) bool { |
| ... | ... | @@ -443,15 +655,16 @@ pub const Type = struct { |
| 443 | 655 | alignment: Align, |
| 444 | 656 | |
| 445 | 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 | 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)) *% |
| 451 | | hash_enum(self.mut) *% |
| 452 | | hash_enum(self.vol) *% |
| 453 | | hash_enum(self.size) *% |
| 454 | | align_hash; |
| 663 | result +%= hashAny(self.child_type, 1); |
| 664 | result +%= hashAny(self.mut, 2); |
| 665 | result +%= hashAny(self.vol, 3); |
| 666 | result +%= hashAny(self.size, 4); |
| 667 | return result; |
| 455 | 668 | } |
| 456 | 669 | |
| 457 | 670 | pub fn eql(self: *const Key, other: *const Key) bool { |
| ... | ... | @@ -605,7 +818,10 @@ pub const Type = struct { |
| 605 | 818 | len: usize, |
| 606 | 819 | |
| 607 | 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 | 827 | pub fn eql(self: *const Key, other: *const Key) bool { |
| ... | ... | @@ -818,27 +1034,37 @@ pub const Type = struct { |
| 818 | 1034 | }; |
| 819 | 1035 | }; |
| 820 | 1036 | |
| 821 | | fn hash_usize(x: usize) u32 { |
| 822 | | return switch (@sizeOf(usize)) { |
| 823 | | 4 => x, |
| 824 | | 8 => @truncate(u32, x *% 0xad44ee2d8e3fc13d), |
| 825 | | else => @compileError("implement this hash function"), |
| 826 | | }; |
| 827 | | } |
| 828 | | |
| 829 | | fn hash_enum(x: var) u32 { |
| 830 | | const rands = []u32{ |
| 831 | | 0x85ebf64f, |
| 832 | | 0x3fcb3211, |
| 833 | | 0x240a4e8e, |
| 834 | | 0x40bb0e3c, |
| 835 | | 0x78be45af, |
| 836 | | 0x1ca98e37, |
| 837 | | 0xec56053a, |
| 838 | | 0x906adc48, |
| 839 | | 0xd4fe9763, |
| 840 | | 0x54c80dac, |
| 841 | | }; |
| 842 | | comptime assert(@memberCount(@typeOf(x)) < rands.len); |
| 843 | | return rands[@enumToInt(x)]; |
| 1037 | fn hashAny(x: var, comptime seed: u64) u32 { |
| 1038 | switch (@typeInfo(@typeOf(x))) { |
| 1039 | builtin.TypeId.Int => |info| { |
| 1040 | comptime var rng = comptime std.rand.DefaultPrng.init(seed); |
| 1041 | const unsigned_x = @bitCast(@IntType(false, info.bits), x); |
| 1042 | if (info.bits <= 32) { |
| 1043 | return u32(unsigned_x) *% comptime rng.random.scalar(u32); |
| 1044 | } else { |
| 1045 | return @truncate(u32, unsigned_x *% comptime rng.random.scalar(@typeOf(unsigned_x))); |
| 1046 | } |
| 1047 | }, |
| 1048 | builtin.TypeId.Pointer => |info| { |
| 1049 | switch (info.size) { |
| 1050 | builtin.TypeInfo.Pointer.Size.One => return hashAny(@ptrToInt(x), seed), |
| 1051 | builtin.TypeInfo.Pointer.Size.Many => @compileError("implement hash function"), |
| 1052 | builtin.TypeInfo.Pointer.Size.Slice => @compileError("implement hash function"), |
| 1053 | } |
| 1054 | }, |
| 1055 | builtin.TypeId.Enum => return hashAny(@enumToInt(x), seed), |
| 1056 | builtin.TypeId.Bool => { |
| 1057 | comptime var rng = comptime std.rand.DefaultPrng.init(seed); |
| 1058 | const vals = comptime [2]u32{ rng.random.scalar(u32), rng.random.scalar(u32) }; |
| 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 | } |