| ... | @@ -160,6 +160,7 @@ pub const Object = struct { | ... | @@ -160,6 +160,7 @@ pub const Object = struct { |
| 160 | llvm_module: *const llvm.Module, | 160 | llvm_module: *const llvm.Module, |
| 161 | context: *const llvm.Context, | 161 | context: *const llvm.Context, |
| 162 | target_machine: *const llvm.TargetMachine, | 162 | target_machine: *const llvm.TargetMachine, |
| | 163 | target_data: *const llvm.TargetData, |
| 163 | /// Ideally we would use `llvm_module.getNamedFunction` to go from *Decl to LLVM function, | 164 | /// Ideally we would use `llvm_module.getNamedFunction` to go from *Decl to LLVM function, |
| 164 | /// but that has some downsides: | 165 | /// but that has some downsides: |
| 165 | /// * we have to compute the fully qualified name every time we want to do the lookup | 166 | /// * we have to compute the fully qualified name every time we want to do the lookup |
| ... | @@ -258,7 +259,7 @@ pub const Object = struct { | ... | @@ -258,7 +259,7 @@ pub const Object = struct { |
| 258 | errdefer target_machine.dispose(); | 259 | errdefer target_machine.dispose(); |
| 259 | | 260 | |
| 260 | const target_data = target_machine.createTargetDataLayout(); | 261 | const target_data = target_machine.createTargetDataLayout(); |
| 261 | defer target_data.dispose(); | 262 | errdefer target_data.dispose(); |
| 262 | | 263 | |
| 263 | llvm_module.setModuleDataLayout(target_data); | 264 | llvm_module.setModuleDataLayout(target_data); |
| 264 | | 265 | |
| ... | @@ -266,6 +267,7 @@ pub const Object = struct { | ... | @@ -266,6 +267,7 @@ pub const Object = struct { |
| 266 | .llvm_module = llvm_module, | 267 | .llvm_module = llvm_module, |
| 267 | .context = context, | 268 | .context = context, |
| 268 | .target_machine = target_machine, | 269 | .target_machine = target_machine, |
| | 270 | .target_data = target_data, |
| 269 | .decl_map = .{}, | 271 | .decl_map = .{}, |
| 270 | .type_map = .{}, | 272 | .type_map = .{}, |
| 271 | .type_map_arena = std.heap.ArenaAllocator.init(gpa), | 273 | .type_map_arena = std.heap.ArenaAllocator.init(gpa), |
| ... | @@ -274,6 +276,7 @@ pub const Object = struct { | ... | @@ -274,6 +276,7 @@ pub const Object = struct { |
| 274 | } | 276 | } |
| 275 | | 277 | |
| 276 | pub fn deinit(self: *Object, gpa: Allocator) void { | 278 | pub fn deinit(self: *Object, gpa: Allocator) void { |
| | 279 | self.target_data.dispose(); |
| 277 | self.target_machine.dispose(); | 280 | self.target_machine.dispose(); |
| 278 | self.llvm_module.dispose(); | 281 | self.llvm_module.dispose(); |
| 279 | self.context.dispose(); | 282 | self.context.dispose(); |
| ... | @@ -955,20 +958,55 @@ pub const DeclGen = struct { | ... | @@ -955,20 +958,55 @@ pub const DeclGen = struct { |
| 955 | // reference, we need to copy it here. | 958 | // reference, we need to copy it here. |
| 956 | gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator()); | 959 | gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator()); |
| 957 | | 960 | |
| 958 | if (t.castTag(.tuple)) |tuple| { | 961 | if (t.isTuple()) { |
| | 962 | const tuple = t.tupleFields(); |
| 959 | const llvm_struct_ty = dg.context.structCreateNamed(""); | 963 | const llvm_struct_ty = dg.context.structCreateNamed(""); |
| 960 | gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls | 964 | gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls |
| 961 | | 965 | |
| 962 | const types = tuple.data.types; | 966 | var llvm_field_types: std.ArrayListUnmanaged(*const llvm.Type) = .{}; |
| 963 | const values = tuple.data.values; | | |
| 964 | var llvm_field_types = try std.ArrayListUnmanaged(*const llvm.Type).initCapacity(gpa, types.len); | | |
| 965 | defer llvm_field_types.deinit(gpa); | 967 | defer llvm_field_types.deinit(gpa); |
| 966 | | 968 | |
| 967 | for (types) |field_ty, i| { | 969 | try llvm_field_types.ensureUnusedCapacity(gpa, tuple.types.len); |
| 968 | const field_val = values[i]; | 970 | |
| | 971 | // We need to insert extra padding if LLVM's isn't enough. |
| | 972 | var zig_offset: u64 = 0; |
| | 973 | var llvm_offset: u64 = 0; |
| | 974 | var zig_big_align: u32 = 0; |
| | 975 | var llvm_big_align: u32 = 0; |
| | 976 | |
| | 977 | for (tuple.types) |field_ty, i| { |
| | 978 | const field_val = tuple.values[i]; |
| 969 | if (field_val.tag() != .unreachable_value) continue; | 979 | if (field_val.tag() != .unreachable_value) continue; |
| 970 | | 980 | |
| 971 | llvm_field_types.appendAssumeCapacity(try dg.llvmType(field_ty)); | 981 | const field_align = field_ty.abiAlignment(target); |
| | 982 | zig_big_align = @maximum(zig_big_align, field_align); |
| | 983 | zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, field_align); |
| | 984 | |
| | 985 | const field_llvm_ty = try dg.llvmType(field_ty); |
| | 986 | const field_llvm_align = dg.object.target_data.ABIAlignmentOfType(field_llvm_ty); |
| | 987 | llvm_big_align = @maximum(llvm_big_align, field_llvm_align); |
| | 988 | llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, field_llvm_align); |
| | 989 | |
| | 990 | const padding_len = @intCast(c_uint, zig_offset - llvm_offset); |
| | 991 | if (padding_len > 0) { |
| | 992 | const llvm_array_ty = dg.context.intType(8).arrayType(padding_len); |
| | 993 | try llvm_field_types.append(gpa, llvm_array_ty); |
| | 994 | llvm_offset = zig_offset; |
| | 995 | } |
| | 996 | try llvm_field_types.append(gpa, field_llvm_ty); |
| | 997 | |
| | 998 | llvm_offset += dg.object.target_data.ABISizeOfType(field_llvm_ty); |
| | 999 | zig_offset += field_ty.abiSize(target); |
| | 1000 | } |
| | 1001 | { |
| | 1002 | zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, zig_big_align); |
| | 1003 | llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, llvm_big_align); |
| | 1004 | const padding_len = @intCast(c_uint, zig_offset - llvm_offset); |
| | 1005 | if (padding_len > 0) { |
| | 1006 | const llvm_array_ty = dg.context.intType(8).arrayType(padding_len); |
| | 1007 | try llvm_field_types.append(gpa, llvm_array_ty); |
| | 1008 | llvm_offset = zig_offset; |
| | 1009 | } |
| 972 | } | 1010 | } |
| 973 | | 1011 | |
| 974 | llvm_struct_ty.structSetBody( | 1012 | llvm_struct_ty.structSetBody( |
| ... | @@ -998,12 +1036,49 @@ pub const DeclGen = struct { | ... | @@ -998,12 +1036,49 @@ pub const DeclGen = struct { |
| 998 | | 1036 | |
| 999 | assert(struct_obj.haveFieldTypes()); | 1037 | assert(struct_obj.haveFieldTypes()); |
| 1000 | | 1038 | |
| 1001 | var llvm_field_types = try std.ArrayListUnmanaged(*const llvm.Type).initCapacity(gpa, struct_obj.fields.count()); | 1039 | var llvm_field_types: std.ArrayListUnmanaged(*const llvm.Type) = .{}; |
| 1002 | defer llvm_field_types.deinit(gpa); | 1040 | defer llvm_field_types.deinit(gpa); |
| 1003 | | 1041 | |
| | 1042 | try llvm_field_types.ensureUnusedCapacity(gpa, struct_obj.fields.count()); |
| | 1043 | |
| | 1044 | // We need to insert extra padding if LLVM's isn't enough. |
| | 1045 | var zig_offset: u64 = 0; |
| | 1046 | var llvm_offset: u64 = 0; |
| | 1047 | var zig_big_align: u32 = 0; |
| | 1048 | var llvm_big_align: u32 = 0; |
| | 1049 | |
| 1004 | for (struct_obj.fields.values()) |field| { | 1050 | for (struct_obj.fields.values()) |field| { |
| 1005 | if (!field.ty.hasRuntimeBits()) continue; | 1051 | if (field.is_comptime or !field.ty.hasRuntimeBits()) continue; |
| 1006 | llvm_field_types.appendAssumeCapacity(try dg.llvmType(field.ty)); | 1052 | |
| | 1053 | const field_align = field.normalAlignment(target); |
| | 1054 | zig_big_align = @maximum(zig_big_align, field_align); |
| | 1055 | zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, field_align); |
| | 1056 | |
| | 1057 | const field_llvm_ty = try dg.llvmType(field.ty); |
| | 1058 | const field_llvm_align = dg.object.target_data.ABIAlignmentOfType(field_llvm_ty); |
| | 1059 | llvm_big_align = @maximum(llvm_big_align, field_llvm_align); |
| | 1060 | llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, field_llvm_align); |
| | 1061 | |
| | 1062 | const padding_len = @intCast(c_uint, zig_offset - llvm_offset); |
| | 1063 | if (padding_len > 0) { |
| | 1064 | const llvm_array_ty = dg.context.intType(8).arrayType(padding_len); |
| | 1065 | try llvm_field_types.append(gpa, llvm_array_ty); |
| | 1066 | llvm_offset = zig_offset; |
| | 1067 | } |
| | 1068 | try llvm_field_types.append(gpa, field_llvm_ty); |
| | 1069 | |
| | 1070 | llvm_offset += dg.object.target_data.ABISizeOfType(field_llvm_ty); |
| | 1071 | zig_offset += field.ty.abiSize(target); |
| | 1072 | } |
| | 1073 | { |
| | 1074 | zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, zig_big_align); |
| | 1075 | llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, llvm_big_align); |
| | 1076 | const padding_len = @intCast(c_uint, zig_offset - llvm_offset); |
| | 1077 | if (padding_len > 0) { |
| | 1078 | const llvm_array_ty = dg.context.intType(8).arrayType(padding_len); |
| | 1079 | try llvm_field_types.append(gpa, llvm_array_ty); |
| | 1080 | llvm_offset = zig_offset; |
| | 1081 | } |
| 1007 | } | 1082 | } |
| 1008 | | 1083 | |
| 1009 | llvm_struct_ty.structSetBody( | 1084 | llvm_struct_ty.structSetBody( |
| ... | @@ -1050,7 +1125,7 @@ pub const DeclGen = struct { | ... | @@ -1050,7 +1125,7 @@ pub const DeclGen = struct { |
| 1050 | llvm_aligned_field_ty, | 1125 | llvm_aligned_field_ty, |
| 1051 | dg.context.intType(8).arrayType(padding_len), | 1126 | dg.context.intType(8).arrayType(padding_len), |
| 1052 | }; | 1127 | }; |
| 1053 | break :t dg.context.structType(&fields, fields.len, .False); | 1128 | break :t dg.context.structType(&fields, fields.len, .True); |
| 1054 | }; | 1129 | }; |
| 1055 | | 1130 | |
| 1056 | if (layout.tag_size == 0) { | 1131 | if (layout.tag_size == 0) { |
| ... | @@ -1461,9 +1536,9 @@ pub const DeclGen = struct { | ... | @@ -1461,9 +1536,9 @@ pub const DeclGen = struct { |
| 1461 | const field_vals = tv.val.castTag(.@"struct").?.data; | 1536 | const field_vals = tv.val.castTag(.@"struct").?.data; |
| 1462 | const gpa = dg.gpa; | 1537 | const gpa = dg.gpa; |
| 1463 | const struct_obj = tv.ty.castTag(.@"struct").?.data; | 1538 | const struct_obj = tv.ty.castTag(.@"struct").?.data; |
| | 1539 | const target = dg.module.getTarget(); |
| 1464 | | 1540 | |
| 1465 | if (struct_obj.layout == .Packed) { | 1541 | if (struct_obj.layout == .Packed) { |
| 1466 | const target = dg.module.getTarget(); | | |
| 1467 | const big_bits = struct_obj.packedIntegerBits(target); | 1542 | const big_bits = struct_obj.packedIntegerBits(target); |
| 1468 | const int_llvm_ty = dg.context.intType(big_bits); | 1543 | const int_llvm_ty = dg.context.intType(big_bits); |
| 1469 | const fields = struct_obj.fields.values(); | 1544 | const fields = struct_obj.fields.values(); |
| ... | @@ -1497,19 +1572,56 @@ pub const DeclGen = struct { | ... | @@ -1497,19 +1572,56 @@ pub const DeclGen = struct { |
| 1497 | var llvm_fields = try std.ArrayListUnmanaged(*const llvm.Value).initCapacity(gpa, llvm_field_count); | 1572 | var llvm_fields = try std.ArrayListUnmanaged(*const llvm.Value).initCapacity(gpa, llvm_field_count); |
| 1498 | defer llvm_fields.deinit(gpa); | 1573 | defer llvm_fields.deinit(gpa); |
| 1499 | | 1574 | |
| | 1575 | // These are used to detect where the extra padding fields are so that we |
| | 1576 | // can initialize them with undefined. |
| | 1577 | var zig_offset: u64 = 0; |
| | 1578 | var llvm_offset: u64 = 0; |
| | 1579 | var zig_big_align: u32 = 0; |
| | 1580 | var llvm_big_align: u32 = 0; |
| | 1581 | |
| 1500 | var need_unnamed = false; | 1582 | var need_unnamed = false; |
| 1501 | for (field_vals) |field_val, i| { | 1583 | for (struct_obj.fields.values()) |field, i| { |
| 1502 | const field_ty = tv.ty.structFieldType(i); | 1584 | if (field.is_comptime or !field.ty.hasRuntimeBits()) continue; |
| 1503 | if (!field_ty.hasRuntimeBits()) continue; | 1585 | |
| | 1586 | const field_align = field.normalAlignment(target); |
| | 1587 | zig_big_align = @maximum(zig_big_align, field_align); |
| | 1588 | zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, field_align); |
| | 1589 | |
| | 1590 | const field_llvm_ty = try dg.llvmType(field.ty); |
| | 1591 | const field_llvm_align = dg.object.target_data.ABIAlignmentOfType(field_llvm_ty); |
| | 1592 | llvm_big_align = @maximum(llvm_big_align, field_llvm_align); |
| | 1593 | llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, field_llvm_align); |
| | 1594 | |
| | 1595 | const padding_len = @intCast(c_uint, zig_offset - llvm_offset); |
| | 1596 | if (padding_len > 0) { |
| | 1597 | const llvm_array_ty = dg.context.intType(8).arrayType(padding_len); |
| | 1598 | // TODO make this and all other padding elsewhere in debug |
| | 1599 | // builds be 0xaa not undef. |
| | 1600 | llvm_fields.appendAssumeCapacity(llvm_array_ty.getUndef()); |
| | 1601 | llvm_offset = zig_offset; |
| | 1602 | } |
| 1504 | | 1603 | |
| 1505 | const field_llvm_val = try dg.genTypedValue(.{ | 1604 | const field_llvm_val = try dg.genTypedValue(.{ |
| 1506 | .ty = field_ty, | 1605 | .ty = field.ty, |
| 1507 | .val = field_val, | 1606 | .val = field_vals[i], |
| 1508 | }); | 1607 | }); |
| 1509 | | 1608 | |
| 1510 | need_unnamed = need_unnamed or dg.isUnnamedType(field_ty, field_llvm_val); | 1609 | need_unnamed = need_unnamed or dg.isUnnamedType(field.ty, field_llvm_val); |
| 1511 | | 1610 | |
| 1512 | llvm_fields.appendAssumeCapacity(field_llvm_val); | 1611 | llvm_fields.appendAssumeCapacity(field_llvm_val); |
| | 1612 | |
| | 1613 | llvm_offset += dg.object.target_data.ABISizeOfType(field_llvm_ty); |
| | 1614 | zig_offset += field.ty.abiSize(target); |
| | 1615 | } |
| | 1616 | { |
| | 1617 | zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, zig_big_align); |
| | 1618 | llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, llvm_big_align); |
| | 1619 | const padding_len = @intCast(c_uint, zig_offset - llvm_offset); |
| | 1620 | if (padding_len > 0) { |
| | 1621 | const llvm_array_ty = dg.context.intType(8).arrayType(padding_len); |
| | 1622 | llvm_fields.appendAssumeCapacity(llvm_array_ty.getUndef()); |
| | 1623 | llvm_offset = zig_offset; |
| | 1624 | } |
| 1513 | } | 1625 | } |
| 1514 | | 1626 | |
| 1515 | if (need_unnamed) { | 1627 | if (need_unnamed) { |
| ... | @@ -1556,7 +1668,7 @@ pub const DeclGen = struct { | ... | @@ -1556,7 +1668,7 @@ pub const DeclGen = struct { |
| 1556 | const fields: [2]*const llvm.Value = .{ | 1668 | const fields: [2]*const llvm.Value = .{ |
| 1557 | field, dg.context.intType(8).arrayType(padding_len).getUndef(), | 1669 | field, dg.context.intType(8).arrayType(padding_len).getUndef(), |
| 1558 | }; | 1670 | }; |
| 1559 | break :p dg.context.constStruct(&fields, fields.len, .False); | 1671 | break :p dg.context.constStruct(&fields, fields.len, .True); |
| 1560 | }; | 1672 | }; |
| 1561 | | 1673 | |
| 1562 | // In this case we must make an unnamed struct because LLVM does | 1674 | // In this case we must make an unnamed struct because LLVM does |
| ... | @@ -1741,7 +1853,7 @@ pub const DeclGen = struct { | ... | @@ -1741,7 +1853,7 @@ pub const DeclGen = struct { |
| 1741 | }, | 1853 | }, |
| 1742 | .Struct => { | 1854 | .Struct => { |
| 1743 | var ty_buf: Type.Payload.Pointer = undefined; | 1855 | var ty_buf: Type.Payload.Pointer = undefined; |
| 1744 | const llvm_field_index = llvmFieldIndex(parent.ty, field_index, target, &ty_buf).?; | 1856 | const llvm_field_index = dg.llvmFieldIndex(parent.ty, field_index, &ty_buf).?; |
| 1745 | const indices: [2]*const llvm.Value = .{ | 1857 | const indices: [2]*const llvm.Value = .{ |
| 1746 | llvm_u32.constInt(0, .False), | 1858 | llvm_u32.constInt(0, .False), |
| 1747 | llvm_u32.constInt(llvm_field_index, .False), | 1859 | llvm_u32.constInt(llvm_field_index, .False), |
| ... | @@ -1972,6 +2084,107 @@ pub const DeclGen = struct { | ... | @@ -1972,6 +2084,107 @@ pub const DeclGen = struct { |
| 1972 | return null; | 2084 | return null; |
| 1973 | } | 2085 | } |
| 1974 | } | 2086 | } |
| | 2087 | |
| | 2088 | /// Take into account 0 bit fields and padding. Returns null if an llvm |
| | 2089 | /// field could not be found. |
| | 2090 | /// This only happens if you want the field index of a zero sized field at |
| | 2091 | /// the end of the struct. |
| | 2092 | fn llvmFieldIndex( |
| | 2093 | dg: *DeclGen, |
| | 2094 | ty: Type, |
| | 2095 | field_index: u32, |
| | 2096 | ptr_pl_buf: *Type.Payload.Pointer, |
| | 2097 | ) ?c_uint { |
| | 2098 | const target = dg.module.getTarget(); |
| | 2099 | |
| | 2100 | // Detects where we inserted extra padding fields so that we can skip |
| | 2101 | // over them in this function. |
| | 2102 | var zig_offset: u64 = 0; |
| | 2103 | var llvm_offset: u64 = 0; |
| | 2104 | var zig_big_align: u32 = 0; |
| | 2105 | var llvm_big_align: u32 = 0; |
| | 2106 | |
| | 2107 | if (ty.isTuple()) { |
| | 2108 | const tuple = ty.tupleFields(); |
| | 2109 | var llvm_field_index: c_uint = 0; |
| | 2110 | for (tuple.types) |field_ty, i| { |
| | 2111 | if (tuple.values[i].tag() != .unreachable_value) continue; |
| | 2112 | |
| | 2113 | const field_align = field_ty.abiAlignment(target); |
| | 2114 | zig_big_align = @maximum(zig_big_align, field_align); |
| | 2115 | zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, field_align); |
| | 2116 | |
| | 2117 | // assert no error because we have already seen a successful |
| | 2118 | // llvmType on this field. |
| | 2119 | const field_llvm_ty = dg.llvmType(field_ty) catch unreachable; |
| | 2120 | const field_llvm_align = dg.object.target_data.ABIAlignmentOfType(field_llvm_ty); |
| | 2121 | llvm_big_align = @maximum(llvm_big_align, field_llvm_align); |
| | 2122 | llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, field_llvm_align); |
| | 2123 | |
| | 2124 | const padding_len = @intCast(c_uint, zig_offset - llvm_offset); |
| | 2125 | if (padding_len > 0) { |
| | 2126 | llvm_field_index += 1; |
| | 2127 | llvm_offset = zig_offset; |
| | 2128 | } |
| | 2129 | |
| | 2130 | if (field_index == i) { |
| | 2131 | ptr_pl_buf.* = .{ |
| | 2132 | .data = .{ |
| | 2133 | .pointee_type = field_ty, |
| | 2134 | .@"align" = field_align, |
| | 2135 | .@"addrspace" = .generic, |
| | 2136 | }, |
| | 2137 | }; |
| | 2138 | return llvm_field_index; |
| | 2139 | } |
| | 2140 | |
| | 2141 | llvm_field_index += 1; |
| | 2142 | llvm_offset += dg.object.target_data.ABISizeOfType(field_llvm_ty); |
| | 2143 | zig_offset += field_ty.abiSize(target); |
| | 2144 | } |
| | 2145 | return null; |
| | 2146 | } |
| | 2147 | assert(ty.containerLayout() != .Packed); |
| | 2148 | |
| | 2149 | var llvm_field_index: c_uint = 0; |
| | 2150 | for (ty.structFields().values()) |field, i| { |
| | 2151 | if (field.is_comptime or !field.ty.hasRuntimeBits()) continue; |
| | 2152 | |
| | 2153 | const field_align = field.normalAlignment(target); |
| | 2154 | zig_big_align = @maximum(zig_big_align, field_align); |
| | 2155 | zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, field_align); |
| | 2156 | |
| | 2157 | // assert no error because we have already seen a successful llvmType on this field. |
| | 2158 | const field_llvm_ty = dg.llvmType(field.ty) catch unreachable; |
| | 2159 | const field_llvm_align = dg.object.target_data.ABIAlignmentOfType(field_llvm_ty); |
| | 2160 | llvm_big_align = @maximum(llvm_big_align, field_llvm_align); |
| | 2161 | llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, field_llvm_align); |
| | 2162 | |
| | 2163 | const padding_len = @intCast(c_uint, zig_offset - llvm_offset); |
| | 2164 | if (padding_len > 0) { |
| | 2165 | llvm_field_index += 1; |
| | 2166 | llvm_offset = zig_offset; |
| | 2167 | } |
| | 2168 | |
| | 2169 | if (field_index == i) { |
| | 2170 | ptr_pl_buf.* = .{ |
| | 2171 | .data = .{ |
| | 2172 | .pointee_type = field.ty, |
| | 2173 | .@"align" = field_align, |
| | 2174 | .@"addrspace" = .generic, |
| | 2175 | }, |
| | 2176 | }; |
| | 2177 | return llvm_field_index; |
| | 2178 | } |
| | 2179 | |
| | 2180 | llvm_field_index += 1; |
| | 2181 | llvm_offset += dg.object.target_data.ABISizeOfType(field_llvm_ty); |
| | 2182 | zig_offset += field.ty.abiSize(target); |
| | 2183 | } else { |
| | 2184 | // We did not find an llvm field that corresponds to this zig field. |
| | 2185 | return null; |
| | 2186 | } |
| | 2187 | } |
| 1975 | }; | 2188 | }; |
| 1976 | | 2189 | |
| 1977 | pub const FuncGen = struct { | 2190 | pub const FuncGen = struct { |
| ... | @@ -2850,7 +3063,7 @@ pub const FuncGen = struct { | ... | @@ -2850,7 +3063,7 @@ pub const FuncGen = struct { |
| 2850 | }, | 3063 | }, |
| 2851 | else => { | 3064 | else => { |
| 2852 | var ptr_ty_buf: Type.Payload.Pointer = undefined; | 3065 | var ptr_ty_buf: Type.Payload.Pointer = undefined; |
| 2853 | const llvm_field_index = llvmFieldIndex(struct_ty, field_index, target, &ptr_ty_buf).?; | 3066 | const llvm_field_index = self.dg.llvmFieldIndex(struct_ty, field_index, &ptr_ty_buf).?; |
| 2854 | return self.builder.buildExtractValue(struct_llvm_val, llvm_field_index, ""); | 3067 | return self.builder.buildExtractValue(struct_llvm_val, llvm_field_index, ""); |
| 2855 | }, | 3068 | }, |
| 2856 | }, | 3069 | }, |
| ... | @@ -2865,7 +3078,7 @@ pub const FuncGen = struct { | ... | @@ -2865,7 +3078,7 @@ pub const FuncGen = struct { |
| 2865 | .Struct => { | 3078 | .Struct => { |
| 2866 | assert(struct_ty.containerLayout() != .Packed); | 3079 | assert(struct_ty.containerLayout() != .Packed); |
| 2867 | var ptr_ty_buf: Type.Payload.Pointer = undefined; | 3080 | var ptr_ty_buf: Type.Payload.Pointer = undefined; |
| 2868 | const llvm_field_index = llvmFieldIndex(struct_ty, field_index, target, &ptr_ty_buf).?; | 3081 | const llvm_field_index = self.dg.llvmFieldIndex(struct_ty, field_index, &ptr_ty_buf).?; |
| 2869 | const field_ptr = self.builder.buildStructGEP(struct_llvm_val, llvm_field_index, ""); | 3082 | const field_ptr = self.builder.buildStructGEP(struct_llvm_val, llvm_field_index, ""); |
| 2870 | const field_ptr_ty = Type.initPayload(&ptr_ty_buf.base); | 3083 | const field_ptr_ty = Type.initPayload(&ptr_ty_buf.base); |
| 2871 | return self.load(field_ptr, field_ptr_ty); | 3084 | return self.load(field_ptr, field_ptr_ty); |
| ... | @@ -3916,6 +4129,7 @@ pub const FuncGen = struct { | ... | @@ -3916,6 +4129,7 @@ pub const FuncGen = struct { |
| 3916 | const operand_is_ref = isByRef(operand_ty); | 4129 | const operand_is_ref = isByRef(operand_ty); |
| 3917 | const result_is_ref = isByRef(inst_ty); | 4130 | const result_is_ref = isByRef(inst_ty); |
| 3918 | const llvm_dest_ty = try self.dg.llvmType(inst_ty); | 4131 | const llvm_dest_ty = try self.dg.llvmType(inst_ty); |
| | 4132 | const target = self.dg.module.getTarget(); |
| 3919 | | 4133 | |
| 3920 | if (operand_is_ref and result_is_ref) { | 4134 | if (operand_is_ref and result_is_ref) { |
| 3921 | // They are both pointers; just do a bitcast on the pointers :) | 4135 | // They are both pointers; just do a bitcast on the pointers :) |
| ... | @@ -3927,7 +4141,6 @@ pub const FuncGen = struct { | ... | @@ -3927,7 +4141,6 @@ pub const FuncGen = struct { |
| 3927 | } | 4141 | } |
| 3928 | | 4142 | |
| 3929 | if (operand_ty.zigTypeTag() == .Vector and inst_ty.zigTypeTag() == .Array) { | 4143 | if (operand_ty.zigTypeTag() == .Vector and inst_ty.zigTypeTag() == .Array) { |
| 3930 | const target = self.dg.module.getTarget(); | | |
| 3931 | const elem_ty = operand_ty.childType(); | 4144 | const elem_ty = operand_ty.childType(); |
| 3932 | if (!result_is_ref) { | 4145 | if (!result_is_ref) { |
| 3933 | return self.dg.todo("implement bitcast vector to non-ref array", .{}); | 4146 | return self.dg.todo("implement bitcast vector to non-ref array", .{}); |
| ... | @@ -3957,7 +4170,6 @@ pub const FuncGen = struct { | ... | @@ -3957,7 +4170,6 @@ pub const FuncGen = struct { |
| 3957 | } | 4170 | } |
| 3958 | return array_ptr; | 4171 | return array_ptr; |
| 3959 | } else if (operand_ty.zigTypeTag() == .Array and inst_ty.zigTypeTag() == .Vector) { | 4172 | } else if (operand_ty.zigTypeTag() == .Array and inst_ty.zigTypeTag() == .Vector) { |
| 3960 | const target = self.dg.module.getTarget(); | | |
| 3961 | const elem_ty = operand_ty.childType(); | 4173 | const elem_ty = operand_ty.childType(); |
| 3962 | const llvm_vector_ty = try self.dg.llvmType(inst_ty); | 4174 | const llvm_vector_ty = try self.dg.llvmType(inst_ty); |
| 3963 | if (!operand_is_ref) { | 4175 | if (!operand_is_ref) { |
| ... | @@ -3998,18 +4210,39 @@ pub const FuncGen = struct { | ... | @@ -3998,18 +4210,39 @@ pub const FuncGen = struct { |
| 3998 | if (operand_is_ref) { | 4210 | if (operand_is_ref) { |
| 3999 | // Bitcast the operand pointer, then load. | 4211 | // Bitcast the operand pointer, then load. |
| 4000 | const casted_ptr = self.builder.buildBitCast(operand, llvm_dest_ty.pointerType(0), ""); | 4212 | const casted_ptr = self.builder.buildBitCast(operand, llvm_dest_ty.pointerType(0), ""); |
| 4001 | return self.builder.buildLoad(casted_ptr, ""); | 4213 | const load_inst = self.builder.buildLoad(casted_ptr, ""); |
| | 4214 | load_inst.setAlignment(operand_ty.abiAlignment(target)); |
| | 4215 | return load_inst; |
| 4002 | } | 4216 | } |
| 4003 | | 4217 | |
| 4004 | if (result_is_ref) { | 4218 | if (result_is_ref) { |
| 4005 | // Bitcast the result pointer, then store. | 4219 | // Bitcast the result pointer, then store. |
| | 4220 | const alignment = @maximum(operand_ty.abiAlignment(target), inst_ty.abiAlignment(target)); |
| 4006 | const result_ptr = self.buildAlloca(llvm_dest_ty); | 4221 | const result_ptr = self.buildAlloca(llvm_dest_ty); |
| | 4222 | result_ptr.setAlignment(alignment); |
| 4007 | const operand_llvm_ty = try self.dg.llvmType(operand_ty); | 4223 | const operand_llvm_ty = try self.dg.llvmType(operand_ty); |
| 4008 | const casted_ptr = self.builder.buildBitCast(result_ptr, operand_llvm_ty.pointerType(0), ""); | 4224 | const casted_ptr = self.builder.buildBitCast(result_ptr, operand_llvm_ty.pointerType(0), ""); |
| 4009 | _ = self.builder.buildStore(operand, casted_ptr); | 4225 | const store_inst = self.builder.buildStore(operand, casted_ptr); |
| | 4226 | store_inst.setAlignment(alignment); |
| 4010 | return result_ptr; | 4227 | return result_ptr; |
| 4011 | } | 4228 | } |
| 4012 | | 4229 | |
| | 4230 | if (llvm_dest_ty.getTypeKind() == .Struct) { |
| | 4231 | // Both our operand and our result are values, not pointers, |
| | 4232 | // but LLVM won't let us bitcast struct values. |
| | 4233 | // Therefore, we store operand to bitcasted alloca, then load for result. |
| | 4234 | const alignment = @maximum(operand_ty.abiAlignment(target), inst_ty.abiAlignment(target)); |
| | 4235 | const result_ptr = self.buildAlloca(llvm_dest_ty); |
| | 4236 | result_ptr.setAlignment(alignment); |
| | 4237 | const operand_llvm_ty = try self.dg.llvmType(operand_ty); |
| | 4238 | const casted_ptr = self.builder.buildBitCast(result_ptr, operand_llvm_ty.pointerType(0), ""); |
| | 4239 | const store_inst = self.builder.buildStore(operand, casted_ptr); |
| | 4240 | store_inst.setAlignment(alignment); |
| | 4241 | const load_inst = self.builder.buildLoad(result_ptr, ""); |
| | 4242 | load_inst.setAlignment(alignment); |
| | 4243 | return load_inst; |
| | 4244 | } |
| | 4245 | |
| 4013 | return self.builder.buildBitCast(operand, llvm_dest_ty, ""); | 4246 | return self.builder.buildBitCast(operand, llvm_dest_ty, ""); |
| 4014 | } | 4247 | } |
| 4015 | | 4248 | |
| ... | @@ -5009,9 +5242,8 @@ pub const FuncGen = struct { | ... | @@ -5009,9 +5242,8 @@ pub const FuncGen = struct { |
| 5009 | return self.builder.buildBitCast(struct_ptr, result_llvm_ty, ""); | 5242 | return self.builder.buildBitCast(struct_ptr, result_llvm_ty, ""); |
| 5010 | }, | 5243 | }, |
| 5011 | else => { | 5244 | else => { |
| 5012 | const target = self.dg.module.getTarget(); | | |
| 5013 | var ty_buf: Type.Payload.Pointer = undefined; | 5245 | var ty_buf: Type.Payload.Pointer = undefined; |
| 5014 | if (llvmFieldIndex(struct_ty, field_index, target, &ty_buf)) |llvm_field_index| { | 5246 | if (self.dg.llvmFieldIndex(struct_ty, field_index, &ty_buf)) |llvm_field_index| { |
| 5015 | return self.builder.buildStructGEP(struct_ptr, llvm_field_index, ""); | 5247 | return self.builder.buildStructGEP(struct_ptr, llvm_field_index, ""); |
| 5016 | } else { | 5248 | } else { |
| 5017 | // If we found no index then this means this is a zero sized field at the | 5249 | // If we found no index then this means this is a zero sized field at the |
| ... | @@ -5422,63 +5654,6 @@ fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) llvm.Ca | ... | @@ -5422,63 +5654,6 @@ fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) llvm.Ca |
| 5422 | }; | 5654 | }; |
| 5423 | } | 5655 | } |
| 5424 | | 5656 | |
| 5425 | /// Take into account 0 bit fields. Returns null if an llvm field could not be found. This only | | |
| 5426 | /// happens if you want the field index of a zero sized field at the end of the struct. | | |
| 5427 | fn llvmFieldIndex( | | |
| 5428 | ty: Type, | | |
| 5429 | field_index: u32, | | |
| 5430 | target: std.Target, | | |
| 5431 | ptr_pl_buf: *Type.Payload.Pointer, | | |
| 5432 | ) ?c_uint { | | |
| 5433 | if (ty.castTag(.tuple)) |payload| { | | |
| 5434 | const values = payload.data.values; | | |
| 5435 | var llvm_field_index: c_uint = 0; | | |
| 5436 | for (values) |val, i| { | | |
| 5437 | if (val.tag() != .unreachable_value) { | | |
| 5438 | continue; | | |
| 5439 | } | | |
| 5440 | if (field_index > i) { | | |
| 5441 | llvm_field_index += 1; | | |
| 5442 | continue; | | |
| 5443 | } | | |
| 5444 | const field_ty = payload.data.types[i]; | | |
| 5445 | ptr_pl_buf.* = .{ | | |
| 5446 | .data = .{ | | |
| 5447 | .pointee_type = field_ty, | | |
| 5448 | .@"align" = field_ty.abiAlignment(target), | | |
| 5449 | .@"addrspace" = .generic, | | |
| 5450 | }, | | |
| 5451 | }; | | |
| 5452 | return llvm_field_index; | | |
| 5453 | } | | |
| 5454 | return null; | | |
| 5455 | } | | |
| 5456 | const struct_obj = ty.castTag(.@"struct").?.data; | | |
| 5457 | assert(struct_obj.layout != .Packed); | | |
| 5458 | | | |
| 5459 | var llvm_field_index: c_uint = 0; | | |
| 5460 | for (struct_obj.fields.values()) |field, i| { | | |
| 5461 | if (!field.ty.hasRuntimeBits()) | | |
| 5462 | continue; | | |
| 5463 | if (field_index > i) { | | |
| 5464 | llvm_field_index += 1; | | |
| 5465 | continue; | | |
| 5466 | } | | |
| 5467 | | | |
| 5468 | ptr_pl_buf.* = .{ | | |
| 5469 | .data = .{ | | |
| 5470 | .pointee_type = field.ty, | | |
| 5471 | .@"align" = field.normalAlignment(target), | | |
| 5472 | .@"addrspace" = .generic, | | |
| 5473 | }, | | |
| 5474 | }; | | |
| 5475 | return llvm_field_index; | | |
| 5476 | } else { | | |
| 5477 | // We did not find an llvm field that corresponds to this zig field. | | |
| 5478 | return null; | | |
| 5479 | } | | |
| 5480 | } | | |
| 5481 | | | |
| 5482 | fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool { | 5657 | fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool { |
| 5483 | switch (fn_info.cc) { | 5658 | switch (fn_info.cc) { |
| 5484 | .Unspecified, .Inline => return isByRef(fn_info.return_type), | 5659 | .Unspecified, .Inline => return isByRef(fn_info.return_type), |
| ... | @@ -5497,7 +5672,7 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool | ... | @@ -5497,7 +5672,7 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool |
| 5497 | } | 5672 | } |
| 5498 | | 5673 | |
| 5499 | fn isByRef(ty: Type) bool { | 5674 | fn isByRef(ty: Type) bool { |
| 5500 | // For tuples (and TODO structs), if there are more than this many non-void | 5675 | // For tuples and structs, if there are more than this many non-void |
| 5501 | // fields, then we make it byref, otherwise byval. | 5676 | // fields, then we make it byref, otherwise byval. |
| 5502 | const max_fields_byval = 2; | 5677 | const max_fields_byval = 2; |
| 5503 | | 5678 | |
| ... | @@ -5529,24 +5704,28 @@ fn isByRef(ty: Type) bool { | ... | @@ -5529,24 +5704,28 @@ fn isByRef(ty: Type) bool { |
| 5529 | .Struct => { | 5704 | .Struct => { |
| 5530 | // Packed structs are represented to LLVM as integers. | 5705 | // Packed structs are represented to LLVM as integers. |
| 5531 | if (ty.containerLayout() == .Packed) return false; | 5706 | if (ty.containerLayout() == .Packed) return false; |
| 5532 | | 5707 | if (ty.isTuple()) { |
| 5533 | if (!ty.hasRuntimeBits()) return false; | 5708 | const tuple = ty.tupleFields(); |
| 5534 | if (ty.castTag(.tuple)) |tuple| { | | |
| 5535 | var count: usize = 0; | 5709 | var count: usize = 0; |
| 5536 | for (tuple.data.values) |field_val, i| { | 5710 | for (tuple.values) |field_val, i| { |
| 5537 | if (field_val.tag() != .unreachable_value) continue; | 5711 | if (field_val.tag() != .unreachable_value) continue; |
| | 5712 | |
| 5538 | count += 1; | 5713 | count += 1; |
| 5539 | if (count > max_fields_byval) { | 5714 | if (count > max_fields_byval) return true; |
| 5540 | return true; | 5715 | if (isByRef(tuple.types[i])) return true; |
| 5541 | } | | |
| 5542 | const field_ty = tuple.data.types[i]; | | |
| 5543 | if (isByRef(field_ty)) { | | |
| 5544 | return true; | | |
| 5545 | } | | |
| 5546 | } | 5716 | } |
| 5547 | return false; | 5717 | return false; |
| 5548 | } | 5718 | } |
| 5549 | return true; | 5719 | var count: usize = 0; |
| | 5720 | const fields = ty.structFields(); |
| | 5721 | for (fields.values()) |field| { |
| | 5722 | if (field.is_comptime or !field.ty.hasRuntimeBits()) continue; |
| | 5723 | |
| | 5724 | count += 1; |
| | 5725 | if (count > max_fields_byval) return true; |
| | 5726 | if (isByRef(field.ty)) return true; |
| | 5727 | } |
| | 5728 | return false; |
| 5550 | }, | 5729 | }, |
| 5551 | .Union => return ty.hasRuntimeBits(), | 5730 | .Union => return ty.hasRuntimeBits(), |
| 5552 | .ErrorUnion => return isByRef(ty.errorUnionPayload()), | 5731 | .ErrorUnion => return isByRef(ty.errorUnionPayload()), |