| ... | ... | @@ -160,6 +160,7 @@ pub const Object = struct { |
| 160 | 160 | llvm_module: *const llvm.Module, |
| 161 | 161 | context: *const llvm.Context, |
| 162 | 162 | target_machine: *const llvm.TargetMachine, |
| 163 | target_data: *const llvm.TargetData, |
| 163 | 164 | /// Ideally we would use `llvm_module.getNamedFunction` to go from *Decl to LLVM function, |
| 164 | 165 | /// but that has some downsides: |
| 165 | 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 | 259 | errdefer target_machine.dispose(); |
| 259 | 260 | |
| 260 | 261 | const target_data = target_machine.createTargetDataLayout(); |
| 261 | | defer target_data.dispose(); |
| 262 | errdefer target_data.dispose(); |
| 262 | 263 | |
| 263 | 264 | llvm_module.setModuleDataLayout(target_data); |
| 264 | 265 | |
| ... | ... | @@ -266,6 +267,7 @@ pub const Object = struct { |
| 266 | 267 | .llvm_module = llvm_module, |
| 267 | 268 | .context = context, |
| 268 | 269 | .target_machine = target_machine, |
| 270 | .target_data = target_data, |
| 269 | 271 | .decl_map = .{}, |
| 270 | 272 | .type_map = .{}, |
| 271 | 273 | .type_map_arena = std.heap.ArenaAllocator.init(gpa), |
| ... | ... | @@ -274,6 +276,7 @@ pub const Object = struct { |
| 274 | 276 | } |
| 275 | 277 | |
| 276 | 278 | pub fn deinit(self: *Object, gpa: Allocator) void { |
| 279 | self.target_data.dispose(); |
| 277 | 280 | self.target_machine.dispose(); |
| 278 | 281 | self.llvm_module.dispose(); |
| 279 | 282 | self.context.dispose(); |
| ... | ... | @@ -955,20 +958,55 @@ pub const DeclGen = struct { |
| 955 | 958 | // reference, we need to copy it here. |
| 956 | 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 | 963 | const llvm_struct_ty = dg.context.structCreateNamed(""); |
| 960 | 964 | gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls |
| 961 | 965 | |
| 962 | | const types = tuple.data.types; |
| 963 | | const values = tuple.data.values; |
| 964 | | var llvm_field_types = try std.ArrayListUnmanaged(*const llvm.Type).initCapacity(gpa, types.len); |
| 966 | var llvm_field_types: std.ArrayListUnmanaged(*const llvm.Type) = .{}; |
| 965 | 967 | defer llvm_field_types.deinit(gpa); |
| 966 | 968 | |
| 967 | | for (types) |field_ty, i| { |
| 968 | | const field_val = values[i]; |
| 969 | try llvm_field_types.ensureUnusedCapacity(gpa, tuple.types.len); |
| 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 | 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 | 1012 | llvm_struct_ty.structSetBody( |
| ... | ... | @@ -998,12 +1036,49 @@ pub const DeclGen = struct { |
| 998 | 1036 | |
| 999 | 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 | 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 | 1050 | for (struct_obj.fields.values()) |field| { |
| 1005 | | if (!field.ty.hasRuntimeBits()) continue; |
| 1006 | | llvm_field_types.appendAssumeCapacity(try dg.llvmType(field.ty)); |
| 1051 | if (field.is_comptime or !field.ty.hasRuntimeBits()) continue; |
| 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 | 1084 | llvm_struct_ty.structSetBody( |
| ... | ... | @@ -1050,7 +1125,7 @@ pub const DeclGen = struct { |
| 1050 | 1125 | llvm_aligned_field_ty, |
| 1051 | 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 | 1131 | if (layout.tag_size == 0) { |
| ... | ... | @@ -1461,9 +1536,9 @@ pub const DeclGen = struct { |
| 1461 | 1536 | const field_vals = tv.val.castTag(.@"struct").?.data; |
| 1462 | 1537 | const gpa = dg.gpa; |
| 1463 | 1538 | const struct_obj = tv.ty.castTag(.@"struct").?.data; |
| 1539 | const target = dg.module.getTarget(); |
| 1464 | 1540 | |
| 1465 | 1541 | if (struct_obj.layout == .Packed) { |
| 1466 | | const target = dg.module.getTarget(); |
| 1467 | 1542 | const big_bits = struct_obj.packedIntegerBits(target); |
| 1468 | 1543 | const int_llvm_ty = dg.context.intType(big_bits); |
| 1469 | 1544 | const fields = struct_obj.fields.values(); |
| ... | ... | @@ -1497,19 +1572,56 @@ pub const DeclGen = struct { |
| 1497 | 1572 | var llvm_fields = try std.ArrayListUnmanaged(*const llvm.Value).initCapacity(gpa, llvm_field_count); |
| 1498 | 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 | 1582 | var need_unnamed = false; |
| 1501 | | for (field_vals) |field_val, i| { |
| 1502 | | const field_ty = tv.ty.structFieldType(i); |
| 1503 | | if (!field_ty.hasRuntimeBits()) continue; |
| 1583 | for (struct_obj.fields.values()) |field, i| { |
| 1584 | if (field.is_comptime or !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 | 1604 | const field_llvm_val = try dg.genTypedValue(.{ |
| 1506 | | .ty = field_ty, |
| 1507 | | .val = field_val, |
| 1605 | .ty = field.ty, |
| 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 | 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 | 1627 | if (need_unnamed) { |
| ... | ... | @@ -1556,7 +1668,7 @@ pub const DeclGen = struct { |
| 1556 | 1668 | const fields: [2]*const llvm.Value = .{ |
| 1557 | 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 | 1674 | // In this case we must make an unnamed struct because LLVM does |
| ... | ... | @@ -1741,7 +1853,7 @@ pub const DeclGen = struct { |
| 1741 | 1853 | }, |
| 1742 | 1854 | .Struct => { |
| 1743 | 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 | 1857 | const indices: [2]*const llvm.Value = .{ |
| 1746 | 1858 | llvm_u32.constInt(0, .False), |
| 1747 | 1859 | llvm_u32.constInt(llvm_field_index, .False), |
| ... | ... | @@ -1972,6 +2084,107 @@ pub const DeclGen = struct { |
| 1972 | 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 | 2190 | pub const FuncGen = struct { |
| ... | ... | @@ -2850,7 +3063,7 @@ pub const FuncGen = struct { |
| 2850 | 3063 | }, |
| 2851 | 3064 | else => { |
| 2852 | 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 | 3067 | return self.builder.buildExtractValue(struct_llvm_val, llvm_field_index, ""); |
| 2855 | 3068 | }, |
| 2856 | 3069 | }, |
| ... | ... | @@ -2865,7 +3078,7 @@ pub const FuncGen = struct { |
| 2865 | 3078 | .Struct => { |
| 2866 | 3079 | assert(struct_ty.containerLayout() != .Packed); |
| 2867 | 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 | 3082 | const field_ptr = self.builder.buildStructGEP(struct_llvm_val, llvm_field_index, ""); |
| 2870 | 3083 | const field_ptr_ty = Type.initPayload(&ptr_ty_buf.base); |
| 2871 | 3084 | return self.load(field_ptr, field_ptr_ty); |
| ... | ... | @@ -3916,6 +4129,7 @@ pub const FuncGen = struct { |
| 3916 | 4129 | const operand_is_ref = isByRef(operand_ty); |
| 3917 | 4130 | const result_is_ref = isByRef(inst_ty); |
| 3918 | 4131 | const llvm_dest_ty = try self.dg.llvmType(inst_ty); |
| 4132 | const target = self.dg.module.getTarget(); |
| 3919 | 4133 | |
| 3920 | 4134 | if (operand_is_ref and result_is_ref) { |
| 3921 | 4135 | // They are both pointers; just do a bitcast on the pointers :) |
| ... | ... | @@ -3927,7 +4141,6 @@ pub const FuncGen = struct { |
| 3927 | 4141 | } |
| 3928 | 4142 | |
| 3929 | 4143 | if (operand_ty.zigTypeTag() == .Vector and inst_ty.zigTypeTag() == .Array) { |
| 3930 | | const target = self.dg.module.getTarget(); |
| 3931 | 4144 | const elem_ty = operand_ty.childType(); |
| 3932 | 4145 | if (!result_is_ref) { |
| 3933 | 4146 | return self.dg.todo("implement bitcast vector to non-ref array", .{}); |
| ... | ... | @@ -3957,7 +4170,6 @@ pub const FuncGen = struct { |
| 3957 | 4170 | } |
| 3958 | 4171 | return array_ptr; |
| 3959 | 4172 | } else if (operand_ty.zigTypeTag() == .Array and inst_ty.zigTypeTag() == .Vector) { |
| 3960 | | const target = self.dg.module.getTarget(); |
| 3961 | 4173 | const elem_ty = operand_ty.childType(); |
| 3962 | 4174 | const llvm_vector_ty = try self.dg.llvmType(inst_ty); |
| 3963 | 4175 | if (!operand_is_ref) { |
| ... | ... | @@ -3998,18 +4210,39 @@ pub const FuncGen = struct { |
| 3998 | 4210 | if (operand_is_ref) { |
| 3999 | 4211 | // Bitcast the operand pointer, then load. |
| 4000 | 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 | 4218 | if (result_is_ref) { |
| 4005 | 4219 | // Bitcast the result pointer, then store. |
| 4220 | const alignment = @maximum(operand_ty.abiAlignment(target), inst_ty.abiAlignment(target)); |
| 4006 | 4221 | const result_ptr = self.buildAlloca(llvm_dest_ty); |
| 4222 | result_ptr.setAlignment(alignment); |
| 4007 | 4223 | const operand_llvm_ty = try self.dg.llvmType(operand_ty); |
| 4008 | 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 | 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 | 4246 | return self.builder.buildBitCast(operand, llvm_dest_ty, ""); |
| 4014 | 4247 | } |
| 4015 | 4248 | |
| ... | ... | @@ -5009,9 +5242,8 @@ pub const FuncGen = struct { |
| 5009 | 5242 | return self.builder.buildBitCast(struct_ptr, result_llvm_ty, ""); |
| 5010 | 5243 | }, |
| 5011 | 5244 | else => { |
| 5012 | | const target = self.dg.module.getTarget(); |
| 5013 | 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 | 5247 | return self.builder.buildStructGEP(struct_ptr, llvm_field_index, ""); |
| 5016 | 5248 | } else { |
| 5017 | 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 | 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 | 5657 | fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool { |
| 5483 | 5658 | switch (fn_info.cc) { |
| 5484 | 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 | 5672 | } |
| 5498 | 5673 | |
| 5499 | 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 | 5676 | // fields, then we make it byref, otherwise byval. |
| 5502 | 5677 | const max_fields_byval = 2; |
| 5503 | 5678 | |
| ... | ... | @@ -5529,24 +5704,28 @@ fn isByRef(ty: Type) bool { |
| 5529 | 5704 | .Struct => { |
| 5530 | 5705 | // Packed structs are represented to LLVM as integers. |
| 5531 | 5706 | if (ty.containerLayout() == .Packed) return false; |
| 5532 | | |
| 5533 | | if (!ty.hasRuntimeBits()) return false; |
| 5534 | | if (ty.castTag(.tuple)) |tuple| { |
| 5707 | if (ty.isTuple()) { |
| 5708 | const tuple = ty.tupleFields(); |
| 5535 | 5709 | var count: usize = 0; |
| 5536 | | for (tuple.data.values) |field_val, i| { |
| 5710 | for (tuple.values) |field_val, i| { |
| 5537 | 5711 | if (field_val.tag() != .unreachable_value) continue; |
| 5712 | |
| 5538 | 5713 | count += 1; |
| 5539 | | if (count > max_fields_byval) { |
| 5540 | | return true; |
| 5541 | | } |
| 5542 | | const field_ty = tuple.data.types[i]; |
| 5543 | | if (isByRef(field_ty)) { |
| 5544 | | return true; |
| 5545 | | } |
| 5714 | if (count > max_fields_byval) return true; |
| 5715 | if (isByRef(tuple.types[i])) return true; |
| 5546 | 5716 | } |
| 5547 | 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 | 5730 | .Union => return ty.hasRuntimeBits(), |
| 5552 | 5731 | .ErrorUnion => return isByRef(ty.errorUnionPayload()), |