| ... | ... | @@ -549,7 +549,6 @@ pub const Object = struct { |
| 549 | 549 | /// - *Module.Decl (Non-Fn) => *DIGlobalVariable |
| 550 | 550 | di_map: std.AutoHashMapUnmanaged(*const anyopaque, *llvm.DINode), |
| 551 | 551 | di_compile_unit: ?*llvm.DICompileUnit, |
| 552 | | context: *llvm.Context, |
| 553 | 552 | target_machine: *llvm.TargetMachine, |
| 554 | 553 | target_data: *llvm.TargetData, |
| 555 | 554 | target: std.Target, |
| ... | ... | @@ -727,7 +726,6 @@ pub const Object = struct { |
| 727 | 726 | .di_map = .{}, |
| 728 | 727 | .di_builder = builder.llvm.di_builder, |
| 729 | 728 | .di_compile_unit = builder.llvm.di_compile_unit, |
| 730 | | .context = builder.llvm.context, |
| 731 | 729 | .target_machine = target_machine, |
| 732 | 730 | .target_data = target_data, |
| 733 | 731 | .target = options.target, |
| ... | ... | @@ -803,13 +801,13 @@ pub const Object = struct { |
| 803 | 801 | .linkage = .private, |
| 804 | 802 | .unnamed_addr = .unnamed_addr, |
| 805 | 803 | .type = str_ty, |
| 806 | | .alignment = comptime Builder.Alignment.fromByteUnits(1), |
| 807 | 804 | .kind = .{ .variable = @enumFromInt(o.builder.variables.items.len) }, |
| 808 | 805 | }; |
| 809 | 806 | var str_variable = Builder.Variable{ |
| 810 | 807 | .global = @enumFromInt(o.builder.globals.count()), |
| 811 | 808 | .mutability = .constant, |
| 812 | 809 | .init = str_init, |
| 810 | .alignment = comptime Builder.Alignment.fromByteUnits(1), |
| 813 | 811 | }; |
| 814 | 812 | try o.builder.llvm.globals.append(o.gpa, str_llvm_global); |
| 815 | 813 | const global_index = try o.builder.addGlobal(.empty, str_global); |
| ... | ... | @@ -833,13 +831,13 @@ pub const Object = struct { |
| 833 | 831 | .linkage = .private, |
| 834 | 832 | .unnamed_addr = .unnamed_addr, |
| 835 | 833 | .type = llvm_table_ty, |
| 836 | | .alignment = Builder.Alignment.fromByteUnits(slice_alignment), |
| 837 | 834 | .kind = .{ .variable = @enumFromInt(o.builder.variables.items.len) }, |
| 838 | 835 | }; |
| 839 | 836 | var variable = Builder.Variable{ |
| 840 | 837 | .global = @enumFromInt(o.builder.globals.count()), |
| 841 | 838 | .mutability = .constant, |
| 842 | 839 | .init = error_name_table_init, |
| 840 | .alignment = Builder.Alignment.fromByteUnits(slice_alignment), |
| 843 | 841 | }; |
| 844 | 842 | try o.builder.llvm.globals.append(o.gpa, error_name_table_global); |
| 845 | 843 | _ = try o.builder.addGlobal(.empty, global); |
| ... | ... | @@ -857,25 +855,19 @@ pub const Object = struct { |
| 857 | 855 | const mod = o.module; |
| 858 | 856 | const errors_len = mod.global_error_set.count(); |
| 859 | 857 | |
| 860 | | var wip = Builder.WipFunction.init(&o.builder, llvm_fn.ptrConst(&o.builder).kind.function); |
| 858 | var wip = try Builder.WipFunction.init(&o.builder, llvm_fn.ptrConst(&o.builder).kind.function); |
| 861 | 859 | defer wip.deinit(); |
| 862 | | |
| 863 | | const builder = wip.llvm.builder; |
| 864 | | const entry_block = try wip.block("Entry"); |
| 865 | | wip.cursor = .{ .block = entry_block }; |
| 866 | | builder.positionBuilderAtEnd(entry_block.toLlvm(&wip)); |
| 867 | | builder.clearCurrentDebugLocation(); |
| 860 | wip.cursor = .{ .block = try wip.block(0, "Entry") }; |
| 868 | 861 | |
| 869 | 862 | // Example source of the following LLVM IR: |
| 870 | 863 | // fn __zig_lt_errors_len(index: u16) bool { |
| 871 | 864 | // return index < total_errors_len; |
| 872 | 865 | // } |
| 873 | 866 | |
| 874 | | const lhs = llvm_fn.toLlvm(&o.builder).getParam(0); |
| 875 | | const rhs = try o.builder.intConst(Builder.Type.err_int, errors_len); |
| 876 | | const is_lt = builder.buildICmp(.ULT, lhs, rhs.toLlvm(&o.builder), ""); |
| 877 | | _ = builder.buildRet(is_lt); |
| 878 | | |
| 867 | const lhs = wip.arg(0); |
| 868 | const rhs = try o.builder.intValue(Builder.Type.err_int, errors_len); |
| 869 | const is_lt = try wip.icmp(.ult, lhs, rhs, ""); |
| 870 | _ = try wip.ret(is_lt); |
| 879 | 871 | try wip.finish(); |
| 880 | 872 | } |
| 881 | 873 | |
| ... | ... | @@ -1148,29 +1140,26 @@ pub const Object = struct { |
| 1148 | 1140 | } |
| 1149 | 1141 | |
| 1150 | 1142 | if (ip.stringToSliceUnwrap(decl.@"linksection")) |section| { |
| 1151 | | global.ptr(&o.builder).section = try o.builder.string(section); |
| 1143 | function.ptr(&o.builder).section = try o.builder.string(section); |
| 1152 | 1144 | llvm_func.setSection(section); |
| 1153 | 1145 | } |
| 1154 | 1146 | |
| 1155 | | // Remove all the basic blocks of a function in order to start over, generating |
| 1156 | | // LLVM IR from an empty function body. |
| 1157 | | while (llvm_func.getFirstBasicBlock()) |bb| { |
| 1158 | | bb.deleteBasicBlock(); |
| 1159 | | } |
| 1160 | | |
| 1161 | 1147 | var deinit_wip = true; |
| 1162 | | var wip = Builder.WipFunction.init(&o.builder, function); |
| 1148 | var wip = try Builder.WipFunction.init(&o.builder, function); |
| 1163 | 1149 | defer if (deinit_wip) wip.deinit(); |
| 1150 | wip.cursor = .{ .block = try wip.block(0, "Entry") }; |
| 1164 | 1151 | |
| 1165 | 1152 | const builder = wip.llvm.builder; |
| 1166 | | const entry_block = try wip.block("Entry"); |
| 1167 | | wip.cursor = .{ .block = entry_block }; |
| 1168 | | builder.positionBuilderAtEnd(entry_block.toLlvm(&wip)); |
| 1153 | var llvm_arg_i: u32 = 0; |
| 1169 | 1154 | |
| 1170 | 1155 | // This gets the LLVM values from the function and stores them in `dg.args`. |
| 1171 | 1156 | const fn_info = mod.typeToFunc(decl.ty).?; |
| 1172 | 1157 | const sret = firstParamSRet(fn_info, mod); |
| 1173 | | const ret_ptr = if (sret) llvm_func.getParam(0) else null; |
| 1158 | const ret_ptr: Builder.Value = if (sret) param: { |
| 1159 | const param = wip.arg(llvm_arg_i); |
| 1160 | llvm_arg_i += 1; |
| 1161 | break :param param; |
| 1162 | } else .none; |
| 1174 | 1163 | const gpa = o.gpa; |
| 1175 | 1164 | |
| 1176 | 1165 | if (ccAbiPromoteInt(fn_info.cc, mod, fn_info.return_type.toType())) |s| switch (s) { |
| ... | ... | @@ -1181,205 +1170,183 @@ pub const Object = struct { |
| 1181 | 1170 | const err_return_tracing = fn_info.return_type.toType().isError(mod) and |
| 1182 | 1171 | mod.comp.bin_file.options.error_return_tracing; |
| 1183 | 1172 | |
| 1184 | | const err_ret_trace = if (err_return_tracing) |
| 1185 | | llvm_func.getParam(@intFromBool(ret_ptr != null)) |
| 1186 | | else |
| 1187 | | null; |
| 1173 | const err_ret_trace: Builder.Value = if (err_return_tracing) param: { |
| 1174 | const param = wip.arg(llvm_arg_i); |
| 1175 | llvm_arg_i += 1; |
| 1176 | break :param param; |
| 1177 | } else .none; |
| 1188 | 1178 | |
| 1189 | 1179 | // This is the list of args we will use that correspond directly to the AIR arg |
| 1190 | 1180 | // instructions. Depending on the calling convention, this list is not necessarily |
| 1191 | 1181 | // a bijection with the actual LLVM parameters of the function. |
| 1192 | | var args = std.ArrayList(*llvm.Value).init(gpa); |
| 1193 | | defer args.deinit(); |
| 1182 | var args: std.ArrayListUnmanaged(Builder.Value) = .{}; |
| 1183 | defer args.deinit(gpa); |
| 1194 | 1184 | |
| 1195 | 1185 | { |
| 1196 | | var llvm_arg_i = @as(c_uint, @intFromBool(ret_ptr != null)) + @intFromBool(err_return_tracing); |
| 1197 | 1186 | var it = iterateParamTypes(o, fn_info); |
| 1198 | | while (try it.next()) |lowering| switch (lowering) { |
| 1199 | | .no_bits => continue, |
| 1200 | | .byval => { |
| 1201 | | assert(!it.byval_attr); |
| 1202 | | const param_index = it.zig_index - 1; |
| 1203 | | const param_ty = fn_info.param_types.get(ip)[param_index].toType(); |
| 1204 | | const param = llvm_func.getParam(llvm_arg_i); |
| 1205 | | try args.ensureUnusedCapacity(1); |
| 1206 | | |
| 1207 | | if (isByRef(param_ty, mod)) { |
| 1208 | | const alignment = param_ty.abiAlignment(mod); |
| 1209 | | const param_llvm_ty = param.typeOf(); |
| 1210 | | const arg_ptr = try o.buildAllocaInner(&wip, builder, llvm_func, false, param_llvm_ty, alignment, target); |
| 1211 | | const store_inst = builder.buildStore(param, arg_ptr); |
| 1212 | | store_inst.setAlignment(alignment); |
| 1213 | | args.appendAssumeCapacity(arg_ptr); |
| 1214 | | } else { |
| 1215 | | args.appendAssumeCapacity(param); |
| 1216 | | |
| 1217 | | o.addByValParamAttrs(llvm_func, param_ty, param_index, fn_info, llvm_arg_i); |
| 1218 | | } |
| 1219 | | llvm_arg_i += 1; |
| 1220 | | }, |
| 1221 | | .byref => { |
| 1222 | | const param_ty = fn_info.param_types.get(ip)[it.zig_index - 1].toType(); |
| 1223 | | const param_llvm_ty = try o.lowerType(param_ty); |
| 1224 | | const param = llvm_func.getParam(llvm_arg_i); |
| 1225 | | const alignment = param_ty.abiAlignment(mod); |
| 1226 | | |
| 1227 | | o.addByRefParamAttrs(llvm_func, llvm_arg_i, alignment, it.byval_attr, param_llvm_ty); |
| 1228 | | llvm_arg_i += 1; |
| 1229 | | |
| 1230 | | try args.ensureUnusedCapacity(1); |
| 1231 | | |
| 1232 | | if (isByRef(param_ty, mod)) { |
| 1233 | | args.appendAssumeCapacity(param); |
| 1234 | | } else { |
| 1235 | | const load_inst = builder.buildLoad(param_llvm_ty.toLlvm(&o.builder), param, ""); |
| 1236 | | load_inst.setAlignment(alignment); |
| 1237 | | args.appendAssumeCapacity(load_inst); |
| 1238 | | } |
| 1239 | | }, |
| 1240 | | .byref_mut => { |
| 1241 | | const param_ty = fn_info.param_types.get(ip)[it.zig_index - 1].toType(); |
| 1242 | | const param_llvm_ty = (try o.lowerType(param_ty)).toLlvm(&o.builder); |
| 1243 | | const param = llvm_func.getParam(llvm_arg_i); |
| 1244 | | const alignment = param_ty.abiAlignment(mod); |
| 1187 | while (try it.next()) |lowering| { |
| 1188 | try args.ensureUnusedCapacity(gpa, 1); |
| 1189 | |
| 1190 | switch (lowering) { |
| 1191 | .no_bits => continue, |
| 1192 | .byval => { |
| 1193 | assert(!it.byval_attr); |
| 1194 | const param_index = it.zig_index - 1; |
| 1195 | const param_ty = fn_info.param_types.get(ip)[param_index].toType(); |
| 1196 | const param = wip.arg(llvm_arg_i); |
| 1197 | |
| 1198 | if (isByRef(param_ty, mod)) { |
| 1199 | const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); |
| 1200 | const param_llvm_ty = param.typeOfWip(&wip); |
| 1201 | const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, alignment, target); |
| 1202 | _ = try wip.store(.normal, param, arg_ptr, alignment); |
| 1203 | args.appendAssumeCapacity(arg_ptr); |
| 1204 | } else { |
| 1205 | args.appendAssumeCapacity(param); |
| 1245 | 1206 | |
| 1246 | | o.addArgAttr(llvm_func, llvm_arg_i, "noundef"); |
| 1247 | | llvm_arg_i += 1; |
| 1207 | o.addByValParamAttrs(llvm_func, param_ty, param_index, fn_info, @intCast(llvm_arg_i)); |
| 1208 | } |
| 1209 | llvm_arg_i += 1; |
| 1210 | }, |
| 1211 | .byref => { |
| 1212 | const param_ty = fn_info.param_types.get(ip)[it.zig_index - 1].toType(); |
| 1213 | const param_llvm_ty = try o.lowerType(param_ty); |
| 1214 | const param = wip.arg(llvm_arg_i); |
| 1215 | const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); |
| 1248 | 1216 | |
| 1249 | | try args.ensureUnusedCapacity(1); |
| 1217 | o.addByRefParamAttrs(llvm_func, @intCast(llvm_arg_i), @intCast(alignment.toByteUnits() orelse 0), it.byval_attr, param_llvm_ty); |
| 1218 | llvm_arg_i += 1; |
| 1250 | 1219 | |
| 1251 | | if (isByRef(param_ty, mod)) { |
| 1252 | | args.appendAssumeCapacity(param); |
| 1253 | | } else { |
| 1254 | | const load_inst = builder.buildLoad(param_llvm_ty, param, ""); |
| 1255 | | load_inst.setAlignment(alignment); |
| 1256 | | args.appendAssumeCapacity(load_inst); |
| 1257 | | } |
| 1258 | | }, |
| 1259 | | .abi_sized_int => { |
| 1260 | | assert(!it.byval_attr); |
| 1261 | | const param_ty = fn_info.param_types.get(ip)[it.zig_index - 1].toType(); |
| 1262 | | const param = llvm_func.getParam(llvm_arg_i); |
| 1263 | | llvm_arg_i += 1; |
| 1220 | if (isByRef(param_ty, mod)) { |
| 1221 | args.appendAssumeCapacity(param); |
| 1222 | } else { |
| 1223 | args.appendAssumeCapacity(try wip.load(.normal, param_llvm_ty, param, alignment, "")); |
| 1224 | } |
| 1225 | }, |
| 1226 | .byref_mut => { |
| 1227 | const param_ty = fn_info.param_types.get(ip)[it.zig_index - 1].toType(); |
| 1228 | const param_llvm_ty = try o.lowerType(param_ty); |
| 1229 | const param = wip.arg(llvm_arg_i); |
| 1230 | const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); |
| 1264 | 1231 | |
| 1265 | | const param_llvm_ty = (try o.lowerType(param_ty)).toLlvm(&o.builder); |
| 1266 | | const int_llvm_ty = (try o.builder.intType(@intCast(param_ty.abiSize(mod) * 8))).toLlvm(&o.builder); |
| 1267 | | const alignment = @max( |
| 1268 | | param_ty.abiAlignment(mod), |
| 1269 | | o.target_data.abiAlignmentOfType(int_llvm_ty), |
| 1270 | | ); |
| 1271 | | const arg_ptr = try o.buildAllocaInner(&wip, builder, llvm_func, false, param_llvm_ty, alignment, target); |
| 1272 | | const store_inst = builder.buildStore(param, arg_ptr); |
| 1273 | | store_inst.setAlignment(alignment); |
| 1232 | o.addArgAttr(llvm_func, @intCast(llvm_arg_i), "noundef"); |
| 1233 | llvm_arg_i += 1; |
| 1274 | 1234 | |
| 1275 | | try args.ensureUnusedCapacity(1); |
| 1235 | if (isByRef(param_ty, mod)) { |
| 1236 | args.appendAssumeCapacity(param); |
| 1237 | } else { |
| 1238 | args.appendAssumeCapacity(try wip.load(.normal, param_llvm_ty, param, alignment, "")); |
| 1239 | } |
| 1240 | }, |
| 1241 | .abi_sized_int => { |
| 1242 | assert(!it.byval_attr); |
| 1243 | const param_ty = fn_info.param_types.get(ip)[it.zig_index - 1].toType(); |
| 1244 | const param = wip.arg(llvm_arg_i); |
| 1245 | llvm_arg_i += 1; |
| 1276 | 1246 | |
| 1277 | | if (isByRef(param_ty, mod)) { |
| 1278 | | args.appendAssumeCapacity(arg_ptr); |
| 1279 | | } else { |
| 1280 | | const load_inst = builder.buildLoad(param_llvm_ty, arg_ptr, ""); |
| 1281 | | load_inst.setAlignment(alignment); |
| 1282 | | args.appendAssumeCapacity(load_inst); |
| 1283 | | } |
| 1284 | | }, |
| 1285 | | .slice => { |
| 1286 | | assert(!it.byval_attr); |
| 1287 | | const param_ty = fn_info.param_types.get(ip)[it.zig_index - 1].toType(); |
| 1288 | | const ptr_info = param_ty.ptrInfo(mod); |
| 1247 | const param_llvm_ty = try o.lowerType(param_ty); |
| 1248 | const int_llvm_ty = try o.builder.intType(@intCast(param_ty.abiSize(mod) * 8)); |
| 1249 | const alignment = Builder.Alignment.fromByteUnits(@max( |
| 1250 | param_ty.abiAlignment(mod), |
| 1251 | o.target_data.abiAlignmentOfType(int_llvm_ty.toLlvm(&o.builder)), |
| 1252 | )); |
| 1253 | const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, alignment, target); |
| 1254 | _ = try wip.store(.normal, param, arg_ptr, alignment); |
| 1289 | 1255 | |
| 1290 | | if (math.cast(u5, it.zig_index - 1)) |i| { |
| 1291 | | if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) { |
| 1292 | | o.addArgAttr(llvm_func, llvm_arg_i, "noalias"); |
| 1256 | args.appendAssumeCapacity(if (isByRef(param_ty, mod)) |
| 1257 | arg_ptr |
| 1258 | else |
| 1259 | try wip.load(.normal, param_llvm_ty, arg_ptr, alignment, "")); |
| 1260 | }, |
| 1261 | .slice => { |
| 1262 | assert(!it.byval_attr); |
| 1263 | const param_ty = fn_info.param_types.get(ip)[it.zig_index - 1].toType(); |
| 1264 | const ptr_info = param_ty.ptrInfo(mod); |
| 1265 | |
| 1266 | if (math.cast(u5, it.zig_index - 1)) |i| { |
| 1267 | if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) { |
| 1268 | o.addArgAttr(llvm_func, @intCast(llvm_arg_i), "noalias"); |
| 1269 | } |
| 1270 | } |
| 1271 | if (param_ty.zigTypeTag(mod) != .Optional) { |
| 1272 | o.addArgAttr(llvm_func, @intCast(llvm_arg_i), "nonnull"); |
| 1273 | } |
| 1274 | if (ptr_info.flags.is_const) { |
| 1275 | o.addArgAttr(llvm_func, @intCast(llvm_arg_i), "readonly"); |
| 1276 | } |
| 1277 | const elem_align = ptr_info.flags.alignment.toByteUnitsOptional() orelse |
| 1278 | @max(ptr_info.child.toType().abiAlignment(mod), 1); |
| 1279 | o.addArgAttrInt(llvm_func, @intCast(llvm_arg_i), "align", elem_align); |
| 1280 | const ptr_param = wip.arg(llvm_arg_i + 0); |
| 1281 | const len_param = wip.arg(llvm_arg_i + 1); |
| 1282 | llvm_arg_i += 2; |
| 1283 | |
| 1284 | const slice_llvm_ty = try o.lowerType(param_ty); |
| 1285 | args.appendAssumeCapacity( |
| 1286 | try wip.buildAggregate(slice_llvm_ty, &.{ ptr_param, len_param }, ""), |
| 1287 | ); |
| 1288 | }, |
| 1289 | .multiple_llvm_types => { |
| 1290 | assert(!it.byval_attr); |
| 1291 | const field_types = it.types_buffer[0..it.types_len]; |
| 1292 | const param_ty = fn_info.param_types.get(ip)[it.zig_index - 1].toType(); |
| 1293 | const param_llvm_ty = try o.lowerType(param_ty); |
| 1294 | const param_alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); |
| 1295 | const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, param_alignment, target); |
| 1296 | const llvm_ty = try o.builder.structType(.normal, field_types); |
| 1297 | for (0..field_types.len) |field_i| { |
| 1298 | const param = wip.arg(llvm_arg_i); |
| 1299 | llvm_arg_i += 1; |
| 1300 | const field_ptr = try wip.gepStruct(llvm_ty, arg_ptr, field_i, ""); |
| 1301 | const alignment = |
| 1302 | Builder.Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)); |
| 1303 | _ = try wip.store(.normal, param, field_ptr, alignment); |
| 1293 | 1304 | } |
| 1294 | | } |
| 1295 | | if (param_ty.zigTypeTag(mod) != .Optional) { |
| 1296 | | o.addArgAttr(llvm_func, llvm_arg_i, "nonnull"); |
| 1297 | | } |
| 1298 | | if (ptr_info.flags.is_const) { |
| 1299 | | o.addArgAttr(llvm_func, llvm_arg_i, "readonly"); |
| 1300 | | } |
| 1301 | | const elem_align = ptr_info.flags.alignment.toByteUnitsOptional() orelse |
| 1302 | | @max(ptr_info.child.toType().abiAlignment(mod), 1); |
| 1303 | | o.addArgAttrInt(llvm_func, llvm_arg_i, "align", elem_align); |
| 1304 | | const ptr_param = llvm_func.getParam(llvm_arg_i); |
| 1305 | | llvm_arg_i += 1; |
| 1306 | | const len_param = llvm_func.getParam(llvm_arg_i); |
| 1307 | | llvm_arg_i += 1; |
| 1308 | | |
| 1309 | | const slice_llvm_ty = (try o.lowerType(param_ty)).toLlvm(&o.builder); |
| 1310 | | const partial = builder.buildInsertValue(slice_llvm_ty.getUndef(), ptr_param, 0, ""); |
| 1311 | | const aggregate = builder.buildInsertValue(partial, len_param, 1, ""); |
| 1312 | | try args.append(aggregate); |
| 1313 | | }, |
| 1314 | | .multiple_llvm_types => { |
| 1315 | | assert(!it.byval_attr); |
| 1316 | | const field_types = it.types_buffer[0..it.types_len]; |
| 1317 | | const param_ty = fn_info.param_types.get(ip)[it.zig_index - 1].toType(); |
| 1318 | | const param_llvm_ty = (try o.lowerType(param_ty)).toLlvm(&o.builder); |
| 1319 | | const param_alignment = param_ty.abiAlignment(mod); |
| 1320 | | const arg_ptr = try o.buildAllocaInner(&wip, builder, llvm_func, false, param_llvm_ty, param_alignment, target); |
| 1321 | | const llvm_ty = (try o.builder.structType(.normal, field_types)).toLlvm(&o.builder); |
| 1322 | | for (0..field_types.len) |field_i| { |
| 1323 | | const param = llvm_func.getParam(llvm_arg_i); |
| 1324 | | llvm_arg_i += 1; |
| 1325 | | const field_ptr = builder.buildStructGEP(llvm_ty, arg_ptr, @intCast(field_i), ""); |
| 1326 | | const store_inst = builder.buildStore(param, field_ptr); |
| 1327 | | store_inst.setAlignment(target.ptrBitWidth() / 8); |
| 1328 | | } |
| 1329 | 1305 | |
| 1330 | | const is_by_ref = isByRef(param_ty, mod); |
| 1331 | | const loaded = if (is_by_ref) arg_ptr else l: { |
| 1332 | | const load_inst = builder.buildLoad(param_llvm_ty, arg_ptr, ""); |
| 1333 | | load_inst.setAlignment(param_alignment); |
| 1334 | | break :l load_inst; |
| 1335 | | }; |
| 1336 | | try args.append(loaded); |
| 1337 | | }, |
| 1338 | | .as_u16 => { |
| 1339 | | assert(!it.byval_attr); |
| 1340 | | const param = llvm_func.getParam(llvm_arg_i); |
| 1341 | | llvm_arg_i += 1; |
| 1342 | | const casted = builder.buildBitCast(param, Builder.Type.half.toLlvm(&o.builder), ""); |
| 1343 | | try args.ensureUnusedCapacity(1); |
| 1344 | | args.appendAssumeCapacity(casted); |
| 1345 | | }, |
| 1346 | | .float_array => { |
| 1347 | | const param_ty = fn_info.param_types.get(ip)[it.zig_index - 1].toType(); |
| 1348 | | const param_llvm_ty = (try o.lowerType(param_ty)).toLlvm(&o.builder); |
| 1349 | | const param = llvm_func.getParam(llvm_arg_i); |
| 1350 | | llvm_arg_i += 1; |
| 1306 | const is_by_ref = isByRef(param_ty, mod); |
| 1307 | args.appendAssumeCapacity(if (is_by_ref) |
| 1308 | arg_ptr |
| 1309 | else |
| 1310 | try wip.load(.normal, param_llvm_ty, arg_ptr, param_alignment, "")); |
| 1311 | }, |
| 1312 | .as_u16 => { |
| 1313 | assert(!it.byval_attr); |
| 1314 | const param = wip.arg(llvm_arg_i); |
| 1315 | llvm_arg_i += 1; |
| 1316 | args.appendAssumeCapacity(try wip.cast(.bitcast, param, .half, "")); |
| 1317 | }, |
| 1318 | .float_array => { |
| 1319 | const param_ty = fn_info.param_types.get(ip)[it.zig_index - 1].toType(); |
| 1320 | const param_llvm_ty = try o.lowerType(param_ty); |
| 1321 | const param = wip.arg(llvm_arg_i); |
| 1322 | llvm_arg_i += 1; |
| 1351 | 1323 | |
| 1352 | | const alignment = param_ty.abiAlignment(mod); |
| 1353 | | const arg_ptr = try o.buildAllocaInner(&wip, builder, llvm_func, false, param_llvm_ty, alignment, target); |
| 1354 | | _ = builder.buildStore(param, arg_ptr); |
| 1324 | const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); |
| 1325 | const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, alignment, target); |
| 1326 | _ = try wip.store(.normal, param, arg_ptr, alignment); |
| 1355 | 1327 | |
| 1356 | | if (isByRef(param_ty, mod)) { |
| 1357 | | try args.append(arg_ptr); |
| 1358 | | } else { |
| 1359 | | const load_inst = builder.buildLoad(param_llvm_ty, arg_ptr, ""); |
| 1360 | | load_inst.setAlignment(alignment); |
| 1361 | | try args.append(load_inst); |
| 1362 | | } |
| 1363 | | }, |
| 1364 | | .i32_array, .i64_array => { |
| 1365 | | const param_ty = fn_info.param_types.get(ip)[it.zig_index - 1].toType(); |
| 1366 | | const param_llvm_ty = (try o.lowerType(param_ty)).toLlvm(&o.builder); |
| 1367 | | const param = llvm_func.getParam(llvm_arg_i); |
| 1368 | | llvm_arg_i += 1; |
| 1328 | args.appendAssumeCapacity(if (isByRef(param_ty, mod)) |
| 1329 | arg_ptr |
| 1330 | else |
| 1331 | try wip.load(.normal, param_llvm_ty, arg_ptr, alignment, "")); |
| 1332 | }, |
| 1333 | .i32_array, .i64_array => { |
| 1334 | const param_ty = fn_info.param_types.get(ip)[it.zig_index - 1].toType(); |
| 1335 | const param_llvm_ty = try o.lowerType(param_ty); |
| 1336 | const param = wip.arg(llvm_arg_i); |
| 1337 | llvm_arg_i += 1; |
| 1369 | 1338 | |
| 1370 | | const alignment = param_ty.abiAlignment(mod); |
| 1371 | | const arg_ptr = try o.buildAllocaInner(&wip, builder, llvm_func, false, param_llvm_ty, alignment, target); |
| 1372 | | _ = builder.buildStore(param, arg_ptr); |
| 1339 | const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); |
| 1340 | const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, alignment, target); |
| 1341 | _ = try wip.store(.normal, param, arg_ptr, alignment); |
| 1373 | 1342 | |
| 1374 | | if (isByRef(param_ty, mod)) { |
| 1375 | | try args.append(arg_ptr); |
| 1376 | | } else { |
| 1377 | | const load_inst = builder.buildLoad(param_llvm_ty, arg_ptr, ""); |
| 1378 | | load_inst.setAlignment(alignment); |
| 1379 | | try args.append(load_inst); |
| 1380 | | } |
| 1381 | | }, |
| 1382 | | }; |
| 1343 | args.appendAssumeCapacity(if (isByRef(param_ty, mod)) |
| 1344 | arg_ptr |
| 1345 | else |
| 1346 | try wip.load(.normal, param_llvm_ty, arg_ptr, alignment, "")); |
| 1347 | }, |
| 1348 | } |
| 1349 | } |
| 1383 | 1350 | } |
| 1384 | 1351 | |
| 1385 | 1352 | var di_file: ?*llvm.DIFile = null; |
| ... | ... | @@ -1421,7 +1388,6 @@ pub const Object = struct { |
| 1421 | 1388 | .gpa = gpa, |
| 1422 | 1389 | .air = air, |
| 1423 | 1390 | .liveness = liveness, |
| 1424 | | .context = o.context, |
| 1425 | 1391 | .dg = &dg, |
| 1426 | 1392 | .wip = wip, |
| 1427 | 1393 | .builder = builder, |
| ... | ... | @@ -1429,9 +1395,8 @@ pub const Object = struct { |
| 1429 | 1395 | .args = args.items, |
| 1430 | 1396 | .arg_index = 0, |
| 1431 | 1397 | .func_inst_table = .{}, |
| 1432 | | .llvm_func = llvm_func, |
| 1433 | 1398 | .blocks = .{}, |
| 1434 | | .single_threaded = mod.comp.bin_file.options.single_threaded, |
| 1399 | .sync_scope = if (mod.comp.bin_file.options.single_threaded) .singlethread else .system, |
| 1435 | 1400 | .di_scope = di_scope, |
| 1436 | 1401 | .di_file = di_file, |
| 1437 | 1402 | .base_line = dg.decl.src_line, |
| ... | ... | @@ -1523,11 +1488,11 @@ pub const Object = struct { |
| 1523 | 1488 | const decl_name_slice = decl_name.toSlice(&self.builder).?; |
| 1524 | 1489 | if (try decl.isFunction(mod)) { |
| 1525 | 1490 | const di_func: *llvm.DISubprogram = @ptrCast(di_node); |
| 1526 | | const linkage_name = llvm.MDString.get(self.context, decl_name_slice.ptr, decl_name_slice.len); |
| 1491 | const linkage_name = llvm.MDString.get(self.builder.llvm.context, decl_name_slice.ptr, decl_name_slice.len); |
| 1527 | 1492 | di_func.replaceLinkageName(linkage_name); |
| 1528 | 1493 | } else { |
| 1529 | 1494 | const di_global: *llvm.DIGlobalVariable = @ptrCast(di_node); |
| 1530 | | const linkage_name = llvm.MDString.get(self.context, decl_name_slice.ptr, decl_name_slice.len); |
| 1495 | const linkage_name = llvm.MDString.get(self.builder.llvm.context, decl_name_slice.ptr, decl_name_slice.len); |
| 1531 | 1496 | di_global.replaceLinkageName(linkage_name); |
| 1532 | 1497 | } |
| 1533 | 1498 | } |
| ... | ... | @@ -1560,11 +1525,11 @@ pub const Object = struct { |
| 1560 | 1525 | const exp_name_slice = exp_name.toSlice(&self.builder).?; |
| 1561 | 1526 | if (try decl.isFunction(mod)) { |
| 1562 | 1527 | const di_func: *llvm.DISubprogram = @ptrCast(di_node); |
| 1563 | | const linkage_name = llvm.MDString.get(self.context, exp_name_slice.ptr, exp_name_slice.len); |
| 1528 | const linkage_name = llvm.MDString.get(self.builder.llvm.context, exp_name_slice.ptr, exp_name_slice.len); |
| 1564 | 1529 | di_func.replaceLinkageName(linkage_name); |
| 1565 | 1530 | } else { |
| 1566 | 1531 | const di_global: *llvm.DIGlobalVariable = @ptrCast(di_node); |
| 1567 | | const linkage_name = llvm.MDString.get(self.context, exp_name_slice.ptr, exp_name_slice.len); |
| 1532 | const linkage_name = llvm.MDString.get(self.builder.llvm.context, exp_name_slice.ptr, exp_name_slice.len); |
| 1568 | 1533 | di_global.replaceLinkageName(linkage_name); |
| 1569 | 1534 | } |
| 1570 | 1535 | } |
| ... | ... | @@ -1598,7 +1563,11 @@ pub const Object = struct { |
| 1598 | 1563 | }, |
| 1599 | 1564 | } |
| 1600 | 1565 | if (mod.intern_pool.stringToSliceUnwrap(exports[0].opts.section)) |section| { |
| 1601 | | global.ptr(&self.builder).section = try self.builder.string(section); |
| 1566 | switch (global.ptrConst(&self.builder).kind) { |
| 1567 | inline .variable, .function => |impl_index| impl_index.ptr(&self.builder).section = |
| 1568 | try self.builder.string(section), |
| 1569 | else => unreachable, |
| 1570 | } |
| 1602 | 1571 | llvm_global.setSection(section); |
| 1603 | 1572 | } |
| 1604 | 1573 | if (decl.val.getVariable(mod)) |decl_var| { |
| ... | ... | @@ -1623,7 +1592,7 @@ pub const Object = struct { |
| 1623 | 1592 | alias.setAliasee(llvm_global); |
| 1624 | 1593 | } else { |
| 1625 | 1594 | _ = self.llvm_module.addAlias( |
| 1626 | | llvm_global.globalGetValueType(), |
| 1595 | global.ptrConst(&self.builder).type.toLlvm(&self.builder), |
| 1627 | 1596 | 0, |
| 1628 | 1597 | llvm_global, |
| 1629 | 1598 | exp_name_z, |
| ... | ... | @@ -2773,7 +2742,7 @@ pub const Object = struct { |
| 2773 | 2742 | } |
| 2774 | 2743 | |
| 2775 | 2744 | if (fn_info.alignment.toByteUnitsOptional()) |a| { |
| 2776 | | global.alignment = Builder.Alignment.fromByteUnits(a); |
| 2745 | function.alignment = Builder.Alignment.fromByteUnits(a); |
| 2777 | 2746 | llvm_fn.setAlignment(@intCast(a)); |
| 2778 | 2747 | } |
| 2779 | 2748 | |
| ... | ... | @@ -2944,7 +2913,7 @@ pub const Object = struct { |
| 2944 | 2913 | const llvm_ty = ty.toLlvm(&o.builder); |
| 2945 | 2914 | if (t.zigTypeTag(mod) == .Opaque) break :check; |
| 2946 | 2915 | if (!t.hasRuntimeBits(mod)) break :check; |
| 2947 | | if (!llvm_ty.isSized().toBool()) break :check; |
| 2916 | if (!try ty.isSized(&o.builder)) break :check; |
| 2948 | 2917 | |
| 2949 | 2918 | const zig_size = t.abiSize(mod); |
| 2950 | 2919 | const llvm_size = o.target_data.abiSizeOfType(llvm_ty); |
| ... | ... | @@ -3807,7 +3776,7 @@ pub const Object = struct { |
| 3807 | 3776 | } |
| 3808 | 3777 | assert(llvm_index == llvm_len); |
| 3809 | 3778 | |
| 3810 | | return try o.builder.structConst(if (need_unnamed) |
| 3779 | return o.builder.structConst(if (need_unnamed) |
| 3811 | 3780 | try o.builder.structType(struct_ty.structKind(&o.builder), fields) |
| 3812 | 3781 | else |
| 3813 | 3782 | struct_ty, vals); |
| ... | ... | @@ -3904,7 +3873,7 @@ pub const Object = struct { |
| 3904 | 3873 | } |
| 3905 | 3874 | assert(llvm_index == llvm_len); |
| 3906 | 3875 | |
| 3907 | | return try o.builder.structConst(if (need_unnamed) |
| 3876 | return o.builder.structConst(if (need_unnamed) |
| 3908 | 3877 | try o.builder.structType(struct_ty.structKind(&o.builder), fields) |
| 3909 | 3878 | else |
| 3910 | 3879 | struct_ty, vals); |
| ... | ... | @@ -3978,7 +3947,7 @@ pub const Object = struct { |
| 3978 | 3947 | vals[2] = try o.builder.undefConst(fields[2]); |
| 3979 | 3948 | len = 3; |
| 3980 | 3949 | } |
| 3981 | | return try o.builder.structConst(if (need_unnamed) |
| 3950 | return o.builder.structConst(if (need_unnamed) |
| 3982 | 3951 | try o.builder.structType(union_ty.structKind(&o.builder), fields[0..len]) |
| 3983 | 3952 | else |
| 3984 | 3953 | union_ty, vals[0..len]); |
| ... | ... | @@ -4012,7 +3981,7 @@ pub const Object = struct { |
| 4012 | 3981 | |
| 4013 | 3982 | const ParentPtr = struct { |
| 4014 | 3983 | ty: Type, |
| 4015 | | llvm_ptr: *llvm.Value, |
| 3984 | llvm_ptr: Builder.Value, |
| 4016 | 3985 | }; |
| 4017 | 3986 | |
| 4018 | 3987 | fn lowerParentPtrDecl(o: *Object, decl_index: Module.Decl.Index) Allocator.Error!Builder.Constant { |
| ... | ... | @@ -4040,12 +4009,10 @@ pub const Object = struct { |
| 4040 | 4009 | return parent_ptr; |
| 4041 | 4010 | } |
| 4042 | 4011 | |
| 4043 | | return o.builder.gepConst(.inbounds, try o.lowerType(eu_ty), parent_ptr, &.{ |
| 4044 | | try o.builder.intConst(.i32, 0), |
| 4045 | | try o.builder.intConst(.i32, @as( |
| 4046 | | i32, |
| 4047 | | if (payload_ty.abiAlignment(mod) > Type.err_int.abiSize(mod)) 2 else 1, |
| 4048 | | )), |
| 4012 | const index: u32 = |
| 4013 | if (payload_ty.abiAlignment(mod) > Type.err_int.abiSize(mod)) 2 else 1; |
| 4014 | return o.builder.gepConst(.inbounds, try o.lowerType(eu_ty), parent_ptr, null, &.{ |
| 4015 | try o.builder.intConst(.i32, 0), try o.builder.intConst(.i32, index), |
| 4049 | 4016 | }); |
| 4050 | 4017 | }, |
| 4051 | 4018 | .opt_payload => |opt_ptr| { |
| ... | ... | @@ -4061,16 +4028,16 @@ pub const Object = struct { |
| 4061 | 4028 | return parent_ptr; |
| 4062 | 4029 | } |
| 4063 | 4030 | |
| 4064 | | return o.builder.gepConst(.inbounds, try o.lowerType(opt_ty), parent_ptr, &(.{ |
| 4065 | | try o.builder.intConst(.i32, 0), |
| 4066 | | } ** 2)); |
| 4031 | return o.builder.gepConst(.inbounds, try o.lowerType(opt_ty), parent_ptr, null, &.{ |
| 4032 | try o.builder.intConst(.i32, 0), try o.builder.intConst(.i32, 0), |
| 4033 | }); |
| 4067 | 4034 | }, |
| 4068 | 4035 | .comptime_field => unreachable, |
| 4069 | 4036 | .elem => |elem_ptr| { |
| 4070 | 4037 | const parent_ptr = try o.lowerParentPtr(elem_ptr.base.toValue(), true); |
| 4071 | 4038 | const elem_ty = mod.intern_pool.typeOf(elem_ptr.base).toType().elemType2(mod); |
| 4072 | 4039 | |
| 4073 | | return o.builder.gepConst(.inbounds, try o.lowerType(elem_ty), parent_ptr, &.{ |
| 4040 | return o.builder.gepConst(.inbounds, try o.lowerType(elem_ty), parent_ptr, null, &.{ |
| 4074 | 4041 | try o.builder.intConst(try o.lowerType(Type.usize), elem_ptr.index), |
| 4075 | 4042 | }); |
| 4076 | 4043 | }, |
| ... | ... | @@ -4092,9 +4059,9 @@ pub const Object = struct { |
| 4092 | 4059 | return parent_ptr; |
| 4093 | 4060 | } |
| 4094 | 4061 | |
| 4095 | | return o.builder.gepConst(.inbounds, try o.lowerType(parent_ty), parent_ptr, &.{ |
| 4096 | | try o.builder.intConst(.i32, 0), |
| 4097 | | try o.builder.intConst(.i32, @intFromBool( |
| 4062 | const parent_llvm_ty = try o.lowerType(parent_ty); |
| 4063 | return o.builder.gepConst(.inbounds, parent_llvm_ty, parent_ptr, null, &.{ |
| 4064 | try o.builder.intConst(.i32, 0), try o.builder.intConst(.i32, @intFromBool( |
| 4098 | 4065 | layout.tag_size > 0 and layout.tag_align >= layout.payload_align, |
| 4099 | 4066 | )), |
| 4100 | 4067 | }); |
| ... | ... | @@ -4109,7 +4076,8 @@ pub const Object = struct { |
| 4109 | 4076 | const prev_bits = b: { |
| 4110 | 4077 | var b: usize = 0; |
| 4111 | 4078 | for (parent_ty.structFields(mod).values()[0..field_index]) |field| { |
| 4112 | | if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime(mod)) continue; |
| 4079 | if (field.is_comptime) continue; |
| 4080 | if (!field.ty.hasRuntimeBitsIgnoreComptime(mod)) continue; |
| 4113 | 4081 | b += @intCast(field.ty.bitSize(mod)); |
| 4114 | 4082 | } |
| 4115 | 4083 | break :b b; |
| ... | ... | @@ -4123,6 +4091,7 @@ pub const Object = struct { |
| 4123 | 4091 | .inbounds, |
| 4124 | 4092 | try o.lowerType(parent_ty), |
| 4125 | 4093 | parent_ptr, |
| 4094 | null, |
| 4126 | 4095 | if (llvmField(parent_ty, field_index, mod)) |llvm_field| &.{ |
| 4127 | 4096 | try o.builder.intConst(.i32, 0), |
| 4128 | 4097 | try o.builder.intConst(.i32, llvm_field.index), |
| ... | ... | @@ -4135,9 +4104,9 @@ pub const Object = struct { |
| 4135 | 4104 | }, |
| 4136 | 4105 | .Pointer => { |
| 4137 | 4106 | assert(parent_ty.isSlice(mod)); |
| 4138 | | return o.builder.gepConst(.inbounds, try o.lowerType(parent_ty), parent_ptr, &.{ |
| 4139 | | try o.builder.intConst(.i32, 0), |
| 4140 | | try o.builder.intConst(.i32, field_index), |
| 4107 | const parent_llvm_ty = try o.lowerType(parent_ty); |
| 4108 | return o.builder.gepConst(.inbounds, parent_llvm_ty, parent_ptr, null, &.{ |
| 4109 | try o.builder.intConst(.i32, 0), try o.builder.intConst(.i32, field_index), |
| 4141 | 4110 | }); |
| 4142 | 4111 | }, |
| 4143 | 4112 | else => unreachable, |
| ... | ... | @@ -4167,8 +4136,7 @@ pub const Object = struct { |
| 4167 | 4136 | |
| 4168 | 4137 | const is_fn_body = decl.ty.zigTypeTag(mod) == .Fn; |
| 4169 | 4138 | if ((!is_fn_body and !decl.ty.hasRuntimeBits(mod)) or |
| 4170 | | (is_fn_body and mod.typeToFunc(decl.ty).?.is_generic)) |
| 4171 | | return o.lowerPtrToVoid(ty); |
| 4139 | (is_fn_body and mod.typeToFunc(decl.ty).?.is_generic)) return o.lowerPtrToVoid(ty); |
| 4172 | 4140 | |
| 4173 | 4141 | try mod.markDeclAlive(decl); |
| 4174 | 4142 | |
| ... | ... | @@ -4240,7 +4208,7 @@ pub const Object = struct { |
| 4240 | 4208 | ) void { |
| 4241 | 4209 | const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len); |
| 4242 | 4210 | assert(kind_id != 0); |
| 4243 | | const llvm_attr = o.context.createEnumAttribute(kind_id, int); |
| 4211 | const llvm_attr = o.builder.llvm.context.createEnumAttribute(kind_id, int); |
| 4244 | 4212 | val.addAttributeAtIndex(index, llvm_attr); |
| 4245 | 4213 | } |
| 4246 | 4214 | |
| ... | ... | @@ -4251,7 +4219,7 @@ pub const Object = struct { |
| 4251 | 4219 | name: []const u8, |
| 4252 | 4220 | value: []const u8, |
| 4253 | 4221 | ) void { |
| 4254 | | const llvm_attr = o.context.createStringAttribute( |
| 4222 | const llvm_attr = o.builder.llvm.context.createStringAttribute( |
| 4255 | 4223 | name.ptr, |
| 4256 | 4224 | @intCast(name.len), |
| 4257 | 4225 | value.ptr, |
| ... | ... | @@ -4346,51 +4314,6 @@ pub const Object = struct { |
| 4346 | 4314 | llvm_fn.addByValAttr(llvm_arg_i, param_llvm_ty.toLlvm(&o.builder)); |
| 4347 | 4315 | } |
| 4348 | 4316 | } |
| 4349 | | |
| 4350 | | fn buildAllocaInner( |
| 4351 | | o: *Object, |
| 4352 | | wip: *Builder.WipFunction, |
| 4353 | | builder: *llvm.Builder, |
| 4354 | | llvm_func: *llvm.Value, |
| 4355 | | di_scope_non_null: bool, |
| 4356 | | llvm_ty: *llvm.Type, |
| 4357 | | maybe_alignment: ?c_uint, |
| 4358 | | target: std.Target, |
| 4359 | | ) Allocator.Error!*llvm.Value { |
| 4360 | | const address_space = llvmAllocaAddressSpace(target); |
| 4361 | | |
| 4362 | | const alloca = blk: { |
| 4363 | | const prev_cursor = wip.cursor; |
| 4364 | | const prev_block = builder.getInsertBlock(); |
| 4365 | | const prev_debug_location = builder.getCurrentDebugLocation2(); |
| 4366 | | defer { |
| 4367 | | wip.cursor = prev_cursor; |
| 4368 | | builder.positionBuilderAtEnd(prev_block); |
| 4369 | | if (di_scope_non_null) { |
| 4370 | | builder.setCurrentDebugLocation2(prev_debug_location); |
| 4371 | | } |
| 4372 | | } |
| 4373 | | |
| 4374 | | const entry_block = llvm_func.getFirstBasicBlock().?; |
| 4375 | | wip.cursor = .{ .block = .entry }; |
| 4376 | | builder.positionBuilder(entry_block, entry_block.getFirstInstruction()); |
| 4377 | | builder.clearCurrentDebugLocation(); |
| 4378 | | |
| 4379 | | break :blk builder.buildAllocaInAddressSpace(llvm_ty, @intFromEnum(address_space), ""); |
| 4380 | | }; |
| 4381 | | |
| 4382 | | if (maybe_alignment) |alignment| { |
| 4383 | | alloca.setAlignment(alignment); |
| 4384 | | } |
| 4385 | | |
| 4386 | | // The pointer returned from this function should have the generic address space, |
| 4387 | | // if this isn't the case then cast it to the generic address space. |
| 4388 | | if (address_space != .default) { |
| 4389 | | return builder.buildAddrSpaceCast(alloca, Builder.Type.ptr.toLlvm(&o.builder), ""); |
| 4390 | | } |
| 4391 | | |
| 4392 | | return alloca; |
| 4393 | | } |
| 4394 | 4317 | }; |
| 4395 | 4318 | |
| 4396 | 4319 | pub const DeclGen = struct { |
| ... | ... | @@ -4424,10 +4347,10 @@ pub const DeclGen = struct { |
| 4424 | 4347 | const variable = try o.resolveGlobalDecl(decl_index); |
| 4425 | 4348 | const global = variable.ptrConst(&o.builder).global; |
| 4426 | 4349 | var llvm_global = global.toLlvm(&o.builder); |
| 4427 | | global.ptr(&o.builder).alignment = Builder.Alignment.fromByteUnits(decl.getAlignment(mod)); |
| 4350 | variable.ptr(&o.builder).alignment = Builder.Alignment.fromByteUnits(decl.getAlignment(mod)); |
| 4428 | 4351 | llvm_global.setAlignment(decl.getAlignment(mod)); |
| 4429 | 4352 | if (mod.intern_pool.stringToSliceUnwrap(decl.@"linksection")) |section| { |
| 4430 | | global.ptr(&o.builder).section = try o.builder.string(section); |
| 4353 | variable.ptr(&o.builder).section = try o.builder.string(section); |
| 4431 | 4354 | llvm_global.setSection(section); |
| 4432 | 4355 | } |
| 4433 | 4356 | assert(decl.has_tv); |
| ... | ... | @@ -4439,10 +4362,7 @@ pub const DeclGen = struct { |
| 4439 | 4362 | if (init_val != .none) { |
| 4440 | 4363 | const llvm_init = try o.lowerValue(init_val); |
| 4441 | 4364 | const llvm_init_ty = llvm_init.typeOf(&o.builder); |
| 4442 | | global.ptr(&o.builder).type = llvm_init_ty; |
| 4443 | | variable.ptr(&o.builder).mutability = .global; |
| 4444 | | variable.ptr(&o.builder).init = llvm_init; |
| 4445 | | if (llvm_global.globalGetValueType() == llvm_init.typeOf(&o.builder).toLlvm(&o.builder)) { |
| 4365 | if (global.ptrConst(&o.builder).type == llvm_init_ty) { |
| 4446 | 4366 | llvm_global.setInitializer(llvm_init.toLlvm(&o.builder)); |
| 4447 | 4367 | } else { |
| 4448 | 4368 | // LLVM does not allow us to change the type of globals. So we must |
| ... | ... | @@ -4477,7 +4397,10 @@ pub const DeclGen = struct { |
| 4477 | 4397 | new_global; |
| 4478 | 4398 | llvm_global.deleteGlobal(); |
| 4479 | 4399 | llvm_global = new_global; |
| 4400 | variable.ptr(&o.builder).mutability = .global; |
| 4401 | global.ptr(&o.builder).type = llvm_init_ty; |
| 4480 | 4402 | } |
| 4403 | variable.ptr(&o.builder).init = llvm_init; |
| 4481 | 4404 | } |
| 4482 | 4405 | |
| 4483 | 4406 | if (o.di_builder) |dib| { |
| ... | ... | @@ -4508,7 +4431,6 @@ pub const FuncGen = struct { |
| 4508 | 4431 | air: Air, |
| 4509 | 4432 | liveness: Liveness, |
| 4510 | 4433 | wip: Builder.WipFunction, |
| 4511 | | context: *llvm.Context, |
| 4512 | 4434 | builder: *llvm.Builder, |
| 4513 | 4435 | di_scope: ?*llvm.DIScope, |
| 4514 | 4436 | di_file: ?*llvm.DIFile, |
| ... | ... | @@ -4525,26 +4447,24 @@ pub const FuncGen = struct { |
| 4525 | 4447 | |
| 4526 | 4448 | /// This stores the LLVM values used in a function, such that they can be referred to |
| 4527 | 4449 | /// in other instructions. This table is cleared before every function is generated. |
| 4528 | | func_inst_table: std.AutoHashMapUnmanaged(Air.Inst.Ref, *llvm.Value), |
| 4450 | func_inst_table: std.AutoHashMapUnmanaged(Air.Inst.Ref, Builder.Value), |
| 4529 | 4451 | |
| 4530 | 4452 | /// If the return type is sret, this is the result pointer. Otherwise null. |
| 4531 | 4453 | /// Note that this can disagree with isByRef for the return type in the case |
| 4532 | 4454 | /// of C ABI functions. |
| 4533 | | ret_ptr: ?*llvm.Value, |
| 4455 | ret_ptr: Builder.Value, |
| 4534 | 4456 | /// Any function that needs to perform Valgrind client requests needs an array alloca |
| 4535 | 4457 | /// instruction, however a maximum of one per function is needed. |
| 4536 | | valgrind_client_request_array: ?*llvm.Value = null, |
| 4458 | valgrind_client_request_array: Builder.Value = .none, |
| 4537 | 4459 | /// These fields are used to refer to the LLVM value of the function parameters |
| 4538 | 4460 | /// in an Arg instruction. |
| 4539 | 4461 | /// This list may be shorter than the list according to the zig type system; |
| 4540 | 4462 | /// it omits 0-bit types. If the function uses sret as the first parameter, |
| 4541 | 4463 | /// this slice does not include it. |
| 4542 | | args: []const *llvm.Value, |
| 4543 | | arg_index: c_uint, |
| 4464 | args: []const Builder.Value, |
| 4465 | arg_index: usize, |
| 4544 | 4466 | |
| 4545 | | llvm_func: *llvm.Value, |
| 4546 | | |
| 4547 | | err_ret_trace: ?*llvm.Value = null, |
| 4467 | err_ret_trace: Builder.Value = .none, |
| 4548 | 4468 | |
| 4549 | 4469 | /// This data structure is used to implement breaking to blocks. |
| 4550 | 4470 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, struct { |
| ... | ... | @@ -4552,13 +4472,16 @@ pub const FuncGen = struct { |
| 4552 | 4472 | breaks: *BreakList, |
| 4553 | 4473 | }), |
| 4554 | 4474 | |
| 4555 | | single_threaded: bool, |
| 4475 | sync_scope: Builder.SyncScope, |
| 4556 | 4476 | |
| 4557 | 4477 | const DbgState = struct { loc: *llvm.DILocation, scope: *llvm.DIScope, base_line: u32 }; |
| 4558 | | const BreakList = std.MultiArrayList(struct { |
| 4559 | | bb: *llvm.BasicBlock, |
| 4560 | | val: *llvm.Value, |
| 4561 | | }); |
| 4478 | const BreakList = union { |
| 4479 | list: std.MultiArrayList(struct { |
| 4480 | bb: Builder.Function.Block.Index, |
| 4481 | val: Builder.Value, |
| 4482 | }), |
| 4483 | len: usize, |
| 4484 | }; |
| 4562 | 4485 | |
| 4563 | 4486 | fn deinit(self: *FuncGen) void { |
| 4564 | 4487 | self.wip.deinit(); |
| ... | ... | @@ -4573,7 +4496,7 @@ pub const FuncGen = struct { |
| 4573 | 4496 | return self.dg.todo(format, args); |
| 4574 | 4497 | } |
| 4575 | 4498 | |
| 4576 | | fn resolveInst(self: *FuncGen, inst: Air.Inst.Ref) !*llvm.Value { |
| 4499 | fn resolveInst(self: *FuncGen, inst: Air.Inst.Ref) !Builder.Value { |
| 4577 | 4500 | const gpa = self.gpa; |
| 4578 | 4501 | const gop = try self.func_inst_table.getOrPut(gpa, inst); |
| 4579 | 4502 | if (gop.found_existing) return gop.value_ptr.*; |
| ... | ... | @@ -4584,8 +4507,8 @@ pub const FuncGen = struct { |
| 4584 | 4507 | .ty = self.typeOf(inst), |
| 4585 | 4508 | .val = (try self.air.value(inst, mod)).?, |
| 4586 | 4509 | }); |
| 4587 | | gop.value_ptr.* = llvm_val.toLlvm(&o.builder); |
| 4588 | | return gop.value_ptr.*; |
| 4510 | gop.value_ptr.* = llvm_val.toValue(); |
| 4511 | return llvm_val.toValue(); |
| 4589 | 4512 | } |
| 4590 | 4513 | |
| 4591 | 4514 | fn resolveValue(self: *FuncGen, tv: TypedValue) Error!Builder.Constant { |
| ... | ... | @@ -4613,19 +4536,19 @@ pub const FuncGen = struct { |
| 4613 | 4536 | .unnamed_addr = .unnamed_addr, |
| 4614 | 4537 | .addr_space = llvm_actual_addrspace, |
| 4615 | 4538 | .type = llvm_ty, |
| 4616 | | .alignment = Builder.Alignment.fromByteUnits(llvm_alignment), |
| 4617 | 4539 | .kind = .{ .variable = @enumFromInt(o.builder.variables.items.len) }, |
| 4618 | 4540 | }; |
| 4619 | 4541 | var variable = Builder.Variable{ |
| 4620 | 4542 | .global = @enumFromInt(o.builder.globals.count()), |
| 4621 | 4543 | .mutability = .constant, |
| 4622 | 4544 | .init = llvm_val, |
| 4545 | .alignment = Builder.Alignment.fromByteUnits(llvm_alignment), |
| 4623 | 4546 | }; |
| 4624 | 4547 | try o.builder.llvm.globals.append(o.gpa, llvm_global); |
| 4625 | 4548 | const global_index = try o.builder.addGlobal(.empty, global); |
| 4626 | 4549 | try o.builder.variables.append(o.gpa, variable); |
| 4627 | 4550 | |
| 4628 | | return try o.builder.convConst( |
| 4551 | return o.builder.convConst( |
| 4629 | 4552 | .unneeded, |
| 4630 | 4553 | global_index.toConst(), |
| 4631 | 4554 | try o.builder.ptrType(llvm_wanted_addrspace), |
| ... | ... | @@ -4651,10 +4574,9 @@ pub const FuncGen = struct { |
| 4651 | 4574 | const ip = &mod.intern_pool; |
| 4652 | 4575 | const air_tags = self.air.instructions.items(.tag); |
| 4653 | 4576 | for (body, 0..) |inst, i| { |
| 4654 | | if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) |
| 4655 | | continue; |
| 4577 | if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) continue; |
| 4656 | 4578 | |
| 4657 | | const opt_value: ?*llvm.Value = switch (air_tags[inst]) { |
| 4579 | const val: Builder.Value = switch (air_tags[inst]) { |
| 4658 | 4580 | // zig fmt: off |
| 4659 | 4581 | .add => try self.airAdd(inst, false), |
| 4660 | 4582 | .add_optimized => try self.airAdd(inst, true), |
| ... | ... | @@ -4745,15 +4667,15 @@ pub const FuncGen = struct { |
| 4745 | 4667 | .cmp_vector_optimized => try self.airCmpVector(inst, true), |
| 4746 | 4668 | .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), |
| 4747 | 4669 | |
| 4748 | | .is_non_null => try self.airIsNonNull(inst, false, .NE), |
| 4749 | | .is_non_null_ptr => try self.airIsNonNull(inst, true , .NE), |
| 4750 | | .is_null => try self.airIsNonNull(inst, false, .EQ), |
| 4751 | | .is_null_ptr => try self.airIsNonNull(inst, true , .EQ), |
| 4670 | .is_non_null => try self.airIsNonNull(inst, false, .ne), |
| 4671 | .is_non_null_ptr => try self.airIsNonNull(inst, true , .ne), |
| 4672 | .is_null => try self.airIsNonNull(inst, false, .eq), |
| 4673 | .is_null_ptr => try self.airIsNonNull(inst, true , .eq), |
| 4752 | 4674 | |
| 4753 | | .is_non_err => try self.airIsErr(inst, .EQ, false), |
| 4754 | | .is_non_err_ptr => try self.airIsErr(inst, .EQ, true), |
| 4755 | | .is_err => try self.airIsErr(inst, .NE, false), |
| 4756 | | .is_err_ptr => try self.airIsErr(inst, .NE, true), |
| 4675 | .is_non_err => try self.airIsErr(inst, .eq, false), |
| 4676 | .is_non_err_ptr => try self.airIsErr(inst, .eq, true), |
| 4677 | .is_err => try self.airIsErr(inst, .ne, false), |
| 4678 | .is_err_ptr => try self.airIsErr(inst, .ne, true), |
| 4757 | 4679 | |
| 4758 | 4680 | .alloc => try self.airAlloc(inst), |
| 4759 | 4681 | .ret_ptr => try self.airRetPtr(inst), |
| ... | ... | @@ -4830,10 +4752,10 @@ pub const FuncGen = struct { |
| 4830 | 4752 | .reduce => try self.airReduce(inst, false), |
| 4831 | 4753 | .reduce_optimized => try self.airReduce(inst, true), |
| 4832 | 4754 | |
| 4833 | | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 4834 | | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| 4835 | | .atomic_store_release => try self.airAtomicStore(inst, .Release), |
| 4836 | | .atomic_store_seq_cst => try self.airAtomicStore(inst, .SequentiallyConsistent), |
| 4755 | .atomic_store_unordered => try self.airAtomicStore(inst, .unordered), |
| 4756 | .atomic_store_monotonic => try self.airAtomicStore(inst, .monotonic), |
| 4757 | .atomic_store_release => try self.airAtomicStore(inst, .release), |
| 4758 | .atomic_store_seq_cst => try self.airAtomicStore(inst, .seq_cst), |
| 4837 | 4759 | |
| 4838 | 4760 | .struct_field_ptr => try self.airStructFieldPtr(inst), |
| 4839 | 4761 | .struct_field_val => try self.airStructFieldVal(body[i..]), |
| ... | ... | @@ -4875,8 +4797,8 @@ pub const FuncGen = struct { |
| 4875 | 4797 | |
| 4876 | 4798 | .inferred_alloc, .inferred_alloc_comptime => unreachable, |
| 4877 | 4799 | |
| 4878 | | .unreach => self.airUnreach(inst), |
| 4879 | | .dbg_stmt => self.airDbgStmt(inst), |
| 4800 | .unreach => try self.airUnreach(inst), |
| 4801 | .dbg_stmt => try self.airDbgStmt(inst), |
| 4880 | 4802 | .dbg_inline_begin => try self.airDbgInlineBegin(inst), |
| 4881 | 4803 | .dbg_inline_end => try self.airDbgInlineEnd(inst), |
| 4882 | 4804 | .dbg_block_begin => try self.airDbgBlockBegin(), |
| ... | ... | @@ -4894,14 +4816,11 @@ pub const FuncGen = struct { |
| 4894 | 4816 | .work_group_id => try self.airWorkGroupId(inst), |
| 4895 | 4817 | // zig fmt: on |
| 4896 | 4818 | }; |
| 4897 | | if (opt_value) |val| { |
| 4898 | | const ref = Air.indexToRef(inst); |
| 4899 | | try self.func_inst_table.putNoClobber(self.gpa, ref, val); |
| 4900 | | } |
| 4819 | if (val != .none) try self.func_inst_table.putNoClobber(self.gpa, Air.indexToRef(inst), val); |
| 4901 | 4820 | } |
| 4902 | 4821 | } |
| 4903 | 4822 | |
| 4904 | | fn airCall(self: *FuncGen, inst: Air.Inst.Index, attr: llvm.CallAttr) !?*llvm.Value { |
| 4823 | fn airCall(self: *FuncGen, inst: Air.Inst.Index, attr: llvm.CallAttr) !Builder.Value { |
| 4905 | 4824 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 4906 | 4825 | const extra = self.air.extraData(Air.Call, pl_op.payload); |
| 4907 | 4826 | const args: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]); |
| ... | ... | @@ -4924,16 +4843,18 @@ pub const FuncGen = struct { |
| 4924 | 4843 | defer llvm_args.deinit(); |
| 4925 | 4844 | |
| 4926 | 4845 | const ret_ptr = if (!sret) null else blk: { |
| 4927 | | const llvm_ret_ty = (try o.lowerType(return_type)).toLlvm(&o.builder); |
| 4928 | | const ret_ptr = try self.buildAlloca(llvm_ret_ty, return_type.abiAlignment(mod)); |
| 4929 | | try llvm_args.append(ret_ptr); |
| 4846 | const llvm_ret_ty = try o.lowerType(return_type); |
| 4847 | const alignment = Builder.Alignment.fromByteUnits(return_type.abiAlignment(mod)); |
| 4848 | const ret_ptr = try self.buildAlloca(llvm_ret_ty, alignment); |
| 4849 | try llvm_args.append(ret_ptr.toLlvm(&self.wip)); |
| 4930 | 4850 | break :blk ret_ptr; |
| 4931 | 4851 | }; |
| 4932 | 4852 | |
| 4933 | 4853 | const err_return_tracing = return_type.isError(mod) and |
| 4934 | 4854 | o.module.comp.bin_file.options.error_return_tracing; |
| 4935 | 4855 | if (err_return_tracing) { |
| 4936 | | try llvm_args.append(self.err_ret_trace.?); |
| 4856 | assert(self.err_ret_trace != .none); |
| 4857 | try llvm_args.append(self.err_ret_trace.toLlvm(&self.wip)); |
| 4937 | 4858 | } |
| 4938 | 4859 | |
| 4939 | 4860 | var it = iterateParamTypes(o, fn_info); |
| ... | ... | @@ -4943,14 +4864,13 @@ pub const FuncGen = struct { |
| 4943 | 4864 | const arg = args[it.zig_index - 1]; |
| 4944 | 4865 | const param_ty = self.typeOf(arg); |
| 4945 | 4866 | const llvm_arg = try self.resolveInst(arg); |
| 4946 | | const llvm_param_ty = (try o.lowerType(param_ty)).toLlvm(&o.builder); |
| 4867 | const llvm_param_ty = try o.lowerType(param_ty); |
| 4947 | 4868 | if (isByRef(param_ty, mod)) { |
| 4948 | | const alignment = param_ty.abiAlignment(mod); |
| 4949 | | const load_inst = self.builder.buildLoad(llvm_param_ty, llvm_arg, ""); |
| 4950 | | load_inst.setAlignment(alignment); |
| 4951 | | try llvm_args.append(load_inst); |
| 4869 | const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); |
| 4870 | const loaded = try self.wip.load(.normal, llvm_param_ty, llvm_arg, alignment, ""); |
| 4871 | try llvm_args.append(loaded.toLlvm(&self.wip)); |
| 4952 | 4872 | } else { |
| 4953 | | try llvm_args.append(llvm_arg); |
| 4873 | try llvm_args.append(llvm_arg.toLlvm(&self.wip)); |
| 4954 | 4874 | } |
| 4955 | 4875 | }, |
| 4956 | 4876 | .byref => { |
| ... | ... | @@ -4958,14 +4878,13 @@ pub const FuncGen = struct { |
| 4958 | 4878 | const param_ty = self.typeOf(arg); |
| 4959 | 4879 | const llvm_arg = try self.resolveInst(arg); |
| 4960 | 4880 | if (isByRef(param_ty, mod)) { |
| 4961 | | try llvm_args.append(llvm_arg); |
| 4881 | try llvm_args.append(llvm_arg.toLlvm(&self.wip)); |
| 4962 | 4882 | } else { |
| 4963 | | const alignment = param_ty.abiAlignment(mod); |
| 4964 | | const param_llvm_ty = llvm_arg.typeOf(); |
| 4883 | const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); |
| 4884 | const param_llvm_ty = llvm_arg.typeOfWip(&self.wip); |
| 4965 | 4885 | const arg_ptr = try self.buildAlloca(param_llvm_ty, alignment); |
| 4966 | | const store_inst = self.builder.buildStore(llvm_arg, arg_ptr); |
| 4967 | | store_inst.setAlignment(alignment); |
| 4968 | | try llvm_args.append(arg_ptr); |
| 4886 | _ = try self.wip.store(.normal, llvm_arg, arg_ptr, alignment); |
| 4887 | try llvm_args.append(arg_ptr.toLlvm(&self.wip)); |
| 4969 | 4888 | } |
| 4970 | 4889 | }, |
| 4971 | 4890 | .byref_mut => { |
| ... | ... | @@ -4973,56 +4892,46 @@ pub const FuncGen = struct { |
| 4973 | 4892 | const param_ty = self.typeOf(arg); |
| 4974 | 4893 | const llvm_arg = try self.resolveInst(arg); |
| 4975 | 4894 | |
| 4976 | | const alignment = param_ty.abiAlignment(mod); |
| 4977 | | const param_llvm_ty = (try o.lowerType(param_ty)).toLlvm(&o.builder); |
| 4895 | const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); |
| 4896 | const param_llvm_ty = try o.lowerType(param_ty); |
| 4978 | 4897 | const arg_ptr = try self.buildAlloca(param_llvm_ty, alignment); |
| 4979 | 4898 | if (isByRef(param_ty, mod)) { |
| 4980 | | const load_inst = self.builder.buildLoad(param_llvm_ty, llvm_arg, ""); |
| 4981 | | load_inst.setAlignment(alignment); |
| 4982 | | |
| 4983 | | const store_inst = self.builder.buildStore(load_inst, arg_ptr); |
| 4984 | | store_inst.setAlignment(alignment); |
| 4985 | | try llvm_args.append(arg_ptr); |
| 4899 | const loaded = try self.wip.load(.normal, param_llvm_ty, llvm_arg, alignment, ""); |
| 4900 | _ = try self.wip.store(.normal, loaded, arg_ptr, alignment); |
| 4986 | 4901 | } else { |
| 4987 | | const store_inst = self.builder.buildStore(llvm_arg, arg_ptr); |
| 4988 | | store_inst.setAlignment(alignment); |
| 4989 | | try llvm_args.append(arg_ptr); |
| 4902 | _ = try self.wip.store(.normal, llvm_arg, arg_ptr, alignment); |
| 4990 | 4903 | } |
| 4904 | try llvm_args.append(arg_ptr.toLlvm(&self.wip)); |
| 4991 | 4905 | }, |
| 4992 | 4906 | .abi_sized_int => { |
| 4993 | 4907 | const arg = args[it.zig_index - 1]; |
| 4994 | 4908 | const param_ty = self.typeOf(arg); |
| 4995 | 4909 | const llvm_arg = try self.resolveInst(arg); |
| 4996 | | const int_llvm_ty = (try o.builder.intType(@intCast(param_ty.abiSize(mod) * 8))).toLlvm(&o.builder); |
| 4910 | const int_llvm_ty = try o.builder.intType(@intCast(param_ty.abiSize(mod) * 8)); |
| 4997 | 4911 | |
| 4998 | 4912 | if (isByRef(param_ty, mod)) { |
| 4999 | | const alignment = param_ty.abiAlignment(mod); |
| 5000 | | const load_inst = self.builder.buildLoad(int_llvm_ty, llvm_arg, ""); |
| 5001 | | load_inst.setAlignment(alignment); |
| 5002 | | try llvm_args.append(load_inst); |
| 4913 | const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); |
| 4914 | const loaded = try self.wip.load(.normal, int_llvm_ty, llvm_arg, alignment, ""); |
| 4915 | try llvm_args.append(loaded.toLlvm(&self.wip)); |
| 5003 | 4916 | } else { |
| 5004 | 4917 | // LLVM does not allow bitcasting structs so we must allocate |
| 5005 | 4918 | // a local, store as one type, and then load as another type. |
| 5006 | | const alignment = @max( |
| 4919 | const alignment = Builder.Alignment.fromByteUnits(@max( |
| 5007 | 4920 | param_ty.abiAlignment(mod), |
| 5008 | | o.target_data.abiAlignmentOfType(int_llvm_ty), |
| 5009 | | ); |
| 4921 | o.target_data.abiAlignmentOfType(int_llvm_ty.toLlvm(&o.builder)), |
| 4922 | )); |
| 5010 | 4923 | const int_ptr = try self.buildAlloca(int_llvm_ty, alignment); |
| 5011 | | const store_inst = self.builder.buildStore(llvm_arg, int_ptr); |
| 5012 | | store_inst.setAlignment(alignment); |
| 5013 | | const load_inst = self.builder.buildLoad(int_llvm_ty, int_ptr, ""); |
| 5014 | | load_inst.setAlignment(alignment); |
| 5015 | | try llvm_args.append(load_inst); |
| 4924 | _ = try self.wip.store(.normal, llvm_arg, int_ptr, alignment); |
| 4925 | const loaded = try self.wip.load(.normal, int_llvm_ty, int_ptr, alignment, ""); |
| 4926 | try llvm_args.append(loaded.toLlvm(&self.wip)); |
| 5016 | 4927 | } |
| 5017 | 4928 | }, |
| 5018 | 4929 | .slice => { |
| 5019 | 4930 | const arg = args[it.zig_index - 1]; |
| 5020 | 4931 | const llvm_arg = try self.resolveInst(arg); |
| 5021 | | const ptr = self.builder.buildExtractValue(llvm_arg, 0, ""); |
| 5022 | | const len = self.builder.buildExtractValue(llvm_arg, 1, ""); |
| 5023 | | try llvm_args.ensureUnusedCapacity(2); |
| 5024 | | llvm_args.appendAssumeCapacity(ptr); |
| 5025 | | llvm_args.appendAssumeCapacity(len); |
| 4932 | const ptr = try self.wip.extractValue(llvm_arg, &.{0}, ""); |
| 4933 | const len = try self.wip.extractValue(llvm_arg, &.{1}, ""); |
| 4934 | try llvm_args.appendSlice(&.{ ptr.toLlvm(&self.wip), len.toLlvm(&self.wip) }); |
| 5026 | 4935 | }, |
| 5027 | 4936 | .multiple_llvm_types => { |
| 5028 | 4937 | const arg = args[it.zig_index - 1]; |
| ... | ... | @@ -5030,75 +4939,77 @@ pub const FuncGen = struct { |
| 5030 | 4939 | const llvm_types = it.types_buffer[0..it.types_len]; |
| 5031 | 4940 | const llvm_arg = try self.resolveInst(arg); |
| 5032 | 4941 | const is_by_ref = isByRef(param_ty, mod); |
| 5033 | | const arg_ptr = if (is_by_ref) llvm_arg else p: { |
| 5034 | | const p = try self.buildAlloca(llvm_arg.typeOf(), null); |
| 5035 | | const store_inst = self.builder.buildStore(llvm_arg, p); |
| 5036 | | store_inst.setAlignment(param_ty.abiAlignment(mod)); |
| 5037 | | break :p p; |
| 4942 | const arg_ptr = if (is_by_ref) llvm_arg else ptr: { |
| 4943 | const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); |
| 4944 | const ptr = try self.buildAlloca(llvm_arg.typeOfWip(&self.wip), alignment); |
| 4945 | _ = try self.wip.store(.normal, llvm_arg, ptr, alignment); |
| 4946 | break :ptr ptr; |
| 5038 | 4947 | }; |
| 5039 | 4948 | |
| 5040 | | const llvm_ty = (try o.builder.structType(.normal, llvm_types)).toLlvm(&o.builder); |
| 4949 | const llvm_ty = try o.builder.structType(.normal, llvm_types); |
| 5041 | 4950 | try llvm_args.ensureUnusedCapacity(it.types_len); |
| 5042 | 4951 | for (llvm_types, 0..) |field_ty, i| { |
| 5043 | | const field_ptr = self.builder.buildStructGEP(llvm_ty, arg_ptr, @intCast(i), ""); |
| 5044 | | const load_inst = self.builder.buildLoad(field_ty.toLlvm(&o.builder), field_ptr, ""); |
| 5045 | | load_inst.setAlignment(target.ptrBitWidth() / 8); |
| 5046 | | llvm_args.appendAssumeCapacity(load_inst); |
| 4952 | const alignment = |
| 4953 | Builder.Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)); |
| 4954 | const field_ptr = try self.wip.gepStruct(llvm_ty, arg_ptr, i, ""); |
| 4955 | const loaded = try self.wip.load(.normal, field_ty, field_ptr, alignment, ""); |
| 4956 | llvm_args.appendAssumeCapacity(loaded.toLlvm(&self.wip)); |
| 5047 | 4957 | } |
| 5048 | 4958 | }, |
| 5049 | 4959 | .as_u16 => { |
| 5050 | 4960 | const arg = args[it.zig_index - 1]; |
| 5051 | 4961 | const llvm_arg = try self.resolveInst(arg); |
| 5052 | | const casted = self.builder.buildBitCast(llvm_arg, Builder.Type.i16.toLlvm(&o.builder), ""); |
| 5053 | | try llvm_args.append(casted); |
| 4962 | const casted = try self.wip.cast(.bitcast, llvm_arg, .i16, ""); |
| 4963 | try llvm_args.append(casted.toLlvm(&self.wip)); |
| 5054 | 4964 | }, |
| 5055 | 4965 | .float_array => |count| { |
| 5056 | 4966 | const arg = args[it.zig_index - 1]; |
| 5057 | 4967 | const arg_ty = self.typeOf(arg); |
| 5058 | 4968 | var llvm_arg = try self.resolveInst(arg); |
| 4969 | const alignment = Builder.Alignment.fromByteUnits(arg_ty.abiAlignment(mod)); |
| 5059 | 4970 | if (!isByRef(arg_ty, mod)) { |
| 5060 | | const p = try self.buildAlloca(llvm_arg.typeOf(), null); |
| 5061 | | const store_inst = self.builder.buildStore(llvm_arg, p); |
| 5062 | | store_inst.setAlignment(arg_ty.abiAlignment(mod)); |
| 5063 | | llvm_arg = store_inst; |
| 4971 | const ptr = try self.buildAlloca(llvm_arg.typeOfWip(&self.wip), alignment); |
| 4972 | _ = try self.wip.store(.normal, llvm_arg, ptr, alignment); |
| 4973 | llvm_arg = ptr; |
| 5064 | 4974 | } |
| 5065 | 4975 | |
| 5066 | 4976 | const float_ty = try o.lowerType(aarch64_c_abi.getFloatArrayType(arg_ty, mod).?); |
| 5067 | 4977 | const array_ty = try o.builder.arrayType(count, float_ty); |
| 5068 | 4978 | |
| 5069 | | const alignment = arg_ty.abiAlignment(mod); |
| 5070 | | const load_inst = self.builder.buildLoad(array_ty.toLlvm(&o.builder), llvm_arg, ""); |
| 5071 | | load_inst.setAlignment(alignment); |
| 5072 | | try llvm_args.append(load_inst); |
| 4979 | const loaded = try self.wip.load(.normal, array_ty, llvm_arg, alignment, ""); |
| 4980 | try llvm_args.append(loaded.toLlvm(&self.wip)); |
| 5073 | 4981 | }, |
| 5074 | 4982 | .i32_array, .i64_array => |arr_len| { |
| 5075 | 4983 | const elem_size: u8 = if (lowering == .i32_array) 32 else 64; |
| 5076 | 4984 | const arg = args[it.zig_index - 1]; |
| 5077 | 4985 | const arg_ty = self.typeOf(arg); |
| 5078 | 4986 | var llvm_arg = try self.resolveInst(arg); |
| 4987 | const alignment = Builder.Alignment.fromByteUnits(arg_ty.abiAlignment(mod)); |
| 5079 | 4988 | if (!isByRef(arg_ty, mod)) { |
| 5080 | | const p = try self.buildAlloca(llvm_arg.typeOf(), null); |
| 5081 | | const store_inst = self.builder.buildStore(llvm_arg, p); |
| 5082 | | store_inst.setAlignment(arg_ty.abiAlignment(mod)); |
| 5083 | | llvm_arg = store_inst; |
| 4989 | const ptr = try self.buildAlloca(llvm_arg.typeOfWip(&self.wip), alignment); |
| 4990 | _ = try self.wip.store(.normal, llvm_arg, ptr, alignment); |
| 4991 | llvm_arg = ptr; |
| 5084 | 4992 | } |
| 5085 | 4993 | |
| 5086 | | const array_ty = try o.builder.arrayType(arr_len, try o.builder.intType(@intCast(elem_size))); |
| 5087 | | const alignment = arg_ty.abiAlignment(mod); |
| 5088 | | const load_inst = self.builder.buildLoad(array_ty.toLlvm(&o.builder), llvm_arg, ""); |
| 5089 | | load_inst.setAlignment(alignment); |
| 5090 | | try llvm_args.append(load_inst); |
| 4994 | const array_ty = |
| 4995 | try o.builder.arrayType(arr_len, try o.builder.intType(@intCast(elem_size))); |
| 4996 | const loaded = try self.wip.load(.normal, array_ty, llvm_arg, alignment, ""); |
| 4997 | try llvm_args.append(loaded.toLlvm(&self.wip)); |
| 5091 | 4998 | }, |
| 5092 | 4999 | }; |
| 5093 | 5000 | |
| 5094 | | const call = self.builder.buildCall( |
| 5095 | | (try o.lowerType(zig_fn_ty)).toLlvm(&o.builder), |
| 5096 | | llvm_fn, |
| 5097 | | llvm_args.items.ptr, |
| 5098 | | @intCast(llvm_args.items.len), |
| 5099 | | toLlvmCallConv(fn_info.cc, target), |
| 5100 | | attr, |
| 5101 | | "", |
| 5001 | const llvm_fn_ty = try o.lowerType(zig_fn_ty); |
| 5002 | const call = (try self.wip.unimplemented(llvm_fn_ty.functionReturn(&o.builder), "")).finish( |
| 5003 | self.builder.buildCall( |
| 5004 | llvm_fn_ty.toLlvm(&o.builder), |
| 5005 | llvm_fn.toLlvm(&self.wip), |
| 5006 | llvm_args.items.ptr, |
| 5007 | @intCast(llvm_args.items.len), |
| 5008 | toLlvmCallConv(fn_info.cc, target), |
| 5009 | attr, |
| 5010 | "", |
| 5011 | ), |
| 5012 | &self.wip, |
| 5102 | 5013 | ); |
| 5103 | 5014 | |
| 5104 | 5015 | if (callee_ty.zigTypeTag(mod) == .Pointer) { |
| ... | ... | @@ -5111,7 +5022,7 @@ pub const FuncGen = struct { |
| 5111 | 5022 | const param_index = it.zig_index - 1; |
| 5112 | 5023 | const param_ty = fn_info.param_types.get(ip)[param_index].toType(); |
| 5113 | 5024 | if (!isByRef(param_ty, mod)) { |
| 5114 | | o.addByValParamAttrs(call, param_ty, param_index, fn_info, it.llvm_index - 1); |
| 5025 | o.addByValParamAttrs(call.toLlvm(&self.wip), param_ty, param_index, fn_info, it.llvm_index - 1); |
| 5115 | 5026 | } |
| 5116 | 5027 | }, |
| 5117 | 5028 | .byref => { |
| ... | ... | @@ -5119,10 +5030,10 @@ pub const FuncGen = struct { |
| 5119 | 5030 | const param_ty = fn_info.param_types.get(ip)[param_index].toType(); |
| 5120 | 5031 | const param_llvm_ty = try o.lowerType(param_ty); |
| 5121 | 5032 | const alignment = param_ty.abiAlignment(mod); |
| 5122 | | o.addByRefParamAttrs(call, it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty); |
| 5033 | o.addByRefParamAttrs(call.toLlvm(&self.wip), it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty); |
| 5123 | 5034 | }, |
| 5124 | 5035 | .byref_mut => { |
| 5125 | | o.addArgAttr(call, it.llvm_index - 1, "noundef"); |
| 5036 | o.addArgAttr(call.toLlvm(&self.wip), it.llvm_index - 1, "noundef"); |
| 5126 | 5037 | }, |
| 5127 | 5038 | // No attributes needed for these. |
| 5128 | 5039 | .no_bits, |
| ... | ... | @@ -5142,70 +5053,63 @@ pub const FuncGen = struct { |
| 5142 | 5053 | |
| 5143 | 5054 | if (math.cast(u5, it.zig_index - 1)) |i| { |
| 5144 | 5055 | if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) { |
| 5145 | | o.addArgAttr(call, llvm_arg_i, "noalias"); |
| 5056 | o.addArgAttr(call.toLlvm(&self.wip), llvm_arg_i, "noalias"); |
| 5146 | 5057 | } |
| 5147 | 5058 | } |
| 5148 | 5059 | if (param_ty.zigTypeTag(mod) != .Optional) { |
| 5149 | | o.addArgAttr(call, llvm_arg_i, "nonnull"); |
| 5060 | o.addArgAttr(call.toLlvm(&self.wip), llvm_arg_i, "nonnull"); |
| 5150 | 5061 | } |
| 5151 | 5062 | if (ptr_info.flags.is_const) { |
| 5152 | | o.addArgAttr(call, llvm_arg_i, "readonly"); |
| 5063 | o.addArgAttr(call.toLlvm(&self.wip), llvm_arg_i, "readonly"); |
| 5153 | 5064 | } |
| 5154 | 5065 | const elem_align = ptr_info.flags.alignment.toByteUnitsOptional() orelse |
| 5155 | 5066 | @max(ptr_info.child.toType().abiAlignment(mod), 1); |
| 5156 | | o.addArgAttrInt(call, llvm_arg_i, "align", elem_align); |
| 5067 | o.addArgAttrInt(call.toLlvm(&self.wip), llvm_arg_i, "align", elem_align); |
| 5157 | 5068 | }, |
| 5158 | 5069 | }; |
| 5159 | 5070 | } |
| 5160 | 5071 | |
| 5161 | 5072 | if (fn_info.return_type == .noreturn_type and attr != .AlwaysTail) { |
| 5162 | | return null; |
| 5073 | return .none; |
| 5163 | 5074 | } |
| 5164 | 5075 | |
| 5165 | 5076 | if (self.liveness.isUnused(inst) or !return_type.hasRuntimeBitsIgnoreComptime(mod)) { |
| 5166 | | return null; |
| 5077 | return .none; |
| 5167 | 5078 | } |
| 5168 | 5079 | |
| 5169 | | const llvm_ret_ty = (try o.lowerType(return_type)).toLlvm(&o.builder); |
| 5080 | const llvm_ret_ty = try o.lowerType(return_type); |
| 5170 | 5081 | |
| 5171 | 5082 | if (ret_ptr) |rp| { |
| 5172 | | call.setCallSret(llvm_ret_ty); |
| 5083 | call.toLlvm(&self.wip).setCallSret(llvm_ret_ty.toLlvm(&o.builder)); |
| 5173 | 5084 | if (isByRef(return_type, mod)) { |
| 5174 | 5085 | return rp; |
| 5175 | 5086 | } else { |
| 5176 | 5087 | // our by-ref status disagrees with sret so we must load. |
| 5177 | | const loaded = self.builder.buildLoad(llvm_ret_ty, rp, ""); |
| 5178 | | loaded.setAlignment(return_type.abiAlignment(mod)); |
| 5179 | | return loaded; |
| 5088 | const return_alignment = Builder.Alignment.fromByteUnits(return_type.abiAlignment(mod)); |
| 5089 | return self.wip.load(.normal, llvm_ret_ty, rp, return_alignment, ""); |
| 5180 | 5090 | } |
| 5181 | 5091 | } |
| 5182 | 5092 | |
| 5183 | | const abi_ret_ty = (try lowerFnRetTy(o, fn_info)).toLlvm(&o.builder); |
| 5093 | const abi_ret_ty = try lowerFnRetTy(o, fn_info); |
| 5184 | 5094 | |
| 5185 | 5095 | if (abi_ret_ty != llvm_ret_ty) { |
| 5186 | 5096 | // In this case the function return type is honoring the calling convention by having |
| 5187 | 5097 | // a different LLVM type than the usual one. We solve this here at the callsite |
| 5188 | 5098 | // by using our canonical type, then loading it if necessary. |
| 5189 | | const alignment = o.target_data.abiAlignmentOfType(abi_ret_ty); |
| 5190 | | const rp = try self.buildAlloca(llvm_ret_ty, alignment); |
| 5191 | | const store_inst = self.builder.buildStore(call, rp); |
| 5192 | | store_inst.setAlignment(alignment); |
| 5193 | | if (isByRef(return_type, mod)) { |
| 5194 | | return rp; |
| 5195 | | } else { |
| 5196 | | const load_inst = self.builder.buildLoad(llvm_ret_ty, rp, ""); |
| 5197 | | load_inst.setAlignment(alignment); |
| 5198 | | return load_inst; |
| 5199 | | } |
| 5099 | const rp = try self.buildAlloca(llvm_ret_ty, .default); |
| 5100 | _ = try self.wip.store(.normal, call, rp, .default); |
| 5101 | return if (isByRef(return_type, mod)) |
| 5102 | rp |
| 5103 | else |
| 5104 | try self.wip.load(.normal, llvm_ret_ty, rp, .default, ""); |
| 5200 | 5105 | } |
| 5201 | 5106 | |
| 5202 | 5107 | if (isByRef(return_type, mod)) { |
| 5203 | 5108 | // our by-ref status disagrees with sret so we must allocate, store, |
| 5204 | 5109 | // and return the allocation pointer. |
| 5205 | | const alignment = return_type.abiAlignment(mod); |
| 5110 | const alignment = Builder.Alignment.fromByteUnits(return_type.abiAlignment(mod)); |
| 5206 | 5111 | const rp = try self.buildAlloca(llvm_ret_ty, alignment); |
| 5207 | | const store_inst = self.builder.buildStore(call, rp); |
| 5208 | | store_inst.setAlignment(alignment); |
| 5112 | _ = try self.wip.store(.normal, call, rp, alignment); |
| 5209 | 5113 | return rp; |
| 5210 | 5114 | } else { |
| 5211 | 5115 | return call; |
| ... | ... | @@ -5239,7 +5143,7 @@ pub const FuncGen = struct { |
| 5239 | 5143 | const panic_decl = mod.declPtr(panic_func.owner_decl); |
| 5240 | 5144 | const fn_info = mod.typeToFunc(panic_decl.ty).?; |
| 5241 | 5145 | const panic_global = try o.resolveLlvmFunction(panic_func.owner_decl); |
| 5242 | | _ = fg.builder.buildCall( |
| 5146 | _ = (try fg.wip.unimplemented(.void, "")).finish(fg.builder.buildCall( |
| 5243 | 5147 | (try o.lowerType(panic_decl.ty)).toLlvm(&o.builder), |
| 5244 | 5148 | panic_global.toLlvm(&o.builder), |
| 5245 | 5149 | &args, |
| ... | ... | @@ -5247,21 +5151,21 @@ pub const FuncGen = struct { |
| 5247 | 5151 | toLlvmCallConv(fn_info.cc, target), |
| 5248 | 5152 | .Auto, |
| 5249 | 5153 | "", |
| 5250 | | ); |
| 5251 | | _ = fg.builder.buildUnreachable(); |
| 5154 | ), &fg.wip); |
| 5155 | _ = try fg.wip.@"unreachable"(); |
| 5252 | 5156 | } |
| 5253 | 5157 | |
| 5254 | | fn airRet(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 5158 | fn airRet(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 5255 | 5159 | const o = self.dg.object; |
| 5256 | 5160 | const mod = o.module; |
| 5257 | 5161 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 5258 | 5162 | const ret_ty = self.typeOf(un_op); |
| 5259 | | if (self.ret_ptr) |ret_ptr| { |
| 5163 | if (self.ret_ptr != .none) { |
| 5260 | 5164 | const operand = try self.resolveInst(un_op); |
| 5261 | 5165 | const ptr_ty = try mod.singleMutPtrType(ret_ty); |
| 5262 | | try self.store(ret_ptr, ptr_ty, operand, .NotAtomic); |
| 5263 | | try self.wip.retVoid(); |
| 5264 | | return null; |
| 5166 | try self.store(self.ret_ptr, ptr_ty, operand, .none); |
| 5167 | _ = try self.wip.retVoid(); |
| 5168 | return .none; |
| 5265 | 5169 | } |
| 5266 | 5170 | const fn_info = mod.typeToFunc(self.dg.decl.ty).?; |
| 5267 | 5171 | if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| ... | ... | @@ -5269,43 +5173,37 @@ pub const FuncGen = struct { |
| 5269 | 5173 | // Functions with an empty error set are emitted with an error code |
| 5270 | 5174 | // return type and return zero so they can be function pointers coerced |
| 5271 | 5175 | // to functions that return anyerror. |
| 5272 | | const int = try o.builder.intConst(Builder.Type.err_int, 0); |
| 5273 | | _ = self.builder.buildRet(int.toLlvm(&o.builder)); |
| 5176 | _ = try self.wip.ret(try o.builder.intValue(Builder.Type.err_int, 0)); |
| 5274 | 5177 | } else { |
| 5275 | | try self.wip.retVoid(); |
| 5178 | _ = try self.wip.retVoid(); |
| 5276 | 5179 | } |
| 5277 | | return null; |
| 5180 | return .none; |
| 5278 | 5181 | } |
| 5279 | 5182 | |
| 5280 | | const abi_ret_ty = (try lowerFnRetTy(o, fn_info)).toLlvm(&o.builder); |
| 5183 | const abi_ret_ty = try lowerFnRetTy(o, fn_info); |
| 5281 | 5184 | const operand = try self.resolveInst(un_op); |
| 5282 | | const alignment = ret_ty.abiAlignment(mod); |
| 5185 | const alignment = Builder.Alignment.fromByteUnits(ret_ty.abiAlignment(mod)); |
| 5283 | 5186 | |
| 5284 | 5187 | if (isByRef(ret_ty, mod)) { |
| 5285 | 5188 | // operand is a pointer however self.ret_ptr is null so that means |
| 5286 | 5189 | // we need to return a value. |
| 5287 | | const load_inst = self.builder.buildLoad(abi_ret_ty, operand, ""); |
| 5288 | | load_inst.setAlignment(alignment); |
| 5289 | | _ = self.builder.buildRet(load_inst); |
| 5290 | | return null; |
| 5190 | _ = try self.wip.ret(try self.wip.load(.normal, abi_ret_ty, operand, alignment, "")); |
| 5191 | return .none; |
| 5291 | 5192 | } |
| 5292 | 5193 | |
| 5293 | | const llvm_ret_ty = operand.typeOf(); |
| 5194 | const llvm_ret_ty = operand.typeOfWip(&self.wip); |
| 5294 | 5195 | if (abi_ret_ty == llvm_ret_ty) { |
| 5295 | | _ = self.builder.buildRet(operand); |
| 5296 | | return null; |
| 5196 | _ = try self.wip.ret(operand); |
| 5197 | return .none; |
| 5297 | 5198 | } |
| 5298 | 5199 | |
| 5299 | 5200 | const rp = try self.buildAlloca(llvm_ret_ty, alignment); |
| 5300 | | const store_inst = self.builder.buildStore(operand, rp); |
| 5301 | | store_inst.setAlignment(alignment); |
| 5302 | | const load_inst = self.builder.buildLoad(abi_ret_ty, rp, ""); |
| 5303 | | load_inst.setAlignment(alignment); |
| 5304 | | _ = self.builder.buildRet(load_inst); |
| 5305 | | return null; |
| 5201 | _ = try self.wip.store(.normal, operand, rp, alignment); |
| 5202 | _ = try self.wip.ret(try self.wip.load(.normal, abi_ret_ty, rp, alignment, "")); |
| 5203 | return .none; |
| 5306 | 5204 | } |
| 5307 | 5205 | |
| 5308 | | fn airRetLoad(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 5206 | fn airRetLoad(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 5309 | 5207 | const o = self.dg.object; |
| 5310 | 5208 | const mod = o.module; |
| 5311 | 5209 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| ... | ... | @@ -5317,106 +5215,121 @@ pub const FuncGen = struct { |
| 5317 | 5215 | // Functions with an empty error set are emitted with an error code |
| 5318 | 5216 | // return type and return zero so they can be function pointers coerced |
| 5319 | 5217 | // to functions that return anyerror. |
| 5320 | | const int = try o.builder.intConst(Builder.Type.err_int, 0); |
| 5321 | | _ = self.builder.buildRet(int.toLlvm(&o.builder)); |
| 5218 | _ = try self.wip.ret(try o.builder.intValue(Builder.Type.err_int, 0)); |
| 5322 | 5219 | } else { |
| 5323 | | try self.wip.retVoid(); |
| 5220 | _ = try self.wip.retVoid(); |
| 5324 | 5221 | } |
| 5325 | | return null; |
| 5222 | return .none; |
| 5326 | 5223 | } |
| 5327 | | if (self.ret_ptr != null) { |
| 5328 | | try self.wip.retVoid(); |
| 5329 | | return null; |
| 5224 | if (self.ret_ptr != .none) { |
| 5225 | _ = try self.wip.retVoid(); |
| 5226 | return .none; |
| 5330 | 5227 | } |
| 5331 | 5228 | const ptr = try self.resolveInst(un_op); |
| 5332 | | const abi_ret_ty = (try lowerFnRetTy(o, fn_info)).toLlvm(&o.builder); |
| 5333 | | const loaded = self.builder.buildLoad(abi_ret_ty, ptr, ""); |
| 5334 | | loaded.setAlignment(ret_ty.abiAlignment(mod)); |
| 5335 | | _ = self.builder.buildRet(loaded); |
| 5336 | | return null; |
| 5229 | const abi_ret_ty = try lowerFnRetTy(o, fn_info); |
| 5230 | const alignment = Builder.Alignment.fromByteUnits(ret_ty.abiAlignment(mod)); |
| 5231 | _ = try self.wip.ret(try self.wip.load(.normal, abi_ret_ty, ptr, alignment, "")); |
| 5232 | return .none; |
| 5337 | 5233 | } |
| 5338 | 5234 | |
| 5339 | | fn airCVaArg(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 5235 | fn airCVaArg(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 5340 | 5236 | const o = self.dg.object; |
| 5341 | 5237 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 5342 | 5238 | const list = try self.resolveInst(ty_op.operand); |
| 5343 | 5239 | const arg_ty = self.air.getRefType(ty_op.ty); |
| 5344 | | const llvm_arg_ty = (try o.lowerType(arg_ty)).toLlvm(&o.builder); |
| 5240 | const llvm_arg_ty = try o.lowerType(arg_ty); |
| 5345 | 5241 | |
| 5346 | | return self.builder.buildVAArg(list, llvm_arg_ty, ""); |
| 5242 | return self.wip.vaArg(list, llvm_arg_ty, ""); |
| 5347 | 5243 | } |
| 5348 | 5244 | |
| 5349 | | fn airCVaCopy(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 5245 | fn airCVaCopy(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 5350 | 5246 | const o = self.dg.object; |
| 5351 | 5247 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 5352 | 5248 | const src_list = try self.resolveInst(ty_op.operand); |
| 5353 | 5249 | const va_list_ty = self.air.getRefType(ty_op.ty); |
| 5354 | | const llvm_va_list_ty = (try o.lowerType(va_list_ty)).toLlvm(&o.builder); |
| 5250 | const llvm_va_list_ty = try o.lowerType(va_list_ty); |
| 5355 | 5251 | const mod = o.module; |
| 5356 | 5252 | |
| 5357 | | const result_alignment = va_list_ty.abiAlignment(mod); |
| 5253 | const result_alignment = Builder.Alignment.fromByteUnits(va_list_ty.abiAlignment(mod)); |
| 5358 | 5254 | const dest_list = try self.buildAlloca(llvm_va_list_ty, result_alignment); |
| 5359 | 5255 | |
| 5360 | 5256 | const llvm_fn_name = "llvm.va_copy"; |
| 5361 | | const llvm_fn = o.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: { |
| 5362 | | const fn_type = try o.builder.fnType(.void, &.{ .ptr, .ptr }, .normal); |
| 5363 | | break :blk o.llvm_module.addFunction(llvm_fn_name, fn_type.toLlvm(&o.builder)); |
| 5364 | | }; |
| 5257 | const llvm_fn_ty = try o.builder.fnType(.void, &.{ .ptr, .ptr }, .normal); |
| 5258 | const llvm_fn = o.llvm_module.getNamedFunction(llvm_fn_name) orelse |
| 5259 | o.llvm_module.addFunction(llvm_fn_name, llvm_fn_ty.toLlvm(&o.builder)); |
| 5365 | 5260 | |
| 5366 | | const args: [2]*llvm.Value = .{ dest_list, src_list }; |
| 5367 | | _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, ""); |
| 5261 | const args: [2]*llvm.Value = .{ dest_list.toLlvm(&self.wip), src_list.toLlvm(&self.wip) }; |
| 5262 | _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall( |
| 5263 | llvm_fn_ty.toLlvm(&o.builder), |
| 5264 | llvm_fn, |
| 5265 | &args, |
| 5266 | args.len, |
| 5267 | .Fast, |
| 5268 | .Auto, |
| 5269 | "", |
| 5270 | ), &self.wip); |
| 5368 | 5271 | |
| 5369 | | if (isByRef(va_list_ty, mod)) { |
| 5370 | | return dest_list; |
| 5371 | | } else { |
| 5372 | | const loaded = self.builder.buildLoad(llvm_va_list_ty, dest_list, ""); |
| 5373 | | loaded.setAlignment(result_alignment); |
| 5374 | | return loaded; |
| 5375 | | } |
| 5272 | return if (isByRef(va_list_ty, mod)) |
| 5273 | dest_list |
| 5274 | else |
| 5275 | try self.wip.load(.normal, llvm_va_list_ty, dest_list, result_alignment, ""); |
| 5376 | 5276 | } |
| 5377 | 5277 | |
| 5378 | | fn airCVaEnd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 5278 | fn airCVaEnd(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 5379 | 5279 | const o = self.dg.object; |
| 5380 | 5280 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 5381 | 5281 | const list = try self.resolveInst(un_op); |
| 5382 | 5282 | |
| 5383 | 5283 | const llvm_fn_name = "llvm.va_end"; |
| 5384 | | const llvm_fn = o.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: { |
| 5385 | | const fn_type = try o.builder.fnType(.void, &.{.ptr}, .normal); |
| 5386 | | break :blk o.llvm_module.addFunction(llvm_fn_name, fn_type.toLlvm(&o.builder)); |
| 5387 | | }; |
| 5388 | | const args: [1]*llvm.Value = .{list}; |
| 5389 | | _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, ""); |
| 5390 | | return null; |
| 5284 | const llvm_fn_ty = try o.builder.fnType(.void, &.{.ptr}, .normal); |
| 5285 | const llvm_fn = o.llvm_module.getNamedFunction(llvm_fn_name) orelse |
| 5286 | o.llvm_module.addFunction(llvm_fn_name, llvm_fn_ty.toLlvm(&o.builder)); |
| 5287 | |
| 5288 | const args: [1]*llvm.Value = .{list.toLlvm(&self.wip)}; |
| 5289 | _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall( |
| 5290 | llvm_fn_ty.toLlvm(&o.builder), |
| 5291 | llvm_fn, |
| 5292 | &args, |
| 5293 | args.len, |
| 5294 | .Fast, |
| 5295 | .Auto, |
| 5296 | "", |
| 5297 | ), &self.wip); |
| 5298 | return .none; |
| 5391 | 5299 | } |
| 5392 | 5300 | |
| 5393 | | fn airCVaStart(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 5301 | fn airCVaStart(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 5394 | 5302 | const o = self.dg.object; |
| 5395 | 5303 | const mod = o.module; |
| 5396 | 5304 | const va_list_ty = self.typeOfIndex(inst); |
| 5397 | | const llvm_va_list_ty = (try o.lowerType(va_list_ty)).toLlvm(&o.builder); |
| 5305 | const llvm_va_list_ty = try o.lowerType(va_list_ty); |
| 5398 | 5306 | |
| 5399 | | const result_alignment = va_list_ty.abiAlignment(mod); |
| 5307 | const result_alignment = Builder.Alignment.fromByteUnits(va_list_ty.abiAlignment(mod)); |
| 5400 | 5308 | const list = try self.buildAlloca(llvm_va_list_ty, result_alignment); |
| 5401 | 5309 | |
| 5402 | 5310 | const llvm_fn_name = "llvm.va_start"; |
| 5403 | | const llvm_fn = o.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: { |
| 5404 | | const fn_type = try o.builder.fnType(.void, &.{.ptr}, .normal); |
| 5405 | | break :blk o.llvm_module.addFunction(llvm_fn_name, fn_type.toLlvm(&o.builder)); |
| 5406 | | }; |
| 5407 | | const args: [1]*llvm.Value = .{list}; |
| 5408 | | _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, ""); |
| 5311 | const llvm_fn_ty = try o.builder.fnType(.void, &.{.ptr}, .normal); |
| 5312 | const llvm_fn = o.llvm_module.getNamedFunction(llvm_fn_name) orelse |
| 5313 | o.llvm_module.addFunction(llvm_fn_name, llvm_fn_ty.toLlvm(&o.builder)); |
| 5409 | 5314 | |
| 5410 | | if (isByRef(va_list_ty, mod)) { |
| 5411 | | return list; |
| 5412 | | } else { |
| 5413 | | const loaded = self.builder.buildLoad(llvm_va_list_ty, list, ""); |
| 5414 | | loaded.setAlignment(result_alignment); |
| 5415 | | return loaded; |
| 5416 | | } |
| 5315 | const args: [1]*llvm.Value = .{list.toLlvm(&self.wip)}; |
| 5316 | _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall( |
| 5317 | llvm_fn_ty.toLlvm(&o.builder), |
| 5318 | llvm_fn, |
| 5319 | &args, |
| 5320 | args.len, |
| 5321 | .Fast, |
| 5322 | .Auto, |
| 5323 | "", |
| 5324 | ), &self.wip); |
| 5325 | |
| 5326 | return if (isByRef(va_list_ty, mod)) |
| 5327 | list |
| 5328 | else |
| 5329 | try self.wip.load(.normal, llvm_va_list_ty, list, result_alignment, ""); |
| 5417 | 5330 | } |
| 5418 | 5331 | |
| 5419 | | fn airCmp(self: *FuncGen, inst: Air.Inst.Index, op: math.CompareOperator, want_fast_math: bool) !?*llvm.Value { |
| 5332 | fn airCmp(self: *FuncGen, inst: Air.Inst.Index, op: math.CompareOperator, want_fast_math: bool) !Builder.Value { |
| 5420 | 5333 | self.builder.setFastMath(want_fast_math); |
| 5421 | 5334 | |
| 5422 | 5335 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| ... | ... | @@ -5427,7 +5340,7 @@ pub const FuncGen = struct { |
| 5427 | 5340 | return self.cmp(lhs, rhs, operand_ty, op); |
| 5428 | 5341 | } |
| 5429 | 5342 | |
| 5430 | | fn airCmpVector(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value { |
| 5343 | fn airCmpVector(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !Builder.Value { |
| 5431 | 5344 | self.builder.setFastMath(want_fast_math); |
| 5432 | 5345 | |
| 5433 | 5346 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| ... | ... | @@ -5441,21 +5354,30 @@ pub const FuncGen = struct { |
| 5441 | 5354 | return self.cmp(lhs, rhs, vec_ty, cmp_op); |
| 5442 | 5355 | } |
| 5443 | 5356 | |
| 5444 | | fn airCmpLtErrorsLen(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 5357 | fn airCmpLtErrorsLen(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 5358 | const o = self.dg.object; |
| 5445 | 5359 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 5446 | 5360 | const operand = try self.resolveInst(un_op); |
| 5447 | 5361 | const llvm_fn = try self.getCmpLtErrorsLenFunction(); |
| 5448 | | const args: [1]*llvm.Value = .{operand}; |
| 5449 | | return self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, ""); |
| 5362 | const args: [1]*llvm.Value = .{operand.toLlvm(&self.wip)}; |
| 5363 | return (try self.wip.unimplemented(.i1, "")).finish(self.builder.buildCall( |
| 5364 | llvm_fn.typeOf(&o.builder).toLlvm(&o.builder), |
| 5365 | llvm_fn.toLlvm(&o.builder), |
| 5366 | &args, |
| 5367 | args.len, |
| 5368 | .Fast, |
| 5369 | .Auto, |
| 5370 | "", |
| 5371 | ), &self.wip); |
| 5450 | 5372 | } |
| 5451 | 5373 | |
| 5452 | 5374 | fn cmp( |
| 5453 | 5375 | self: *FuncGen, |
| 5454 | | lhs: *llvm.Value, |
| 5455 | | rhs: *llvm.Value, |
| 5376 | lhs: Builder.Value, |
| 5377 | rhs: Builder.Value, |
| 5456 | 5378 | operand_ty: Type, |
| 5457 | 5379 | op: math.CompareOperator, |
| 5458 | | ) Allocator.Error!*llvm.Value { |
| 5380 | ) Allocator.Error!Builder.Value { |
| 5459 | 5381 | const o = self.dg.object; |
| 5460 | 5382 | const mod = o.module; |
| 5461 | 5383 | const scalar_ty = operand_ty.scalarType(mod); |
| ... | ... | @@ -5472,50 +5394,48 @@ pub const FuncGen = struct { |
| 5472 | 5394 | // We need to emit instructions to check for equality/inequality |
| 5473 | 5395 | // of optionals that are not pointers. |
| 5474 | 5396 | const is_by_ref = isByRef(scalar_ty, mod); |
| 5475 | | const opt_llvm_ty = (try o.lowerType(scalar_ty)).toLlvm(&o.builder); |
| 5476 | | const lhs_non_null = try self.optIsNonNull(opt_llvm_ty, lhs, is_by_ref); |
| 5477 | | const rhs_non_null = try self.optIsNonNull(opt_llvm_ty, rhs, is_by_ref); |
| 5397 | const opt_llvm_ty = try o.lowerType(scalar_ty); |
| 5398 | const lhs_non_null = try self.optCmpNull(.ne, opt_llvm_ty, lhs, is_by_ref); |
| 5399 | const rhs_non_null = try self.optCmpNull(.ne, opt_llvm_ty, rhs, is_by_ref); |
| 5478 | 5400 | const llvm_i2 = try o.builder.intType(2); |
| 5479 | | const lhs_non_null_i2 = self.builder.buildZExt(lhs_non_null, llvm_i2.toLlvm(&o.builder), ""); |
| 5480 | | const rhs_non_null_i2 = self.builder.buildZExt(rhs_non_null, llvm_i2.toLlvm(&o.builder), ""); |
| 5481 | | const lhs_shifted = self.builder.buildShl(lhs_non_null_i2, (try o.builder.intConst(llvm_i2, 1)).toLlvm(&o.builder), ""); |
| 5482 | | const lhs_rhs_ored = self.builder.buildOr(lhs_shifted, rhs_non_null_i2, ""); |
| 5483 | | const both_null_block = try self.wip.block("BothNull"); |
| 5484 | | const mixed_block = try self.wip.block("Mixed"); |
| 5485 | | const both_pl_block = try self.wip.block("BothNonNull"); |
| 5486 | | const end_block = try self.wip.block("End"); |
| 5487 | | const llvm_switch = self.builder.buildSwitch(lhs_rhs_ored, mixed_block.toLlvm(&self.wip), 2); |
| 5488 | | const llvm_i2_00 = try o.builder.intConst(llvm_i2, 0b00); |
| 5489 | | const llvm_i2_11 = try o.builder.intConst(llvm_i2, 0b11); |
| 5490 | | llvm_switch.addCase(llvm_i2_00.toLlvm(&o.builder), both_null_block.toLlvm(&self.wip)); |
| 5491 | | llvm_switch.addCase(llvm_i2_11.toLlvm(&o.builder), both_pl_block.toLlvm(&self.wip)); |
| 5401 | const lhs_non_null_i2 = try self.wip.cast(.zext, lhs_non_null, llvm_i2, ""); |
| 5402 | const rhs_non_null_i2 = try self.wip.cast(.zext, rhs_non_null, llvm_i2, ""); |
| 5403 | const lhs_shifted = try self.wip.bin(.shl, lhs_non_null_i2, try o.builder.intValue(llvm_i2, 1), ""); |
| 5404 | const lhs_rhs_ored = try self.wip.bin(.@"or", lhs_shifted, rhs_non_null_i2, ""); |
| 5405 | const both_null_block = try self.wip.block(1, "BothNull"); |
| 5406 | const mixed_block = try self.wip.block(1, "Mixed"); |
| 5407 | const both_pl_block = try self.wip.block(1, "BothNonNull"); |
| 5408 | const end_block = try self.wip.block(3, "End"); |
| 5409 | var wip_switch = try self.wip.@"switch"(lhs_rhs_ored, mixed_block, 2); |
| 5410 | defer wip_switch.finish(&self.wip); |
| 5411 | try wip_switch.addCase( |
| 5412 | try o.builder.intConst(llvm_i2, 0b00), |
| 5413 | both_null_block, |
| 5414 | &self.wip, |
| 5415 | ); |
| 5416 | try wip_switch.addCase( |
| 5417 | try o.builder.intConst(llvm_i2, 0b11), |
| 5418 | both_pl_block, |
| 5419 | &self.wip, |
| 5420 | ); |
| 5492 | 5421 | |
| 5493 | 5422 | self.wip.cursor = .{ .block = both_null_block }; |
| 5494 | | self.builder.positionBuilderAtEnd(both_null_block.toLlvm(&self.wip)); |
| 5495 | | _ = self.builder.buildBr(end_block.toLlvm(&self.wip)); |
| 5423 | _ = try self.wip.br(end_block); |
| 5496 | 5424 | |
| 5497 | 5425 | self.wip.cursor = .{ .block = mixed_block }; |
| 5498 | | self.builder.positionBuilderAtEnd(mixed_block.toLlvm(&self.wip)); |
| 5499 | | _ = self.builder.buildBr(end_block.toLlvm(&self.wip)); |
| 5426 | _ = try self.wip.br(end_block); |
| 5500 | 5427 | |
| 5501 | 5428 | self.wip.cursor = .{ .block = both_pl_block }; |
| 5502 | | self.builder.positionBuilderAtEnd(both_pl_block.toLlvm(&self.wip)); |
| 5503 | 5429 | const lhs_payload = try self.optPayloadHandle(opt_llvm_ty, lhs, scalar_ty, true); |
| 5504 | 5430 | const rhs_payload = try self.optPayloadHandle(opt_llvm_ty, rhs, scalar_ty, true); |
| 5505 | 5431 | const payload_cmp = try self.cmp(lhs_payload, rhs_payload, payload_ty, op); |
| 5506 | | _ = self.builder.buildBr(end_block.toLlvm(&self.wip)); |
| 5507 | | const both_pl_block_end = self.builder.getInsertBlock(); |
| 5432 | _ = try self.wip.br(end_block); |
| 5433 | const both_pl_block_end = self.wip.cursor.block; |
| 5508 | 5434 | |
| 5509 | 5435 | self.wip.cursor = .{ .block = end_block }; |
| 5510 | | self.builder.positionBuilderAtEnd(end_block.toLlvm(&self.wip)); |
| 5511 | | const incoming_blocks: [3]*llvm.BasicBlock = .{ |
| 5512 | | both_null_block.toLlvm(&self.wip), |
| 5513 | | mixed_block.toLlvm(&self.wip), |
| 5514 | | both_pl_block_end, |
| 5515 | | }; |
| 5516 | | const llvm_i1_0 = Builder.Constant.false.toLlvm(&o.builder); |
| 5517 | | const llvm_i1_1 = Builder.Constant.true.toLlvm(&o.builder); |
| 5518 | | const incoming_values: [3]*llvm.Value = .{ |
| 5436 | const llvm_i1_0 = try o.builder.intValue(.i1, 0); |
| 5437 | const llvm_i1_1 = try o.builder.intValue(.i1, 1); |
| 5438 | const incoming_values: [3]Builder.Value = .{ |
| 5519 | 5439 | switch (op) { |
| 5520 | 5440 | .eq => llvm_i1_1, |
| 5521 | 5441 | .neq => llvm_i1_0, |
| ... | ... | @@ -5529,31 +5449,30 @@ pub const FuncGen = struct { |
| 5529 | 5449 | payload_cmp, |
| 5530 | 5450 | }; |
| 5531 | 5451 | |
| 5532 | | const phi_node = self.builder.buildPhi(Builder.Type.i1.toLlvm(&o.builder), ""); |
| 5533 | | comptime assert(incoming_values.len == incoming_blocks.len); |
| 5534 | | phi_node.addIncoming( |
| 5452 | const phi = try self.wip.phi(.i1, ""); |
| 5453 | try phi.finish( |
| 5535 | 5454 | &incoming_values, |
| 5536 | | &incoming_blocks, |
| 5537 | | incoming_values.len, |
| 5455 | &.{ both_null_block, mixed_block, both_pl_block_end }, |
| 5456 | &self.wip, |
| 5538 | 5457 | ); |
| 5539 | | return phi_node; |
| 5458 | return phi.toValue(); |
| 5540 | 5459 | }, |
| 5541 | 5460 | .Float => return self.buildFloatCmp(op, operand_ty, .{ lhs, rhs }), |
| 5542 | 5461 | else => unreachable, |
| 5543 | 5462 | }; |
| 5544 | 5463 | const is_signed = int_ty.isSignedInt(mod); |
| 5545 | | const operation: llvm.IntPredicate = switch (op) { |
| 5546 | | .eq => .EQ, |
| 5547 | | .neq => .NE, |
| 5548 | | .lt => if (is_signed) llvm.IntPredicate.SLT else .ULT, |
| 5549 | | .lte => if (is_signed) llvm.IntPredicate.SLE else .ULE, |
| 5550 | | .gt => if (is_signed) llvm.IntPredicate.SGT else .UGT, |
| 5551 | | .gte => if (is_signed) llvm.IntPredicate.SGE else .UGE, |
| 5464 | const cond: Builder.IntegerCondition = switch (op) { |
| 5465 | .eq => .eq, |
| 5466 | .neq => .ne, |
| 5467 | .lt => if (is_signed) .slt else .ult, |
| 5468 | .lte => if (is_signed) .sle else .ule, |
| 5469 | .gt => if (is_signed) .sgt else .ugt, |
| 5470 | .gte => if (is_signed) .sge else .uge, |
| 5552 | 5471 | }; |
| 5553 | | return self.builder.buildICmp(operation, lhs, rhs, ""); |
| 5472 | return self.wip.icmp(cond, lhs, rhs, ""); |
| 5554 | 5473 | } |
| 5555 | 5474 | |
| 5556 | | fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 5475 | fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 5557 | 5476 | const o = self.dg.object; |
| 5558 | 5477 | const mod = o.module; |
| 5559 | 5478 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| ... | ... | @@ -5563,13 +5482,15 @@ pub const FuncGen = struct { |
| 5563 | 5482 | |
| 5564 | 5483 | if (inst_ty.isNoReturn(mod)) { |
| 5565 | 5484 | try self.genBody(body); |
| 5566 | | return null; |
| 5485 | return .none; |
| 5567 | 5486 | } |
| 5568 | 5487 | |
| 5569 | | var breaks: BreakList = .{}; |
| 5570 | | defer breaks.deinit(self.gpa); |
| 5488 | const have_block_result = inst_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod); |
| 5571 | 5489 | |
| 5572 | | const parent_bb = try self.wip.block("Block"); |
| 5490 | var breaks: BreakList = if (have_block_result) .{ .list = .{} } else .{ .len = 0 }; |
| 5491 | defer if (have_block_result) breaks.list.deinit(self.gpa); |
| 5492 | |
| 5493 | const parent_bb = try self.wip.block(0, "Block"); |
| 5573 | 5494 | try self.blocks.putNoClobber(self.gpa, inst, .{ |
| 5574 | 5495 | .parent_bb = parent_bb, |
| 5575 | 5496 | .breaks = &breaks, |
| ... | ... | @@ -5579,35 +5500,32 @@ pub const FuncGen = struct { |
| 5579 | 5500 | try self.genBody(body); |
| 5580 | 5501 | |
| 5581 | 5502 | self.wip.cursor = .{ .block = parent_bb }; |
| 5582 | | self.builder.positionBuilderAtEnd(parent_bb.toLlvm(&self.wip)); |
| 5583 | 5503 | |
| 5584 | 5504 | // Create a phi node only if the block returns a value. |
| 5585 | | const is_body = inst_ty.zigTypeTag(mod) == .Fn; |
| 5586 | | if (!is_body and !inst_ty.hasRuntimeBitsIgnoreComptime(mod)) return null; |
| 5587 | | |
| 5588 | | const raw_llvm_ty = (try o.lowerType(inst_ty)).toLlvm(&o.builder); |
| 5589 | | |
| 5590 | | const llvm_ty = ty: { |
| 5591 | | // If the zig tag type is a function, this represents an actual function body; not |
| 5592 | | // a pointer to it. LLVM IR allows the call instruction to use function bodies instead |
| 5593 | | // of function pointers, however the phi makes it a runtime value and therefore |
| 5594 | | // the LLVM type has to be wrapped in a pointer. |
| 5595 | | if (is_body or isByRef(inst_ty, mod)) { |
| 5596 | | break :ty Builder.Type.ptr.toLlvm(&o.builder); |
| 5597 | | } |
| 5598 | | break :ty raw_llvm_ty; |
| 5599 | | }; |
| 5505 | if (have_block_result) { |
| 5506 | const raw_llvm_ty = try o.lowerType(inst_ty); |
| 5507 | const llvm_ty: Builder.Type = ty: { |
| 5508 | // If the zig tag type is a function, this represents an actual function body; not |
| 5509 | // a pointer to it. LLVM IR allows the call instruction to use function bodies instead |
| 5510 | // of function pointers, however the phi makes it a runtime value and therefore |
| 5511 | // the LLVM type has to be wrapped in a pointer. |
| 5512 | if (inst_ty.zigTypeTag(mod) == .Fn or isByRef(inst_ty, mod)) { |
| 5513 | break :ty .ptr; |
| 5514 | } |
| 5515 | break :ty raw_llvm_ty; |
| 5516 | }; |
| 5600 | 5517 | |
| 5601 | | const phi_node = self.builder.buildPhi(llvm_ty, ""); |
| 5602 | | phi_node.addIncoming( |
| 5603 | | breaks.items(.val).ptr, |
| 5604 | | breaks.items(.bb).ptr, |
| 5605 | | @intCast(breaks.len), |
| 5606 | | ); |
| 5607 | | return phi_node; |
| 5518 | parent_bb.ptr(&self.wip).incoming = @intCast(breaks.list.len); |
| 5519 | const phi = try self.wip.phi(llvm_ty, ""); |
| 5520 | try phi.finish(breaks.list.items(.val), breaks.list.items(.bb), &self.wip); |
| 5521 | return phi.toValue(); |
| 5522 | } else { |
| 5523 | parent_bb.ptr(&self.wip).incoming = @intCast(breaks.len); |
| 5524 | return .none; |
| 5525 | } |
| 5608 | 5526 | } |
| 5609 | 5527 | |
| 5610 | | fn airBr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 5528 | fn airBr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 5611 | 5529 | const o = self.dg.object; |
| 5612 | 5530 | const branch = self.air.instructions.items(.data)[inst].br; |
| 5613 | 5531 | const block = self.blocks.get(branch.block_inst).?; |
| ... | ... | @@ -5615,44 +5533,39 @@ pub const FuncGen = struct { |
| 5615 | 5533 | // Add the values to the lists only if the break provides a value. |
| 5616 | 5534 | const operand_ty = self.typeOf(branch.operand); |
| 5617 | 5535 | const mod = o.module; |
| 5618 | | if (operand_ty.hasRuntimeBitsIgnoreComptime(mod) or operand_ty.zigTypeTag(mod) == .Fn) { |
| 5536 | if (operand_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) { |
| 5619 | 5537 | const val = try self.resolveInst(branch.operand); |
| 5620 | 5538 | |
| 5621 | 5539 | // For the phi node, we need the basic blocks and the values of the |
| 5622 | 5540 | // break instructions. |
| 5623 | | try block.breaks.append(self.gpa, .{ |
| 5624 | | .bb = self.builder.getInsertBlock(), |
| 5625 | | .val = val, |
| 5626 | | }); |
| 5627 | | } |
| 5628 | | _ = self.builder.buildBr(block.parent_bb.toLlvm(&self.wip)); |
| 5629 | | return null; |
| 5541 | try block.breaks.list.append(self.gpa, .{ .bb = self.wip.cursor.block, .val = val }); |
| 5542 | } else block.breaks.len += 1; |
| 5543 | _ = try self.wip.br(block.parent_bb); |
| 5544 | return .none; |
| 5630 | 5545 | } |
| 5631 | 5546 | |
| 5632 | | fn airCondBr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 5547 | fn airCondBr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 5633 | 5548 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 5634 | 5549 | const cond = try self.resolveInst(pl_op.operand); |
| 5635 | 5550 | const extra = self.air.extraData(Air.CondBr, pl_op.payload); |
| 5636 | 5551 | const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; |
| 5637 | 5552 | const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; |
| 5638 | 5553 | |
| 5639 | | const then_block = try self.wip.block("Then"); |
| 5640 | | const else_block = try self.wip.block("Else"); |
| 5641 | | _ = self.builder.buildCondBr(cond, then_block.toLlvm(&self.wip), else_block.toLlvm(&self.wip)); |
| 5554 | const then_block = try self.wip.block(1, "Then"); |
| 5555 | const else_block = try self.wip.block(1, "Else"); |
| 5556 | _ = try self.wip.brCond(cond, then_block, else_block); |
| 5642 | 5557 | |
| 5643 | 5558 | self.wip.cursor = .{ .block = then_block }; |
| 5644 | | self.builder.positionBuilderAtEnd(then_block.toLlvm(&self.wip)); |
| 5645 | 5559 | try self.genBody(then_body); |
| 5646 | 5560 | |
| 5647 | 5561 | self.wip.cursor = .{ .block = else_block }; |
| 5648 | | self.builder.positionBuilderAtEnd(else_block.toLlvm(&self.wip)); |
| 5649 | 5562 | try self.genBody(else_body); |
| 5650 | 5563 | |
| 5651 | 5564 | // No need to reset the insert cursor since this instruction is noreturn. |
| 5652 | | return null; |
| 5565 | return .none; |
| 5653 | 5566 | } |
| 5654 | 5567 | |
| 5655 | | fn airTry(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value { |
| 5568 | fn airTry(self: *FuncGen, body_tail: []const Air.Inst.Index) !Builder.Value { |
| 5656 | 5569 | const o = self.dg.object; |
| 5657 | 5570 | const mod = o.module; |
| 5658 | 5571 | const inst = body_tail[0]; |
| ... | ... | @@ -5667,7 +5580,7 @@ pub const FuncGen = struct { |
| 5667 | 5580 | return lowerTry(self, err_union, body, err_union_ty, false, can_elide_load, is_unused); |
| 5668 | 5581 | } |
| 5669 | 5582 | |
| 5670 | | fn airTryPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 5583 | fn airTryPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 5671 | 5584 | const o = self.dg.object; |
| 5672 | 5585 | const mod = o.module; |
| 5673 | 5586 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| ... | ... | @@ -5681,139 +5594,149 @@ pub const FuncGen = struct { |
| 5681 | 5594 | |
| 5682 | 5595 | fn lowerTry( |
| 5683 | 5596 | fg: *FuncGen, |
| 5684 | | err_union: *llvm.Value, |
| 5597 | err_union: Builder.Value, |
| 5685 | 5598 | body: []const Air.Inst.Index, |
| 5686 | 5599 | err_union_ty: Type, |
| 5687 | 5600 | operand_is_ptr: bool, |
| 5688 | 5601 | can_elide_load: bool, |
| 5689 | 5602 | is_unused: bool, |
| 5690 | | ) !?*llvm.Value { |
| 5603 | ) !Builder.Value { |
| 5691 | 5604 | const o = fg.dg.object; |
| 5692 | 5605 | const mod = o.module; |
| 5693 | 5606 | const payload_ty = err_union_ty.errorUnionPayload(mod); |
| 5694 | 5607 | const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(mod); |
| 5695 | | const err_union_llvm_ty = (try o.lowerType(err_union_ty)).toLlvm(&o.builder); |
| 5608 | const err_union_llvm_ty = try o.lowerType(err_union_ty); |
| 5696 | 5609 | |
| 5697 | 5610 | if (!err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) { |
| 5698 | | const is_err = err: { |
| 5699 | | const err_set_ty = Builder.Type.err_int.toLlvm(&o.builder); |
| 5700 | | const zero = (try o.builder.intConst(Builder.Type.err_int, 0)).toLlvm(&o.builder); |
| 5611 | const loaded = loaded: { |
| 5701 | 5612 | if (!payload_has_bits) { |
| 5702 | 5613 | // TODO add alignment to this load |
| 5703 | | const loaded = if (operand_is_ptr) |
| 5704 | | fg.builder.buildLoad(err_set_ty, err_union, "") |
| 5614 | break :loaded if (operand_is_ptr) |
| 5615 | try fg.wip.load(.normal, Builder.Type.err_int, err_union, .default, "") |
| 5705 | 5616 | else |
| 5706 | 5617 | err_union; |
| 5707 | | break :err fg.builder.buildICmp(.NE, loaded, zero, ""); |
| 5708 | 5618 | } |
| 5709 | 5619 | const err_field_index = errUnionErrorOffset(payload_ty, mod); |
| 5710 | 5620 | if (operand_is_ptr or isByRef(err_union_ty, mod)) { |
| 5711 | | const err_field_ptr = fg.builder.buildStructGEP(err_union_llvm_ty, err_union, err_field_index, ""); |
| 5621 | const err_field_ptr = |
| 5622 | try fg.wip.gepStruct(err_union_llvm_ty, err_union, err_field_index, ""); |
| 5712 | 5623 | // TODO add alignment to this load |
| 5713 | | const loaded = fg.builder.buildLoad(err_set_ty, err_field_ptr, ""); |
| 5714 | | break :err fg.builder.buildICmp(.NE, loaded, zero, ""); |
| 5624 | break :loaded try fg.wip.load( |
| 5625 | .normal, |
| 5626 | Builder.Type.err_int, |
| 5627 | err_field_ptr, |
| 5628 | .default, |
| 5629 | "", |
| 5630 | ); |
| 5715 | 5631 | } |
| 5716 | | const loaded = fg.builder.buildExtractValue(err_union, err_field_index, ""); |
| 5717 | | break :err fg.builder.buildICmp(.NE, loaded, zero, ""); |
| 5632 | break :loaded try fg.wip.extractValue(err_union, &.{err_field_index}, ""); |
| 5718 | 5633 | }; |
| 5634 | const zero = try o.builder.intValue(Builder.Type.err_int, 0); |
| 5635 | const is_err = try fg.wip.icmp(.ne, loaded, zero, ""); |
| 5719 | 5636 | |
| 5720 | | const return_block = try fg.wip.block("TryRet"); |
| 5721 | | const continue_block = try fg.wip.block("TryCont"); |
| 5722 | | _ = fg.builder.buildCondBr(is_err, return_block.toLlvm(&fg.wip), continue_block.toLlvm(&fg.wip)); |
| 5637 | const return_block = try fg.wip.block(1, "TryRet"); |
| 5638 | const continue_block = try fg.wip.block(1, "TryCont"); |
| 5639 | _ = try fg.wip.brCond(is_err, return_block, continue_block); |
| 5723 | 5640 | |
| 5724 | 5641 | fg.wip.cursor = .{ .block = return_block }; |
| 5725 | | fg.builder.positionBuilderAtEnd(return_block.toLlvm(&fg.wip)); |
| 5726 | 5642 | try fg.genBody(body); |
| 5727 | 5643 | |
| 5728 | 5644 | fg.wip.cursor = .{ .block = continue_block }; |
| 5729 | | fg.builder.positionBuilderAtEnd(continue_block.toLlvm(&fg.wip)); |
| 5730 | | } |
| 5731 | | if (is_unused) { |
| 5732 | | return null; |
| 5733 | | } |
| 5734 | | if (!payload_has_bits) { |
| 5735 | | return if (operand_is_ptr) err_union else null; |
| 5736 | 5645 | } |
| 5646 | if (is_unused) return .none; |
| 5647 | if (!payload_has_bits) return if (operand_is_ptr) err_union else .none; |
| 5737 | 5648 | const offset = errUnionPayloadOffset(payload_ty, mod); |
| 5738 | 5649 | if (operand_is_ptr) { |
| 5739 | | return fg.builder.buildStructGEP(err_union_llvm_ty, err_union, offset, ""); |
| 5650 | return fg.wip.gepStruct(err_union_llvm_ty, err_union, offset, ""); |
| 5740 | 5651 | } else if (isByRef(err_union_ty, mod)) { |
| 5741 | | const payload_ptr = fg.builder.buildStructGEP(err_union_llvm_ty, err_union, offset, ""); |
| 5652 | const payload_ptr = try fg.wip.gepStruct(err_union_llvm_ty, err_union, offset, ""); |
| 5653 | const payload_alignment = Builder.Alignment.fromByteUnits(payload_ty.abiAlignment(mod)); |
| 5742 | 5654 | if (isByRef(payload_ty, mod)) { |
| 5743 | 5655 | if (can_elide_load) |
| 5744 | 5656 | return payload_ptr; |
| 5745 | 5657 | |
| 5746 | | return fg.loadByRef(payload_ptr, payload_ty, payload_ty.abiAlignment(mod), false); |
| 5658 | return fg.loadByRef(payload_ptr, payload_ty, payload_alignment, false); |
| 5747 | 5659 | } |
| 5748 | | const load_inst = fg.builder.buildLoad(err_union_llvm_ty.structGetTypeAtIndex(offset), payload_ptr, ""); |
| 5749 | | load_inst.setAlignment(payload_ty.abiAlignment(mod)); |
| 5750 | | return load_inst; |
| 5660 | const load_ty = err_union_llvm_ty.structFields(&o.builder)[offset]; |
| 5661 | return fg.wip.load(.normal, load_ty, payload_ptr, payload_alignment, ""); |
| 5751 | 5662 | } |
| 5752 | | return fg.builder.buildExtractValue(err_union, offset, ""); |
| 5663 | return fg.wip.extractValue(err_union, &.{offset}, ""); |
| 5753 | 5664 | } |
| 5754 | 5665 | |
| 5755 | | fn airSwitchBr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 5666 | fn airSwitchBr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 5756 | 5667 | const o = self.dg.object; |
| 5757 | 5668 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 5758 | 5669 | const cond = try self.resolveInst(pl_op.operand); |
| 5759 | 5670 | const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload); |
| 5760 | | const else_block = try self.wip.block("Else"); |
| 5761 | | const llvm_usize = (try o.lowerType(Type.usize)).toLlvm(&o.builder); |
| 5762 | | const cond_int = if (cond.typeOf().getTypeKind() == .Pointer) |
| 5763 | | self.builder.buildPtrToInt(cond, llvm_usize, "") |
| 5671 | const else_block = try self.wip.block(1, "Default"); |
| 5672 | const llvm_usize = try o.lowerType(Type.usize); |
| 5673 | const cond_int = if (cond.typeOfWip(&self.wip).isPointer(&o.builder)) |
| 5674 | try self.wip.cast(.ptrtoint, cond, llvm_usize, "") |
| 5764 | 5675 | else |
| 5765 | 5676 | cond; |
| 5766 | | const llvm_switch = self.builder.buildSwitch(cond_int, else_block.toLlvm(&self.wip), switch_br.data.cases_len); |
| 5767 | 5677 | |
| 5768 | 5678 | var extra_index: usize = switch_br.end; |
| 5769 | 5679 | var case_i: u32 = 0; |
| 5680 | var llvm_cases_len: u32 = 0; |
| 5681 | while (case_i < switch_br.data.cases_len) : (case_i += 1) { |
| 5682 | const case = self.air.extraData(Air.SwitchBr.Case, extra_index); |
| 5683 | const items: []const Air.Inst.Ref = |
| 5684 | @ptrCast(self.air.extra[case.end..][0..case.data.items_len]); |
| 5685 | const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; |
| 5686 | extra_index = case.end + case.data.items_len + case_body.len; |
| 5770 | 5687 | |
| 5688 | llvm_cases_len += @intCast(items.len); |
| 5689 | } |
| 5690 | |
| 5691 | var wip_switch = try self.wip.@"switch"(cond_int, else_block, llvm_cases_len); |
| 5692 | defer wip_switch.finish(&self.wip); |
| 5693 | |
| 5694 | extra_index = switch_br.end; |
| 5695 | case_i = 0; |
| 5771 | 5696 | while (case_i < switch_br.data.cases_len) : (case_i += 1) { |
| 5772 | 5697 | const case = self.air.extraData(Air.SwitchBr.Case, extra_index); |
| 5773 | | const items: []const Air.Inst.Ref = @ptrCast(self.air.extra[case.end..][0..case.data.items_len]); |
| 5698 | const items: []const Air.Inst.Ref = |
| 5699 | @ptrCast(self.air.extra[case.end..][0..case.data.items_len]); |
| 5774 | 5700 | const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; |
| 5775 | 5701 | extra_index = case.end + case.data.items_len + case_body.len; |
| 5776 | 5702 | |
| 5777 | | const case_block = try self.wip.block("Case"); |
| 5703 | const case_block = try self.wip.block(@intCast(items.len), "Case"); |
| 5778 | 5704 | |
| 5779 | 5705 | for (items) |item| { |
| 5780 | | const llvm_item = try self.resolveInst(item); |
| 5781 | | const llvm_int_item = if (llvm_item.typeOf().getTypeKind() == .Pointer) |
| 5782 | | llvm_item.constPtrToInt(llvm_usize) |
| 5706 | const llvm_item = (try self.resolveInst(item)).toConst().?; |
| 5707 | const llvm_int_item = if (llvm_item.typeOf(&o.builder).isPointer(&o.builder)) |
| 5708 | try o.builder.castConst(.ptrtoint, llvm_item, llvm_usize) |
| 5783 | 5709 | else |
| 5784 | 5710 | llvm_item; |
| 5785 | | llvm_switch.addCase(llvm_int_item, case_block.toLlvm(&self.wip)); |
| 5711 | try wip_switch.addCase(llvm_int_item, case_block, &self.wip); |
| 5786 | 5712 | } |
| 5787 | 5713 | |
| 5788 | 5714 | self.wip.cursor = .{ .block = case_block }; |
| 5789 | | self.builder.positionBuilderAtEnd(case_block.toLlvm(&self.wip)); |
| 5790 | 5715 | try self.genBody(case_body); |
| 5791 | 5716 | } |
| 5792 | 5717 | |
| 5793 | 5718 | self.wip.cursor = .{ .block = else_block }; |
| 5794 | | self.builder.positionBuilderAtEnd(else_block.toLlvm(&self.wip)); |
| 5795 | 5719 | const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; |
| 5796 | 5720 | if (else_body.len != 0) { |
| 5797 | 5721 | try self.genBody(else_body); |
| 5798 | 5722 | } else { |
| 5799 | | _ = self.builder.buildUnreachable(); |
| 5723 | _ = try self.wip.@"unreachable"(); |
| 5800 | 5724 | } |
| 5801 | 5725 | |
| 5802 | 5726 | // No need to reset the insert cursor since this instruction is noreturn. |
| 5803 | | return null; |
| 5727 | return .none; |
| 5804 | 5728 | } |
| 5805 | 5729 | |
| 5806 | | fn airLoop(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 5730 | fn airLoop(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 5807 | 5731 | const o = self.dg.object; |
| 5808 | 5732 | const mod = o.module; |
| 5809 | 5733 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 5810 | 5734 | const loop = self.air.extraData(Air.Block, ty_pl.payload); |
| 5811 | 5735 | const body = self.air.extra[loop.end..][0..loop.data.body_len]; |
| 5812 | | const loop_block = try self.wip.block("Loop"); |
| 5813 | | _ = self.builder.buildBr(loop_block.toLlvm(&self.wip)); |
| 5736 | const loop_block = try self.wip.block(2, "Loop"); |
| 5737 | _ = try self.wip.br(loop_block); |
| 5814 | 5738 | |
| 5815 | 5739 | self.wip.cursor = .{ .block = loop_block }; |
| 5816 | | self.builder.positionBuilderAtEnd(loop_block.toLlvm(&self.wip)); |
| 5817 | 5740 | try self.genBody(body); |
| 5818 | 5741 | |
| 5819 | 5742 | // TODO instead of this logic, change AIR to have the property that |
| ... | ... | @@ -5823,35 +5746,30 @@ pub const FuncGen = struct { |
| 5823 | 5746 | // be while(true) instead of for(body), which will eliminate 1 branch on |
| 5824 | 5747 | // a hot path. |
| 5825 | 5748 | if (body.len == 0 or !self.typeOfIndex(body[body.len - 1]).isNoReturn(mod)) { |
| 5826 | | _ = self.builder.buildBr(loop_block.toLlvm(&self.wip)); |
| 5749 | _ = try self.wip.br(loop_block); |
| 5827 | 5750 | } |
| 5828 | | return null; |
| 5751 | return .none; |
| 5829 | 5752 | } |
| 5830 | 5753 | |
| 5831 | | fn airArrayToSlice(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 5754 | fn airArrayToSlice(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 5832 | 5755 | const o = self.dg.object; |
| 5833 | 5756 | const mod = o.module; |
| 5834 | 5757 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 5835 | 5758 | const operand_ty = self.typeOf(ty_op.operand); |
| 5836 | 5759 | const array_ty = operand_ty.childType(mod); |
| 5837 | 5760 | const llvm_usize = try o.lowerType(Type.usize); |
| 5838 | | const len = (try o.builder.intConst(llvm_usize, array_ty.arrayLen(mod))).toLlvm(&o.builder); |
| 5839 | | const slice_llvm_ty = (try o.lowerType(self.typeOfIndex(inst))).toLlvm(&o.builder); |
| 5761 | const len = try o.builder.intValue(llvm_usize, array_ty.arrayLen(mod)); |
| 5762 | const slice_llvm_ty = try o.lowerType(self.typeOfIndex(inst)); |
| 5840 | 5763 | const operand = try self.resolveInst(ty_op.operand); |
| 5841 | | if (!array_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 5842 | | const partial = self.builder.buildInsertValue(slice_llvm_ty.getUndef(), operand, 0, ""); |
| 5843 | | return self.builder.buildInsertValue(partial, len, 1, ""); |
| 5844 | | } |
| 5845 | | const indices: [2]*llvm.Value = .{ |
| 5846 | | (try o.builder.intConst(llvm_usize, 0)).toLlvm(&o.builder), |
| 5847 | | } ** 2; |
| 5848 | | const array_llvm_ty = (try o.lowerType(array_ty)).toLlvm(&o.builder); |
| 5849 | | const ptr = self.builder.buildInBoundsGEP(array_llvm_ty, operand, &indices, indices.len, ""); |
| 5850 | | const partial = self.builder.buildInsertValue(slice_llvm_ty.getUndef(), ptr, 0, ""); |
| 5851 | | return self.builder.buildInsertValue(partial, len, 1, ""); |
| 5764 | if (!array_ty.hasRuntimeBitsIgnoreComptime(mod)) |
| 5765 | return self.wip.buildAggregate(slice_llvm_ty, &.{ operand, len }, ""); |
| 5766 | const ptr = try self.wip.gep(.inbounds, try o.lowerType(array_ty), operand, &.{ |
| 5767 | try o.builder.intValue(llvm_usize, 0), try o.builder.intValue(llvm_usize, 0), |
| 5768 | }, ""); |
| 5769 | return self.wip.buildAggregate(slice_llvm_ty, &.{ ptr, len }, ""); |
| 5852 | 5770 | } |
| 5853 | 5771 | |
| 5854 | | fn airFloatFromInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 5772 | fn airFloatFromInt(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 5855 | 5773 | const o = self.dg.object; |
| 5856 | 5774 | const mod = o.module; |
| 5857 | 5775 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| ... | ... | @@ -5865,23 +5783,21 @@ pub const FuncGen = struct { |
| 5865 | 5783 | const dest_llvm_ty = try o.lowerType(dest_ty); |
| 5866 | 5784 | const target = mod.getTarget(); |
| 5867 | 5785 | |
| 5868 | | if (intrinsicsAllowed(dest_scalar_ty, target)) { |
| 5869 | | if (operand_scalar_ty.isSignedInt(mod)) { |
| 5870 | | return self.builder.buildSIToFP(operand, dest_llvm_ty.toLlvm(&o.builder), ""); |
| 5871 | | } else { |
| 5872 | | return self.builder.buildUIToFP(operand, dest_llvm_ty.toLlvm(&o.builder), ""); |
| 5873 | | } |
| 5874 | | } |
| 5786 | if (intrinsicsAllowed(dest_scalar_ty, target)) return self.wip.conv( |
| 5787 | if (operand_scalar_ty.isSignedInt(mod)) .signed else .unsigned, |
| 5788 | operand, |
| 5789 | dest_llvm_ty, |
| 5790 | "", |
| 5791 | ); |
| 5875 | 5792 | |
| 5876 | 5793 | const rt_int_bits = compilerRtIntBits(@intCast(operand_scalar_ty.bitSize(mod))); |
| 5877 | 5794 | const rt_int_ty = try o.builder.intType(rt_int_bits); |
| 5878 | | var extended = e: { |
| 5879 | | if (operand_scalar_ty.isSignedInt(mod)) { |
| 5880 | | break :e self.builder.buildSExtOrBitCast(operand, rt_int_ty.toLlvm(&o.builder), ""); |
| 5881 | | } else { |
| 5882 | | break :e self.builder.buildZExtOrBitCast(operand, rt_int_ty.toLlvm(&o.builder), ""); |
| 5883 | | } |
| 5884 | | }; |
| 5795 | var extended = try self.wip.conv( |
| 5796 | if (operand_scalar_ty.isSignedInt(mod)) .signed else .unsigned, |
| 5797 | operand, |
| 5798 | rt_int_ty, |
| 5799 | "", |
| 5800 | ); |
| 5885 | 5801 | const dest_bits = dest_scalar_ty.floatBits(target); |
| 5886 | 5802 | const compiler_rt_operand_abbrev = compilerRtIntAbbrev(rt_int_bits); |
| 5887 | 5803 | const compiler_rt_dest_abbrev = compilerRtFloatAbbrev(dest_bits); |
| ... | ... | @@ -5897,16 +5813,23 @@ pub const FuncGen = struct { |
| 5897 | 5813 | // On Windows x86-64, "ti" functions must use Vector(2, u64) instead of the standard |
| 5898 | 5814 | // i128 calling convention to adhere to the ABI that LLVM expects compiler-rt to have. |
| 5899 | 5815 | param_type = try o.builder.vectorType(.normal, 2, .i64); |
| 5900 | | extended = self.builder.buildBitCast(extended, param_type.toLlvm(&o.builder), ""); |
| 5816 | extended = try self.wip.cast(.bitcast, extended, param_type, ""); |
| 5901 | 5817 | } |
| 5902 | 5818 | |
| 5903 | 5819 | const libc_fn = try self.getLibcFunction(fn_name, &.{param_type}, dest_llvm_ty); |
| 5904 | | const params = [1]*llvm.Value{extended}; |
| 5905 | | |
| 5906 | | return self.builder.buildCall(libc_fn.globalGetValueType(), libc_fn, &params, params.len, .C, .Auto, ""); |
| 5820 | const params = [1]*llvm.Value{extended.toLlvm(&self.wip)}; |
| 5821 | return (try self.wip.unimplemented(dest_llvm_ty, "")).finish(self.builder.buildCall( |
| 5822 | libc_fn.typeOf(&o.builder).toLlvm(&o.builder), |
| 5823 | libc_fn.toLlvm(&o.builder), |
| 5824 | &params, |
| 5825 | params.len, |
| 5826 | .C, |
| 5827 | .Auto, |
| 5828 | "", |
| 5829 | ), &self.wip); |
| 5907 | 5830 | } |
| 5908 | 5831 | |
| 5909 | | fn airIntFromFloat(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value { |
| 5832 | fn airIntFromFloat(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !Builder.Value { |
| 5910 | 5833 | self.builder.setFastMath(want_fast_math); |
| 5911 | 5834 | |
| 5912 | 5835 | const o = self.dg.object; |
| ... | ... | @@ -5924,11 +5847,12 @@ pub const FuncGen = struct { |
| 5924 | 5847 | |
| 5925 | 5848 | if (intrinsicsAllowed(operand_scalar_ty, target)) { |
| 5926 | 5849 | // TODO set fast math flag |
| 5927 | | if (dest_scalar_ty.isSignedInt(mod)) { |
| 5928 | | return self.builder.buildFPToSI(operand, dest_llvm_ty.toLlvm(&o.builder), ""); |
| 5929 | | } else { |
| 5930 | | return self.builder.buildFPToUI(operand, dest_llvm_ty.toLlvm(&o.builder), ""); |
| 5931 | | } |
| 5850 | return self.wip.conv( |
| 5851 | if (dest_scalar_ty.isSignedInt(mod)) .signed else .unsigned, |
| 5852 | operand, |
| 5853 | dest_llvm_ty, |
| 5854 | "", |
| 5855 | ); |
| 5932 | 5856 | } |
| 5933 | 5857 | |
| 5934 | 5858 | const rt_int_bits = compilerRtIntBits(@intCast(dest_scalar_ty.bitSize(mod))); |
| ... | ... | @@ -5953,66 +5877,69 @@ pub const FuncGen = struct { |
| 5953 | 5877 | |
| 5954 | 5878 | const operand_llvm_ty = try o.lowerType(operand_ty); |
| 5955 | 5879 | const libc_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, libc_ret_ty); |
| 5956 | | const params = [1]*llvm.Value{operand}; |
| 5957 | | |
| 5958 | | var result = self.builder.buildCall(libc_fn.globalGetValueType(), libc_fn, &params, params.len, .C, .Auto, ""); |
| 5880 | const params = [1]*llvm.Value{operand.toLlvm(&self.wip)}; |
| 5881 | var result = (try self.wip.unimplemented(libc_ret_ty, "")).finish(self.builder.buildCall( |
| 5882 | libc_fn.typeOf(&o.builder).toLlvm(&o.builder), |
| 5883 | libc_fn.toLlvm(&o.builder), |
| 5884 | &params, |
| 5885 | params.len, |
| 5886 | .C, |
| 5887 | .Auto, |
| 5888 | "", |
| 5889 | ), &self.wip); |
| 5959 | 5890 | |
| 5960 | | if (libc_ret_ty != ret_ty) result = self.builder.buildBitCast(result, ret_ty.toLlvm(&o.builder), ""); |
| 5961 | | if (ret_ty != dest_llvm_ty) result = self.builder.buildTrunc(result, dest_llvm_ty.toLlvm(&o.builder), ""); |
| 5891 | if (libc_ret_ty != ret_ty) result = try self.wip.cast(.bitcast, result, ret_ty, ""); |
| 5892 | if (ret_ty != dest_llvm_ty) result = try self.wip.cast(.trunc, result, dest_llvm_ty, ""); |
| 5962 | 5893 | return result; |
| 5963 | 5894 | } |
| 5964 | 5895 | |
| 5965 | | fn sliceOrArrayPtr(fg: *FuncGen, ptr: *llvm.Value, ty: Type) *llvm.Value { |
| 5896 | fn sliceOrArrayPtr(fg: *FuncGen, ptr: Builder.Value, ty: Type) Allocator.Error!Builder.Value { |
| 5966 | 5897 | const o = fg.dg.object; |
| 5967 | 5898 | const mod = o.module; |
| 5968 | | if (ty.isSlice(mod)) { |
| 5969 | | return fg.builder.buildExtractValue(ptr, 0, ""); |
| 5970 | | } else { |
| 5971 | | return ptr; |
| 5972 | | } |
| 5899 | return if (ty.isSlice(mod)) fg.wip.extractValue(ptr, &.{0}, "") else ptr; |
| 5973 | 5900 | } |
| 5974 | 5901 | |
| 5975 | | fn sliceOrArrayLenInBytes(fg: *FuncGen, ptr: *llvm.Value, ty: Type) Allocator.Error!*llvm.Value { |
| 5902 | fn sliceOrArrayLenInBytes(fg: *FuncGen, ptr: Builder.Value, ty: Type) Allocator.Error!Builder.Value { |
| 5976 | 5903 | const o = fg.dg.object; |
| 5977 | 5904 | const mod = o.module; |
| 5978 | 5905 | const llvm_usize = try o.lowerType(Type.usize); |
| 5979 | 5906 | switch (ty.ptrSize(mod)) { |
| 5980 | 5907 | .Slice => { |
| 5981 | | const len = fg.builder.buildExtractValue(ptr, 1, ""); |
| 5908 | const len = try fg.wip.extractValue(ptr, &.{1}, ""); |
| 5982 | 5909 | const elem_ty = ty.childType(mod); |
| 5983 | 5910 | const abi_size = elem_ty.abiSize(mod); |
| 5984 | 5911 | if (abi_size == 1) return len; |
| 5985 | | const abi_size_llvm_val = try o.builder.intConst(llvm_usize, abi_size); |
| 5986 | | return fg.builder.buildMul(len, abi_size_llvm_val.toLlvm(&o.builder), ""); |
| 5912 | const abi_size_llvm_val = try o.builder.intValue(llvm_usize, abi_size); |
| 5913 | return fg.wip.bin(.@"mul nuw", len, abi_size_llvm_val, ""); |
| 5987 | 5914 | }, |
| 5988 | 5915 | .One => { |
| 5989 | 5916 | const array_ty = ty.childType(mod); |
| 5990 | 5917 | const elem_ty = array_ty.childType(mod); |
| 5991 | 5918 | const abi_size = elem_ty.abiSize(mod); |
| 5992 | | return (try o.builder.intConst(llvm_usize, array_ty.arrayLen(mod) * abi_size)).toLlvm(&o.builder); |
| 5919 | return o.builder.intValue(llvm_usize, array_ty.arrayLen(mod) * abi_size); |
| 5993 | 5920 | }, |
| 5994 | 5921 | .Many, .C => unreachable, |
| 5995 | 5922 | } |
| 5996 | 5923 | } |
| 5997 | 5924 | |
| 5998 | | fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*llvm.Value { |
| 5925 | fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: u32) !Builder.Value { |
| 5999 | 5926 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 6000 | 5927 | const operand = try self.resolveInst(ty_op.operand); |
| 6001 | | return self.builder.buildExtractValue(operand, index, ""); |
| 5928 | return self.wip.extractValue(operand, &.{index}, ""); |
| 6002 | 5929 | } |
| 6003 | 5930 | |
| 6004 | | fn airPtrSliceFieldPtr(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*llvm.Value { |
| 5931 | fn airPtrSliceFieldPtr(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !Builder.Value { |
| 6005 | 5932 | const o = self.dg.object; |
| 6006 | 5933 | const mod = o.module; |
| 6007 | 5934 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 6008 | 5935 | const slice_ptr = try self.resolveInst(ty_op.operand); |
| 6009 | 5936 | const slice_ptr_ty = self.typeOf(ty_op.operand); |
| 6010 | | const slice_llvm_ty = (try o.lowerPtrElemTy(slice_ptr_ty.childType(mod))).toLlvm(&o.builder); |
| 5937 | const slice_llvm_ty = try o.lowerPtrElemTy(slice_ptr_ty.childType(mod)); |
| 6011 | 5938 | |
| 6012 | | return self.builder.buildStructGEP(slice_llvm_ty, slice_ptr, index, ""); |
| 5939 | return self.wip.gepStruct(slice_llvm_ty, slice_ptr, index, ""); |
| 6013 | 5940 | } |
| 6014 | 5941 | |
| 6015 | | fn airSliceElemVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value { |
| 5942 | fn airSliceElemVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !Builder.Value { |
| 6016 | 5943 | const o = self.dg.object; |
| 6017 | 5944 | const mod = o.module; |
| 6018 | 5945 | const inst = body_tail[0]; |
| ... | ... | @@ -6021,21 +5948,21 @@ pub const FuncGen = struct { |
| 6021 | 5948 | const slice = try self.resolveInst(bin_op.lhs); |
| 6022 | 5949 | const index = try self.resolveInst(bin_op.rhs); |
| 6023 | 5950 | const elem_ty = slice_ty.childType(mod); |
| 6024 | | const llvm_elem_ty = (try o.lowerPtrElemTy(elem_ty)).toLlvm(&o.builder); |
| 6025 | | const base_ptr = self.builder.buildExtractValue(slice, 0, ""); |
| 6026 | | const indices: [1]*llvm.Value = .{index}; |
| 6027 | | const ptr = self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, ""); |
| 5951 | const llvm_elem_ty = try o.lowerPtrElemTy(elem_ty); |
| 5952 | const base_ptr = try self.wip.extractValue(slice, &.{0}, ""); |
| 5953 | const ptr = try self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, &.{index}, ""); |
| 6028 | 5954 | if (isByRef(elem_ty, mod)) { |
| 6029 | 5955 | if (self.canElideLoad(body_tail)) |
| 6030 | 5956 | return ptr; |
| 6031 | 5957 | |
| 6032 | | return self.loadByRef(ptr, elem_ty, elem_ty.abiAlignment(mod), false); |
| 5958 | const elem_alignment = Builder.Alignment.fromByteUnits(elem_ty.abiAlignment(mod)); |
| 5959 | return self.loadByRef(ptr, elem_ty, elem_alignment, false); |
| 6033 | 5960 | } |
| 6034 | 5961 | |
| 6035 | 5962 | return self.load(ptr, slice_ty); |
| 6036 | 5963 | } |
| 6037 | 5964 | |
| 6038 | | fn airSliceElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 5965 | fn airSliceElemPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 6039 | 5966 | const o = self.dg.object; |
| 6040 | 5967 | const mod = o.module; |
| 6041 | 5968 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| ... | ... | @@ -6044,13 +5971,12 @@ pub const FuncGen = struct { |
| 6044 | 5971 | |
| 6045 | 5972 | const slice = try self.resolveInst(bin_op.lhs); |
| 6046 | 5973 | const index = try self.resolveInst(bin_op.rhs); |
| 6047 | | const llvm_elem_ty = (try o.lowerPtrElemTy(slice_ty.childType(mod))).toLlvm(&o.builder); |
| 6048 | | const base_ptr = self.builder.buildExtractValue(slice, 0, ""); |
| 6049 | | const indices: [1]*llvm.Value = .{index}; |
| 6050 | | return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, ""); |
| 5974 | const llvm_elem_ty = try o.lowerPtrElemTy(slice_ty.childType(mod)); |
| 5975 | const base_ptr = try self.wip.extractValue(slice, &.{0}, ""); |
| 5976 | return self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, &.{index}, ""); |
| 6051 | 5977 | } |
| 6052 | 5978 | |
| 6053 | | fn airArrayElemVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value { |
| 5979 | fn airArrayElemVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !Builder.Value { |
| 6054 | 5980 | const o = self.dg.object; |
| 6055 | 5981 | const mod = o.module; |
| 6056 | 5982 | const inst = body_tail[0]; |
| ... | ... | @@ -6059,21 +5985,20 @@ pub const FuncGen = struct { |
| 6059 | 5985 | const array_ty = self.typeOf(bin_op.lhs); |
| 6060 | 5986 | const array_llvm_val = try self.resolveInst(bin_op.lhs); |
| 6061 | 5987 | const rhs = try self.resolveInst(bin_op.rhs); |
| 6062 | | const array_llvm_ty = (try o.lowerType(array_ty)).toLlvm(&o.builder); |
| 5988 | const array_llvm_ty = try o.lowerType(array_ty); |
| 6063 | 5989 | const elem_ty = array_ty.childType(mod); |
| 6064 | 5990 | if (isByRef(array_ty, mod)) { |
| 6065 | | const indices: [2]*llvm.Value = .{ |
| 6066 | | (try o.builder.intConst(.i32, 0)).toLlvm(&o.builder), |
| 6067 | | rhs, |
| 5991 | const indices: [2]Builder.Value = .{ |
| 5992 | try o.builder.intValue(try o.lowerType(Type.usize), 0), rhs, |
| 6068 | 5993 | }; |
| 6069 | 5994 | if (isByRef(elem_ty, mod)) { |
| 6070 | | const elem_ptr = self.builder.buildInBoundsGEP(array_llvm_ty, array_llvm_val, &indices, indices.len, ""); |
| 6071 | | if (canElideLoad(self, body_tail)) |
| 6072 | | return elem_ptr; |
| 6073 | | |
| 6074 | | return self.loadByRef(elem_ptr, elem_ty, elem_ty.abiAlignment(mod), false); |
| 5995 | const elem_ptr = |
| 5996 | try self.wip.gep(.inbounds, array_llvm_ty, array_llvm_val, &indices, ""); |
| 5997 | if (canElideLoad(self, body_tail)) return elem_ptr; |
| 5998 | const elem_alignment = Builder.Alignment.fromByteUnits(elem_ty.abiAlignment(mod)); |
| 5999 | return self.loadByRef(elem_ptr, elem_ty, elem_alignment, false); |
| 6075 | 6000 | } else { |
| 6076 | | const elem_llvm_ty = (try o.lowerType(elem_ty)).toLlvm(&o.builder); |
| 6001 | const elem_llvm_ty = try o.lowerType(elem_ty); |
| 6077 | 6002 | if (Air.refToIndex(bin_op.lhs)) |lhs_index| { |
| 6078 | 6003 | if (self.air.instructions.items(.tag)[lhs_index] == .load) { |
| 6079 | 6004 | const load_data = self.air.instructions.items(.data)[lhs_index]; |
| ... | ... | @@ -6081,66 +6006,70 @@ pub const FuncGen = struct { |
| 6081 | 6006 | if (Air.refToIndex(load_ptr)) |load_ptr_index| { |
| 6082 | 6007 | const load_ptr_tag = self.air.instructions.items(.tag)[load_ptr_index]; |
| 6083 | 6008 | switch (load_ptr_tag) { |
| 6084 | | .struct_field_ptr, .struct_field_ptr_index_0, .struct_field_ptr_index_1, .struct_field_ptr_index_2, .struct_field_ptr_index_3 => { |
| 6009 | .struct_field_ptr, |
| 6010 | .struct_field_ptr_index_0, |
| 6011 | .struct_field_ptr_index_1, |
| 6012 | .struct_field_ptr_index_2, |
| 6013 | .struct_field_ptr_index_3, |
| 6014 | => { |
| 6085 | 6015 | const load_ptr_inst = try self.resolveInst(load_ptr); |
| 6086 | | const gep = self.builder.buildInBoundsGEP(array_llvm_ty, load_ptr_inst, &indices, indices.len, ""); |
| 6087 | | return self.builder.buildLoad(elem_llvm_ty, gep, ""); |
| 6016 | const gep = try self.wip.gep( |
| 6017 | .inbounds, |
| 6018 | array_llvm_ty, |
| 6019 | load_ptr_inst, |
| 6020 | &indices, |
| 6021 | "", |
| 6022 | ); |
| 6023 | return self.wip.load(.normal, elem_llvm_ty, gep, .default, ""); |
| 6088 | 6024 | }, |
| 6089 | 6025 | else => {}, |
| 6090 | 6026 | } |
| 6091 | 6027 | } |
| 6092 | 6028 | } |
| 6093 | 6029 | } |
| 6094 | | const elem_ptr = self.builder.buildInBoundsGEP(array_llvm_ty, array_llvm_val, &indices, indices.len, ""); |
| 6095 | | return self.builder.buildLoad(elem_llvm_ty, elem_ptr, ""); |
| 6030 | const elem_ptr = |
| 6031 | try self.wip.gep(.inbounds, array_llvm_ty, array_llvm_val, &indices, ""); |
| 6032 | return self.wip.load(.normal, elem_llvm_ty, elem_ptr, .default, ""); |
| 6096 | 6033 | } |
| 6097 | 6034 | } |
| 6098 | 6035 | |
| 6099 | 6036 | // This branch can be reached for vectors, which are always by-value. |
| 6100 | | return self.builder.buildExtractElement(array_llvm_val, rhs, ""); |
| 6037 | return self.wip.extractElement(array_llvm_val, rhs, ""); |
| 6101 | 6038 | } |
| 6102 | 6039 | |
| 6103 | | fn airPtrElemVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value { |
| 6040 | fn airPtrElemVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !Builder.Value { |
| 6104 | 6041 | const o = self.dg.object; |
| 6105 | 6042 | const mod = o.module; |
| 6106 | 6043 | const inst = body_tail[0]; |
| 6107 | 6044 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 6108 | 6045 | const ptr_ty = self.typeOf(bin_op.lhs); |
| 6109 | 6046 | const elem_ty = ptr_ty.childType(mod); |
| 6110 | | const llvm_elem_ty = (try o.lowerPtrElemTy(elem_ty)).toLlvm(&o.builder); |
| 6047 | const llvm_elem_ty = try o.lowerPtrElemTy(elem_ty); |
| 6111 | 6048 | const base_ptr = try self.resolveInst(bin_op.lhs); |
| 6112 | 6049 | const rhs = try self.resolveInst(bin_op.rhs); |
| 6113 | 6050 | // TODO: when we go fully opaque pointers in LLVM 16 we can remove this branch |
| 6114 | | const ptr = if (ptr_ty.isSinglePointer(mod)) ptr: { |
| 6051 | const ptr = try self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, if (ptr_ty.isSinglePointer(mod)) |
| 6115 | 6052 | // If this is a single-item pointer to an array, we need another index in the GEP. |
| 6116 | | const indices: [2]*llvm.Value = .{ |
| 6117 | | (try o.builder.intConst(.i32, 0)).toLlvm(&o.builder), |
| 6118 | | rhs, |
| 6119 | | }; |
| 6120 | | break :ptr self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, ""); |
| 6121 | | } else ptr: { |
| 6122 | | const indices: [1]*llvm.Value = .{rhs}; |
| 6123 | | break :ptr self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, ""); |
| 6124 | | }; |
| 6053 | &.{ try o.builder.intValue(try o.lowerType(Type.usize), 0), rhs } |
| 6054 | else |
| 6055 | &.{rhs}, ""); |
| 6125 | 6056 | if (isByRef(elem_ty, mod)) { |
| 6126 | | if (self.canElideLoad(body_tail)) |
| 6127 | | return ptr; |
| 6128 | | |
| 6129 | | return self.loadByRef(ptr, elem_ty, elem_ty.abiAlignment(mod), false); |
| 6057 | if (self.canElideLoad(body_tail)) return ptr; |
| 6058 | const elem_alignment = Builder.Alignment.fromByteUnits(elem_ty.abiAlignment(mod)); |
| 6059 | return self.loadByRef(ptr, elem_ty, elem_alignment, false); |
| 6130 | 6060 | } |
| 6131 | 6061 | |
| 6132 | 6062 | return self.load(ptr, ptr_ty); |
| 6133 | 6063 | } |
| 6134 | 6064 | |
| 6135 | | fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 6065 | fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 6136 | 6066 | const o = self.dg.object; |
| 6137 | 6067 | const mod = o.module; |
| 6138 | 6068 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 6139 | 6069 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 6140 | 6070 | const ptr_ty = self.typeOf(bin_op.lhs); |
| 6141 | 6071 | const elem_ty = ptr_ty.childType(mod); |
| 6142 | | if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) |
| 6143 | | return (try o.lowerPtrToVoid(ptr_ty)).toLlvm(&o.builder); |
| 6072 | if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) return (try o.lowerPtrToVoid(ptr_ty)).toValue(); |
| 6144 | 6073 | |
| 6145 | 6074 | const base_ptr = try self.resolveInst(bin_op.lhs); |
| 6146 | 6075 | const rhs = try self.resolveInst(bin_op.rhs); |
| ... | ... | @@ -6148,21 +6077,15 @@ pub const FuncGen = struct { |
| 6148 | 6077 | const elem_ptr = self.air.getRefType(ty_pl.ty); |
| 6149 | 6078 | if (elem_ptr.ptrInfo(mod).flags.vector_index != .none) return base_ptr; |
| 6150 | 6079 | |
| 6151 | | const llvm_elem_ty = (try o.lowerPtrElemTy(elem_ty)).toLlvm(&o.builder); |
| 6152 | | if (ptr_ty.isSinglePointer(mod)) { |
| 6080 | const llvm_elem_ty = try o.lowerPtrElemTy(elem_ty); |
| 6081 | return try self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, if (ptr_ty.isSinglePointer(mod)) |
| 6153 | 6082 | // If this is a single-item pointer to an array, we need another index in the GEP. |
| 6154 | | const indices: [2]*llvm.Value = .{ |
| 6155 | | (try o.builder.intConst(.i32, 0)).toLlvm(&o.builder), |
| 6156 | | rhs, |
| 6157 | | }; |
| 6158 | | return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, ""); |
| 6159 | | } else { |
| 6160 | | const indices: [1]*llvm.Value = .{rhs}; |
| 6161 | | return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, ""); |
| 6162 | | } |
| 6083 | &.{ try o.builder.intValue(try o.lowerType(Type.usize), 0), rhs } |
| 6084 | else |
| 6085 | &.{rhs}, ""); |
| 6163 | 6086 | } |
| 6164 | 6087 | |
| 6165 | | fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 6088 | fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 6166 | 6089 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 6167 | 6090 | const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 6168 | 6091 | const struct_ptr = try self.resolveInst(struct_field.struct_operand); |
| ... | ... | @@ -6174,14 +6097,14 @@ pub const FuncGen = struct { |
| 6174 | 6097 | self: *FuncGen, |
| 6175 | 6098 | inst: Air.Inst.Index, |
| 6176 | 6099 | field_index: u32, |
| 6177 | | ) !?*llvm.Value { |
| 6100 | ) !Builder.Value { |
| 6178 | 6101 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 6179 | 6102 | const struct_ptr = try self.resolveInst(ty_op.operand); |
| 6180 | 6103 | const struct_ptr_ty = self.typeOf(ty_op.operand); |
| 6181 | 6104 | return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, field_index); |
| 6182 | 6105 | } |
| 6183 | 6106 | |
| 6184 | | fn airStructFieldVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value { |
| 6107 | fn airStructFieldVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !Builder.Value { |
| 6185 | 6108 | const o = self.dg.object; |
| 6186 | 6109 | const mod = o.module; |
| 6187 | 6110 | const inst = body_tail[0]; |
| ... | ... | @@ -6191,9 +6114,7 @@ pub const FuncGen = struct { |
| 6191 | 6114 | const struct_llvm_val = try self.resolveInst(struct_field.struct_operand); |
| 6192 | 6115 | const field_index = struct_field.field_index; |
| 6193 | 6116 | const field_ty = struct_ty.structFieldType(field_index, mod); |
| 6194 | | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 6195 | | return null; |
| 6196 | | } |
| 6117 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) return .none; |
| 6197 | 6118 | |
| 6198 | 6119 | if (!isByRef(struct_ty, mod)) { |
| 6199 | 6120 | assert(!isByRef(field_ty, mod)); |
| ... | ... | @@ -6203,39 +6124,44 @@ pub const FuncGen = struct { |
| 6203 | 6124 | const struct_obj = mod.typeToStruct(struct_ty).?; |
| 6204 | 6125 | const bit_offset = struct_obj.packedFieldBitOffset(mod, field_index); |
| 6205 | 6126 | const containing_int = struct_llvm_val; |
| 6206 | | const shift_amt = containing_int.typeOf().constInt(bit_offset, .False); |
| 6207 | | const shifted_value = self.builder.buildLShr(containing_int, shift_amt, ""); |
| 6208 | | const elem_llvm_ty = (try o.lowerType(field_ty)).toLlvm(&o.builder); |
| 6127 | const shift_amt = |
| 6128 | try o.builder.intValue(containing_int.typeOfWip(&self.wip), bit_offset); |
| 6129 | const shifted_value = try self.wip.bin(.lshr, containing_int, shift_amt, ""); |
| 6130 | const elem_llvm_ty = try o.lowerType(field_ty); |
| 6209 | 6131 | if (field_ty.zigTypeTag(mod) == .Float or field_ty.zigTypeTag(mod) == .Vector) { |
| 6210 | | const same_size_int = (try o.builder.intType(@intCast(field_ty.bitSize(mod)))).toLlvm(&o.builder); |
| 6211 | | const truncated_int = self.builder.buildTrunc(shifted_value, same_size_int, ""); |
| 6212 | | return self.builder.buildBitCast(truncated_int, elem_llvm_ty, ""); |
| 6132 | const same_size_int = try o.builder.intType(@intCast(field_ty.bitSize(mod))); |
| 6133 | const truncated_int = |
| 6134 | try self.wip.cast(.trunc, shifted_value, same_size_int, ""); |
| 6135 | return self.wip.cast(.bitcast, truncated_int, elem_llvm_ty, ""); |
| 6213 | 6136 | } else if (field_ty.isPtrAtRuntime(mod)) { |
| 6214 | | const same_size_int = (try o.builder.intType(@intCast(field_ty.bitSize(mod)))).toLlvm(&o.builder); |
| 6215 | | const truncated_int = self.builder.buildTrunc(shifted_value, same_size_int, ""); |
| 6216 | | return self.builder.buildIntToPtr(truncated_int, elem_llvm_ty, ""); |
| 6137 | const same_size_int = try o.builder.intType(@intCast(field_ty.bitSize(mod))); |
| 6138 | const truncated_int = |
| 6139 | try self.wip.cast(.trunc, shifted_value, same_size_int, ""); |
| 6140 | return self.wip.cast(.inttoptr, truncated_int, elem_llvm_ty, ""); |
| 6217 | 6141 | } |
| 6218 | | return self.builder.buildTrunc(shifted_value, elem_llvm_ty, ""); |
| 6142 | return self.wip.cast(.trunc, shifted_value, elem_llvm_ty, ""); |
| 6219 | 6143 | }, |
| 6220 | 6144 | else => { |
| 6221 | 6145 | const llvm_field_index = llvmField(struct_ty, field_index, mod).?.index; |
| 6222 | | return self.builder.buildExtractValue(struct_llvm_val, llvm_field_index, ""); |
| 6146 | return self.wip.extractValue(struct_llvm_val, &.{llvm_field_index}, ""); |
| 6223 | 6147 | }, |
| 6224 | 6148 | }, |
| 6225 | 6149 | .Union => { |
| 6226 | 6150 | assert(struct_ty.containerLayout(mod) == .Packed); |
| 6227 | 6151 | const containing_int = struct_llvm_val; |
| 6228 | | const elem_llvm_ty = (try o.lowerType(field_ty)).toLlvm(&o.builder); |
| 6152 | const elem_llvm_ty = try o.lowerType(field_ty); |
| 6229 | 6153 | if (field_ty.zigTypeTag(mod) == .Float or field_ty.zigTypeTag(mod) == .Vector) { |
| 6230 | | const same_size_int = (try o.builder.intType(@intCast(field_ty.bitSize(mod)))).toLlvm(&o.builder); |
| 6231 | | const truncated_int = self.builder.buildTrunc(containing_int, same_size_int, ""); |
| 6232 | | return self.builder.buildBitCast(truncated_int, elem_llvm_ty, ""); |
| 6154 | const same_size_int = try o.builder.intType(@intCast(field_ty.bitSize(mod))); |
| 6155 | const truncated_int = |
| 6156 | try self.wip.cast(.trunc, containing_int, same_size_int, ""); |
| 6157 | return self.wip.cast(.bitcast, truncated_int, elem_llvm_ty, ""); |
| 6233 | 6158 | } else if (field_ty.isPtrAtRuntime(mod)) { |
| 6234 | | const same_size_int = (try o.builder.intType(@intCast(field_ty.bitSize(mod)))).toLlvm(&o.builder); |
| 6235 | | const truncated_int = self.builder.buildTrunc(containing_int, same_size_int, ""); |
| 6236 | | return self.builder.buildIntToPtr(truncated_int, elem_llvm_ty, ""); |
| 6159 | const same_size_int = try o.builder.intType(@intCast(field_ty.bitSize(mod))); |
| 6160 | const truncated_int = |
| 6161 | try self.wip.cast(.trunc, containing_int, same_size_int, ""); |
| 6162 | return self.wip.cast(.inttoptr, truncated_int, elem_llvm_ty, ""); |
| 6237 | 6163 | } |
| 6238 | | return self.builder.buildTrunc(containing_int, elem_llvm_ty, ""); |
| 6164 | return self.wip.cast(.trunc, containing_int, elem_llvm_ty, ""); |
| 6239 | 6165 | }, |
| 6240 | 6166 | else => unreachable, |
| 6241 | 6167 | } |
| ... | ... | @@ -6245,8 +6171,9 @@ pub const FuncGen = struct { |
| 6245 | 6171 | .Struct => { |
| 6246 | 6172 | assert(struct_ty.containerLayout(mod) != .Packed); |
| 6247 | 6173 | const llvm_field = llvmField(struct_ty, field_index, mod).?; |
| 6248 | | const struct_llvm_ty = (try o.lowerType(struct_ty)).toLlvm(&o.builder); |
| 6249 | | const field_ptr = self.builder.buildStructGEP(struct_llvm_ty, struct_llvm_val, llvm_field.index, ""); |
| 6174 | const struct_llvm_ty = try o.lowerType(struct_ty); |
| 6175 | const field_ptr = |
| 6176 | try self.wip.gepStruct(struct_llvm_ty, struct_llvm_val, llvm_field.index, ""); |
| 6250 | 6177 | const field_ptr_ty = try mod.ptrType(.{ |
| 6251 | 6178 | .child = llvm_field.ty.toIntern(), |
| 6252 | 6179 | .flags = .{ |
| ... | ... | @@ -6258,31 +6185,32 @@ pub const FuncGen = struct { |
| 6258 | 6185 | return field_ptr; |
| 6259 | 6186 | |
| 6260 | 6187 | assert(llvm_field.alignment != 0); |
| 6261 | | return self.loadByRef(field_ptr, field_ty, llvm_field.alignment, false); |
| 6188 | const field_alignment = Builder.Alignment.fromByteUnits(llvm_field.alignment); |
| 6189 | return self.loadByRef(field_ptr, field_ty, field_alignment, false); |
| 6262 | 6190 | } else { |
| 6263 | 6191 | return self.load(field_ptr, field_ptr_ty); |
| 6264 | 6192 | } |
| 6265 | 6193 | }, |
| 6266 | 6194 | .Union => { |
| 6267 | | const union_llvm_ty = (try o.lowerType(struct_ty)).toLlvm(&o.builder); |
| 6195 | const union_llvm_ty = try o.lowerType(struct_ty); |
| 6268 | 6196 | const layout = struct_ty.unionGetLayout(mod); |
| 6269 | 6197 | const payload_index = @intFromBool(layout.tag_align >= layout.payload_align); |
| 6270 | | const field_ptr = self.builder.buildStructGEP(union_llvm_ty, struct_llvm_val, payload_index, ""); |
| 6271 | | const llvm_field_ty = (try o.lowerType(field_ty)).toLlvm(&o.builder); |
| 6198 | const field_ptr = |
| 6199 | try self.wip.gepStruct(union_llvm_ty, struct_llvm_val, payload_index, ""); |
| 6200 | const llvm_field_ty = try o.lowerType(field_ty); |
| 6201 | const payload_alignment = Builder.Alignment.fromByteUnits(layout.payload_align); |
| 6272 | 6202 | if (isByRef(field_ty, mod)) { |
| 6273 | | if (canElideLoad(self, body_tail)) |
| 6274 | | return field_ptr; |
| 6275 | | |
| 6276 | | return self.loadByRef(field_ptr, field_ty, layout.payload_align, false); |
| 6203 | if (canElideLoad(self, body_tail)) return field_ptr; |
| 6204 | return self.loadByRef(field_ptr, field_ty, payload_alignment, false); |
| 6277 | 6205 | } else { |
| 6278 | | return self.builder.buildLoad(llvm_field_ty, field_ptr, ""); |
| 6206 | return self.wip.load(.normal, llvm_field_ty, field_ptr, payload_alignment, ""); |
| 6279 | 6207 | } |
| 6280 | 6208 | }, |
| 6281 | 6209 | else => unreachable, |
| 6282 | 6210 | } |
| 6283 | 6211 | } |
| 6284 | 6212 | |
| 6285 | | fn airFieldParentPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 6213 | fn airFieldParentPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 6286 | 6214 | const o = self.dg.object; |
| 6287 | 6215 | const mod = o.module; |
| 6288 | 6216 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| ... | ... | @@ -6292,33 +6220,36 @@ pub const FuncGen = struct { |
| 6292 | 6220 | |
| 6293 | 6221 | const parent_ty = self.air.getRefType(ty_pl.ty).childType(mod); |
| 6294 | 6222 | const field_offset = parent_ty.structFieldOffset(extra.field_index, mod); |
| 6223 | if (field_offset == 0) return field_ptr; |
| 6295 | 6224 | |
| 6296 | | const res_ty = (try o.lowerType(self.air.getRefType(ty_pl.ty))).toLlvm(&o.builder); |
| 6297 | | if (field_offset == 0) { |
| 6298 | | return field_ptr; |
| 6299 | | } |
| 6225 | const res_ty = try o.lowerType(self.air.getRefType(ty_pl.ty)); |
| 6300 | 6226 | const llvm_usize = try o.lowerType(Type.usize); |
| 6301 | 6227 | |
| 6302 | | const field_ptr_int = self.builder.buildPtrToInt(field_ptr, llvm_usize.toLlvm(&o.builder), ""); |
| 6303 | | const base_ptr_int = self.builder.buildNUWSub(field_ptr_int, (try o.builder.intConst(llvm_usize, field_offset)).toLlvm(&o.builder), ""); |
| 6304 | | return self.builder.buildIntToPtr(base_ptr_int, res_ty, ""); |
| 6228 | const field_ptr_int = try self.wip.cast(.ptrtoint, field_ptr, llvm_usize, ""); |
| 6229 | const base_ptr_int = try self.wip.bin( |
| 6230 | .@"sub nuw", |
| 6231 | field_ptr_int, |
| 6232 | try o.builder.intValue(llvm_usize, field_offset), |
| 6233 | "", |
| 6234 | ); |
| 6235 | return self.wip.cast(.inttoptr, base_ptr_int, res_ty, ""); |
| 6305 | 6236 | } |
| 6306 | 6237 | |
| 6307 | | fn airNot(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 6238 | fn airNot(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 6308 | 6239 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 6309 | 6240 | const operand = try self.resolveInst(ty_op.operand); |
| 6310 | 6241 | |
| 6311 | | return self.builder.buildNot(operand, ""); |
| 6242 | return self.wip.not(operand, ""); |
| 6312 | 6243 | } |
| 6313 | 6244 | |
| 6314 | | fn airUnreach(self: *FuncGen, inst: Air.Inst.Index) ?*llvm.Value { |
| 6245 | fn airUnreach(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 6315 | 6246 | _ = inst; |
| 6316 | | _ = self.builder.buildUnreachable(); |
| 6317 | | return null; |
| 6247 | _ = try self.wip.@"unreachable"(); |
| 6248 | return .none; |
| 6318 | 6249 | } |
| 6319 | 6250 | |
| 6320 | | fn airDbgStmt(self: *FuncGen, inst: Air.Inst.Index) ?*llvm.Value { |
| 6321 | | const di_scope = self.di_scope orelse return null; |
| 6251 | fn airDbgStmt(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 6252 | const di_scope = self.di_scope orelse return .none; |
| 6322 | 6253 | const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; |
| 6323 | 6254 | self.prev_dbg_line = @intCast(self.base_line + dbg_stmt.line + 1); |
| 6324 | 6255 | self.prev_dbg_column = @intCast(dbg_stmt.column + 1); |
| ... | ... | @@ -6327,12 +6258,12 @@ pub const FuncGen = struct { |
| 6327 | 6258 | else |
| 6328 | 6259 | null; |
| 6329 | 6260 | self.builder.setCurrentDebugLocation(self.prev_dbg_line, self.prev_dbg_column, di_scope, inlined_at); |
| 6330 | | return null; |
| 6261 | return .none; |
| 6331 | 6262 | } |
| 6332 | 6263 | |
| 6333 | | fn airDbgInlineBegin(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 6264 | fn airDbgInlineBegin(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 6334 | 6265 | const o = self.dg.object; |
| 6335 | | const dib = o.di_builder orelse return null; |
| 6266 | const dib = o.di_builder orelse return .none; |
| 6336 | 6267 | const ty_fn = self.air.instructions.items(.data)[inst].ty_fn; |
| 6337 | 6268 | |
| 6338 | 6269 | const mod = o.module; |
| ... | ... | @@ -6385,12 +6316,12 @@ pub const FuncGen = struct { |
| 6385 | 6316 | const lexical_block = dib.createLexicalBlock(subprogram.toScope(), di_file, line_number, 1); |
| 6386 | 6317 | self.di_scope = lexical_block.toScope(); |
| 6387 | 6318 | self.base_line = decl.src_line; |
| 6388 | | return null; |
| 6319 | return .none; |
| 6389 | 6320 | } |
| 6390 | 6321 | |
| 6391 | | fn airDbgInlineEnd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 6322 | fn airDbgInlineEnd(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 6392 | 6323 | const o = self.dg.object; |
| 6393 | | if (o.di_builder == null) return null; |
| 6324 | if (o.di_builder == null) return .none; |
| 6394 | 6325 | const ty_fn = self.air.instructions.items(.data)[inst].ty_fn; |
| 6395 | 6326 | |
| 6396 | 6327 | const mod = o.module; |
| ... | ... | @@ -6400,30 +6331,30 @@ pub const FuncGen = struct { |
| 6400 | 6331 | const old = self.dbg_inlined.pop(); |
| 6401 | 6332 | self.di_scope = old.scope; |
| 6402 | 6333 | self.base_line = old.base_line; |
| 6403 | | return null; |
| 6334 | return .none; |
| 6404 | 6335 | } |
| 6405 | 6336 | |
| 6406 | | fn airDbgBlockBegin(self: *FuncGen) !?*llvm.Value { |
| 6337 | fn airDbgBlockBegin(self: *FuncGen) !Builder.Value { |
| 6407 | 6338 | const o = self.dg.object; |
| 6408 | | const dib = o.di_builder orelse return null; |
| 6339 | const dib = o.di_builder orelse return .none; |
| 6409 | 6340 | const old_scope = self.di_scope.?; |
| 6410 | 6341 | try self.dbg_block_stack.append(self.gpa, old_scope); |
| 6411 | 6342 | const lexical_block = dib.createLexicalBlock(old_scope, self.di_file.?, self.prev_dbg_line, self.prev_dbg_column); |
| 6412 | 6343 | self.di_scope = lexical_block.toScope(); |
| 6413 | | return null; |
| 6344 | return .none; |
| 6414 | 6345 | } |
| 6415 | 6346 | |
| 6416 | | fn airDbgBlockEnd(self: *FuncGen) !?*llvm.Value { |
| 6347 | fn airDbgBlockEnd(self: *FuncGen) !Builder.Value { |
| 6417 | 6348 | const o = self.dg.object; |
| 6418 | | if (o.di_builder == null) return null; |
| 6349 | if (o.di_builder == null) return .none; |
| 6419 | 6350 | self.di_scope = self.dbg_block_stack.pop(); |
| 6420 | | return null; |
| 6351 | return .none; |
| 6421 | 6352 | } |
| 6422 | 6353 | |
| 6423 | | fn airDbgVarPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 6354 | fn airDbgVarPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 6424 | 6355 | const o = self.dg.object; |
| 6425 | 6356 | const mod = o.module; |
| 6426 | | const dib = o.di_builder orelse return null; |
| 6357 | const dib = o.di_builder orelse return .none; |
| 6427 | 6358 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 6428 | 6359 | const operand = try self.resolveInst(pl_op.operand); |
| 6429 | 6360 | const name = self.air.nullTerminatedString(pl_op.payload); |
| ... | ... | @@ -6443,22 +6374,20 @@ pub const FuncGen = struct { |
| 6443 | 6374 | else |
| 6444 | 6375 | null; |
| 6445 | 6376 | const debug_loc = llvm.getDebugLoc(self.prev_dbg_line, self.prev_dbg_column, self.di_scope.?, inlined_at); |
| 6446 | | const insert_block = self.builder.getInsertBlock(); |
| 6447 | | _ = dib.insertDeclareAtEnd(operand, di_local_var, debug_loc, insert_block); |
| 6448 | | return null; |
| 6377 | const insert_block = self.wip.cursor.block.toLlvm(&self.wip); |
| 6378 | _ = dib.insertDeclareAtEnd(operand.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); |
| 6379 | return .none; |
| 6449 | 6380 | } |
| 6450 | 6381 | |
| 6451 | | fn airDbgVarVal(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 6382 | fn airDbgVarVal(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 6452 | 6383 | const o = self.dg.object; |
| 6453 | | const dib = o.di_builder orelse return null; |
| 6384 | const dib = o.di_builder orelse return .none; |
| 6454 | 6385 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 6455 | 6386 | const operand = try self.resolveInst(pl_op.operand); |
| 6456 | 6387 | const operand_ty = self.typeOf(pl_op.operand); |
| 6457 | 6388 | const name = self.air.nullTerminatedString(pl_op.payload); |
| 6458 | 6389 | |
| 6459 | | if (needDbgVarWorkaround(o)) { |
| 6460 | | return null; |
| 6461 | | } |
| 6390 | if (needDbgVarWorkaround(o)) return .none; |
| 6462 | 6391 | |
| 6463 | 6392 | const di_local_var = dib.createAutoVariable( |
| 6464 | 6393 | self.di_scope.?, |
| ... | ... | @@ -6474,23 +6403,22 @@ pub const FuncGen = struct { |
| 6474 | 6403 | else |
| 6475 | 6404 | null; |
| 6476 | 6405 | const debug_loc = llvm.getDebugLoc(self.prev_dbg_line, self.prev_dbg_column, self.di_scope.?, inlined_at); |
| 6477 | | const insert_block = self.builder.getInsertBlock(); |
| 6406 | const insert_block = self.wip.cursor.block.toLlvm(&self.wip); |
| 6478 | 6407 | const mod = o.module; |
| 6479 | 6408 | if (isByRef(operand_ty, mod)) { |
| 6480 | | _ = dib.insertDeclareAtEnd(operand, di_local_var, debug_loc, insert_block); |
| 6409 | _ = dib.insertDeclareAtEnd(operand.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); |
| 6481 | 6410 | } else if (o.module.comp.bin_file.options.optimize_mode == .Debug) { |
| 6482 | | const alignment = operand_ty.abiAlignment(mod); |
| 6483 | | const alloca = try self.buildAlloca(operand.typeOf(), alignment); |
| 6484 | | const store_inst = self.builder.buildStore(operand, alloca); |
| 6485 | | store_inst.setAlignment(alignment); |
| 6486 | | _ = dib.insertDeclareAtEnd(alloca, di_local_var, debug_loc, insert_block); |
| 6411 | const alignment = Builder.Alignment.fromByteUnits(operand_ty.abiAlignment(mod)); |
| 6412 | const alloca = try self.buildAlloca(operand.typeOfWip(&self.wip), alignment); |
| 6413 | _ = try self.wip.store(.normal, operand, alloca, alignment); |
| 6414 | _ = dib.insertDeclareAtEnd(alloca.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); |
| 6487 | 6415 | } else { |
| 6488 | | _ = dib.insertDbgValueIntrinsicAtEnd(operand, di_local_var, debug_loc, insert_block); |
| 6416 | _ = dib.insertDbgValueIntrinsicAtEnd(operand.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); |
| 6489 | 6417 | } |
| 6490 | | return null; |
| 6418 | return .none; |
| 6491 | 6419 | } |
| 6492 | 6420 | |
| 6493 | | fn airAssembly(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 6421 | fn airAssembly(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 6494 | 6422 | // Eventually, the Zig compiler needs to be reworked to have inline |
| 6495 | 6423 | // assembly go through the same parsing code regardless of backend, and |
| 6496 | 6424 | // have LLVM-flavored inline assembly be *output* from that assembler. |
| ... | ... | @@ -6523,11 +6451,11 @@ pub const FuncGen = struct { |
| 6523 | 6451 | const llvm_ret_indirect = try arena.alloc(bool, max_return_count); |
| 6524 | 6452 | |
| 6525 | 6453 | const max_param_count = inputs.len + outputs.len; |
| 6526 | | const llvm_param_types = try arena.alloc(*llvm.Type, max_param_count); |
| 6454 | const llvm_param_types = try arena.alloc(Builder.Type, max_param_count); |
| 6527 | 6455 | const llvm_param_values = try arena.alloc(*llvm.Value, max_param_count); |
| 6528 | 6456 | // This stores whether we need to add an elementtype attribute and |
| 6529 | 6457 | // if so, the element type itself. |
| 6530 | | const llvm_param_attrs = try arena.alloc(?*llvm.Type, max_param_count); |
| 6458 | const llvm_param_attrs = try arena.alloc(Builder.Type, max_param_count); |
| 6531 | 6459 | const mod = o.module; |
| 6532 | 6460 | const target = mod.getTarget(); |
| 6533 | 6461 | |
| ... | ... | @@ -6564,9 +6492,9 @@ pub const FuncGen = struct { |
| 6564 | 6492 | // Pass the result by reference as an indirect output (e.g. "=*m") |
| 6565 | 6493 | llvm_constraints.appendAssumeCapacity('*'); |
| 6566 | 6494 | |
| 6567 | | llvm_param_values[llvm_param_i] = output_inst; |
| 6568 | | llvm_param_types[llvm_param_i] = output_inst.typeOf(); |
| 6569 | | llvm_param_attrs[llvm_param_i] = elem_llvm_ty.toLlvm(&o.builder); |
| 6495 | llvm_param_values[llvm_param_i] = output_inst.toLlvm(&self.wip); |
| 6496 | llvm_param_types[llvm_param_i] = output_inst.typeOfWip(&self.wip); |
| 6497 | llvm_param_attrs[llvm_param_i] = elem_llvm_ty; |
| 6570 | 6498 | llvm_param_i += 1; |
| 6571 | 6499 | } else { |
| 6572 | 6500 | // Pass the result directly (e.g. "=r") |
| ... | ... | @@ -6614,27 +6542,26 @@ pub const FuncGen = struct { |
| 6614 | 6542 | if (isByRef(arg_ty, mod)) { |
| 6615 | 6543 | llvm_elem_ty = try o.lowerPtrElemTy(arg_ty); |
| 6616 | 6544 | if (constraintAllowsMemory(constraint)) { |
| 6617 | | llvm_param_values[llvm_param_i] = arg_llvm_value; |
| 6618 | | llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf(); |
| 6545 | llvm_param_values[llvm_param_i] = arg_llvm_value.toLlvm(&self.wip); |
| 6546 | llvm_param_types[llvm_param_i] = arg_llvm_value.typeOfWip(&self.wip); |
| 6619 | 6547 | } else { |
| 6620 | | const alignment = arg_ty.abiAlignment(mod); |
| 6621 | | const arg_llvm_ty = (try o.lowerType(arg_ty)).toLlvm(&o.builder); |
| 6622 | | const load_inst = self.builder.buildLoad(arg_llvm_ty, arg_llvm_value, ""); |
| 6623 | | load_inst.setAlignment(alignment); |
| 6624 | | llvm_param_values[llvm_param_i] = load_inst; |
| 6548 | const alignment = Builder.Alignment.fromByteUnits(arg_ty.abiAlignment(mod)); |
| 6549 | const arg_llvm_ty = try o.lowerType(arg_ty); |
| 6550 | const load_inst = |
| 6551 | try self.wip.load(.normal, arg_llvm_ty, arg_llvm_value, alignment, ""); |
| 6552 | llvm_param_values[llvm_param_i] = load_inst.toLlvm(&self.wip); |
| 6625 | 6553 | llvm_param_types[llvm_param_i] = arg_llvm_ty; |
| 6626 | 6554 | } |
| 6627 | 6555 | } else { |
| 6628 | 6556 | if (constraintAllowsRegister(constraint)) { |
| 6629 | | llvm_param_values[llvm_param_i] = arg_llvm_value; |
| 6630 | | llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf(); |
| 6557 | llvm_param_values[llvm_param_i] = arg_llvm_value.toLlvm(&self.wip); |
| 6558 | llvm_param_types[llvm_param_i] = arg_llvm_value.typeOfWip(&self.wip); |
| 6631 | 6559 | } else { |
| 6632 | | const alignment = arg_ty.abiAlignment(mod); |
| 6633 | | const arg_ptr = try self.buildAlloca(arg_llvm_value.typeOf(), alignment); |
| 6634 | | const store_inst = self.builder.buildStore(arg_llvm_value, arg_ptr); |
| 6635 | | store_inst.setAlignment(alignment); |
| 6636 | | llvm_param_values[llvm_param_i] = arg_ptr; |
| 6637 | | llvm_param_types[llvm_param_i] = arg_ptr.typeOf(); |
| 6560 | const alignment = Builder.Alignment.fromByteUnits(arg_ty.abiAlignment(mod)); |
| 6561 | const arg_ptr = try self.buildAlloca(arg_llvm_value.typeOfWip(&self.wip), alignment); |
| 6562 | _ = try self.wip.store(.normal, arg_llvm_value, arg_ptr, alignment); |
| 6563 | llvm_param_values[llvm_param_i] = arg_ptr.toLlvm(&self.wip); |
| 6564 | llvm_param_types[llvm_param_i] = arg_ptr.typeOfWip(&self.wip); |
| 6638 | 6565 | } |
| 6639 | 6566 | } |
| 6640 | 6567 | |
| ... | ... | @@ -6658,12 +6585,12 @@ pub const FuncGen = struct { |
| 6658 | 6585 | // In the case of indirect inputs, LLVM requires the callsite to have |
| 6659 | 6586 | // an elementtype(<ty>) attribute. |
| 6660 | 6587 | if (constraint[0] == '*') { |
| 6661 | | llvm_param_attrs[llvm_param_i] = (if (llvm_elem_ty != .none) |
| 6588 | llvm_param_attrs[llvm_param_i] = if (llvm_elem_ty != .none) |
| 6662 | 6589 | llvm_elem_ty |
| 6663 | 6590 | else |
| 6664 | | try o.lowerPtrElemTy(arg_ty.childType(mod))).toLlvm(&o.builder); |
| 6591 | try o.lowerPtrElemTy(arg_ty.childType(mod)); |
| 6665 | 6592 | } else { |
| 6666 | | llvm_param_attrs[llvm_param_i] = null; |
| 6593 | llvm_param_attrs[llvm_param_i] = .none; |
| 6667 | 6594 | } |
| 6668 | 6595 | |
| 6669 | 6596 | llvm_param_i += 1; |
| ... | ... | @@ -6786,14 +6713,9 @@ pub const FuncGen = struct { |
| 6786 | 6713 | else => try o.builder.structType(.normal, llvm_ret_types), |
| 6787 | 6714 | }; |
| 6788 | 6715 | |
| 6789 | | const llvm_fn_ty = llvm.functionType( |
| 6790 | | ret_llvm_ty.toLlvm(&o.builder), |
| 6791 | | llvm_param_types.ptr, |
| 6792 | | @intCast(param_count), |
| 6793 | | .False, |
| 6794 | | ); |
| 6716 | const llvm_fn_ty = try o.builder.fnType(ret_llvm_ty, llvm_param_types[0..param_count], .normal); |
| 6795 | 6717 | const asm_fn = llvm.getInlineAsm( |
| 6796 | | llvm_fn_ty, |
| 6718 | llvm_fn_ty.toLlvm(&o.builder), |
| 6797 | 6719 | rendered_template.items.ptr, |
| 6798 | 6720 | rendered_template.items.len, |
| 6799 | 6721 | llvm_constraints.items.ptr, |
| ... | ... | @@ -6803,18 +6725,18 @@ pub const FuncGen = struct { |
| 6803 | 6725 | .ATT, |
| 6804 | 6726 | .False, |
| 6805 | 6727 | ); |
| 6806 | | const call = self.builder.buildCall( |
| 6807 | | llvm_fn_ty, |
| 6728 | const call = (try self.wip.unimplemented(ret_llvm_ty, "")).finish(self.builder.buildCall( |
| 6729 | llvm_fn_ty.toLlvm(&o.builder), |
| 6808 | 6730 | asm_fn, |
| 6809 | 6731 | llvm_param_values.ptr, |
| 6810 | 6732 | @intCast(param_count), |
| 6811 | 6733 | .C, |
| 6812 | 6734 | .Auto, |
| 6813 | 6735 | "", |
| 6814 | | ); |
| 6736 | ), &self.wip); |
| 6815 | 6737 | for (llvm_param_attrs[0..param_count], 0..) |llvm_elem_ty, i| { |
| 6816 | | if (llvm_elem_ty) |llvm_ty| { |
| 6817 | | llvm.setCallElemTypeAttr(call, i, llvm_ty); |
| 6738 | if (llvm_elem_ty != .none) { |
| 6739 | llvm.setCallElemTypeAttr(call.toLlvm(&self.wip), i, llvm_elem_ty.toLlvm(&o.builder)); |
| 6818 | 6740 | } |
| 6819 | 6741 | } |
| 6820 | 6742 | |
| ... | ... | @@ -6823,16 +6745,17 @@ pub const FuncGen = struct { |
| 6823 | 6745 | for (outputs, 0..) |output, i| { |
| 6824 | 6746 | if (llvm_ret_indirect[i]) continue; |
| 6825 | 6747 | |
| 6826 | | const output_value = if (return_count > 1) b: { |
| 6827 | | break :b self.builder.buildExtractValue(call, @intCast(llvm_ret_i), ""); |
| 6828 | | } else call; |
| 6748 | const output_value = if (return_count > 1) |
| 6749 | try self.wip.extractValue(call, &[_]u32{@intCast(llvm_ret_i)}, "") |
| 6750 | else |
| 6751 | call; |
| 6829 | 6752 | |
| 6830 | 6753 | if (output != .none) { |
| 6831 | 6754 | const output_ptr = try self.resolveInst(output); |
| 6832 | 6755 | const output_ptr_ty = self.typeOf(output); |
| 6833 | 6756 | |
| 6834 | | const store_inst = self.builder.buildStore(output_value, output_ptr); |
| 6835 | | store_inst.setAlignment(output_ptr_ty.ptrAlignment(mod)); |
| 6757 | const alignment = Builder.Alignment.fromByteUnits(output_ptr_ty.ptrAlignment(mod)); |
| 6758 | _ = try self.wip.store(.normal, output_value, output_ptr, alignment); |
| 6836 | 6759 | } else { |
| 6837 | 6760 | ret_val = output_value; |
| 6838 | 6761 | } |
| ... | ... | @@ -6846,8 +6769,8 @@ pub const FuncGen = struct { |
| 6846 | 6769 | self: *FuncGen, |
| 6847 | 6770 | inst: Air.Inst.Index, |
| 6848 | 6771 | operand_is_ptr: bool, |
| 6849 | | pred: llvm.IntPredicate, |
| 6850 | | ) !?*llvm.Value { |
| 6772 | cond: Builder.IntegerCondition, |
| 6773 | ) !Builder.Value { |
| 6851 | 6774 | const o = self.dg.object; |
| 6852 | 6775 | const mod = o.module; |
| 6853 | 6776 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| ... | ... | @@ -6858,45 +6781,40 @@ pub const FuncGen = struct { |
| 6858 | 6781 | const payload_ty = optional_ty.optionalChild(mod); |
| 6859 | 6782 | if (optional_ty.optionalReprIsPayload(mod)) { |
| 6860 | 6783 | const loaded = if (operand_is_ptr) |
| 6861 | | self.builder.buildLoad(optional_llvm_ty.toLlvm(&o.builder), operand, "") |
| 6784 | try self.wip.load(.normal, optional_llvm_ty, operand, .default, "") |
| 6862 | 6785 | else |
| 6863 | 6786 | operand; |
| 6864 | 6787 | if (payload_ty.isSlice(mod)) { |
| 6865 | | const slice_ptr = self.builder.buildExtractValue(loaded, 0, ""); |
| 6788 | const slice_ptr = try self.wip.extractValue(loaded, &.{0}, ""); |
| 6866 | 6789 | const ptr_ty = try o.builder.ptrType(toLlvmAddressSpace( |
| 6867 | 6790 | payload_ty.ptrAddressSpace(mod), |
| 6868 | 6791 | mod.getTarget(), |
| 6869 | 6792 | )); |
| 6870 | | return self.builder.buildICmp(pred, slice_ptr, (try o.builder.nullConst(ptr_ty)).toLlvm(&o.builder), ""); |
| 6793 | return self.wip.icmp(cond, slice_ptr, try o.builder.nullValue(ptr_ty), ""); |
| 6871 | 6794 | } |
| 6872 | | return self.builder.buildICmp(pred, loaded, (try o.builder.zeroInitConst(optional_llvm_ty)).toLlvm(&o.builder), ""); |
| 6795 | return self.wip.icmp(cond, loaded, try o.builder.zeroInitValue(optional_llvm_ty), ""); |
| 6873 | 6796 | } |
| 6874 | 6797 | |
| 6875 | 6798 | comptime assert(optional_layout_version == 3); |
| 6876 | 6799 | |
| 6877 | 6800 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 6878 | 6801 | const loaded = if (operand_is_ptr) |
| 6879 | | self.builder.buildLoad(optional_llvm_ty.toLlvm(&o.builder), operand, "") |
| 6802 | try self.wip.load(.normal, optional_llvm_ty, operand, .default, "") |
| 6880 | 6803 | else |
| 6881 | 6804 | operand; |
| 6882 | | return self.builder.buildICmp(pred, loaded, (try o.builder.intConst(.i8, 0)).toLlvm(&o.builder), ""); |
| 6805 | return self.wip.icmp(cond, loaded, try o.builder.intValue(.i8, 0), ""); |
| 6883 | 6806 | } |
| 6884 | 6807 | |
| 6885 | 6808 | const is_by_ref = operand_is_ptr or isByRef(optional_ty, mod); |
| 6886 | | const non_null_bit = try self.optIsNonNull(optional_llvm_ty.toLlvm(&o.builder), operand, is_by_ref); |
| 6887 | | if (pred == .EQ) { |
| 6888 | | return self.builder.buildNot(non_null_bit, ""); |
| 6889 | | } else { |
| 6890 | | return non_null_bit; |
| 6891 | | } |
| 6809 | return self.optCmpNull(cond, optional_llvm_ty, operand, is_by_ref); |
| 6892 | 6810 | } |
| 6893 | 6811 | |
| 6894 | 6812 | fn airIsErr( |
| 6895 | 6813 | self: *FuncGen, |
| 6896 | 6814 | inst: Air.Inst.Index, |
| 6897 | | op: llvm.IntPredicate, |
| 6815 | cond: Builder.IntegerCondition, |
| 6898 | 6816 | operand_is_ptr: bool, |
| 6899 | | ) !?*llvm.Value { |
| 6817 | ) !Builder.Value { |
| 6900 | 6818 | const o = self.dg.object; |
| 6901 | 6819 | const mod = o.module; |
| 6902 | 6820 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| ... | ... | @@ -6904,39 +6822,37 @@ pub const FuncGen = struct { |
| 6904 | 6822 | const operand_ty = self.typeOf(un_op); |
| 6905 | 6823 | const err_union_ty = if (operand_is_ptr) operand_ty.childType(mod) else operand_ty; |
| 6906 | 6824 | const payload_ty = err_union_ty.errorUnionPayload(mod); |
| 6907 | | const zero = (try o.builder.intConst(Builder.Type.err_int, 0)).toLlvm(&o.builder); |
| 6825 | const zero = try o.builder.intValue(Builder.Type.err_int, 0); |
| 6908 | 6826 | |
| 6909 | 6827 | if (err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) { |
| 6910 | | const val: Builder.Constant = switch (op) { |
| 6911 | | .EQ => .true, // 0 == 0 |
| 6912 | | .NE => .false, // 0 != 0 |
| 6828 | const val: Builder.Constant = switch (cond) { |
| 6829 | .eq => .true, // 0 == 0 |
| 6830 | .ne => .false, // 0 != 0 |
| 6913 | 6831 | else => unreachable, |
| 6914 | 6832 | }; |
| 6915 | | return val.toLlvm(&o.builder); |
| 6833 | return val.toValue(); |
| 6916 | 6834 | } |
| 6917 | 6835 | |
| 6918 | 6836 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 6919 | 6837 | const loaded = if (operand_is_ptr) |
| 6920 | | self.builder.buildLoad((try o.lowerType(err_union_ty)).toLlvm(&o.builder), operand, "") |
| 6838 | try self.wip.load(.normal, try o.lowerType(err_union_ty), operand, .default, "") |
| 6921 | 6839 | else |
| 6922 | 6840 | operand; |
| 6923 | | return self.builder.buildICmp(op, loaded, zero, ""); |
| 6841 | return self.wip.icmp(cond, loaded, zero, ""); |
| 6924 | 6842 | } |
| 6925 | 6843 | |
| 6926 | 6844 | const err_field_index = errUnionErrorOffset(payload_ty, mod); |
| 6927 | 6845 | |
| 6928 | | if (operand_is_ptr or isByRef(err_union_ty, mod)) { |
| 6929 | | const err_union_llvm_ty = (try o.lowerType(err_union_ty)).toLlvm(&o.builder); |
| 6930 | | const err_field_ptr = self.builder.buildStructGEP(err_union_llvm_ty, operand, err_field_index, ""); |
| 6931 | | const loaded = self.builder.buildLoad(Builder.Type.err_int.toLlvm(&o.builder), err_field_ptr, ""); |
| 6932 | | return self.builder.buildICmp(op, loaded, zero, ""); |
| 6933 | | } |
| 6934 | | |
| 6935 | | const loaded = self.builder.buildExtractValue(operand, err_field_index, ""); |
| 6936 | | return self.builder.buildICmp(op, loaded, zero, ""); |
| 6846 | const loaded = if (operand_is_ptr or isByRef(err_union_ty, mod)) loaded: { |
| 6847 | const err_union_llvm_ty = try o.lowerType(err_union_ty); |
| 6848 | const err_field_ptr = |
| 6849 | try self.wip.gepStruct(err_union_llvm_ty, operand, err_field_index, ""); |
| 6850 | break :loaded try self.wip.load(.normal, Builder.Type.err_int, err_field_ptr, .default, ""); |
| 6851 | } else try self.wip.extractValue(operand, &.{err_field_index}, ""); |
| 6852 | return self.wip.icmp(cond, loaded, zero, ""); |
| 6937 | 6853 | } |
| 6938 | 6854 | |
| 6939 | | fn airOptionalPayloadPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 6855 | fn airOptionalPayloadPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 6940 | 6856 | const o = self.dg.object; |
| 6941 | 6857 | const mod = o.module; |
| 6942 | 6858 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| ... | ... | @@ -6952,11 +6868,10 @@ pub const FuncGen = struct { |
| 6952 | 6868 | // The payload and the optional are the same value. |
| 6953 | 6869 | return operand; |
| 6954 | 6870 | } |
| 6955 | | const optional_llvm_ty = (try o.lowerType(optional_ty)).toLlvm(&o.builder); |
| 6956 | | return self.builder.buildStructGEP(optional_llvm_ty, operand, 0, ""); |
| 6871 | return self.wip.gepStruct(try o.lowerType(optional_ty), operand, 0, ""); |
| 6957 | 6872 | } |
| 6958 | 6873 | |
| 6959 | | fn airOptionalPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 6874 | fn airOptionalPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 6960 | 6875 | comptime assert(optional_layout_version == 3); |
| 6961 | 6876 | |
| 6962 | 6877 | const o = self.dg.object; |
| ... | ... | @@ -6965,10 +6880,10 @@ pub const FuncGen = struct { |
| 6965 | 6880 | const operand = try self.resolveInst(ty_op.operand); |
| 6966 | 6881 | const optional_ty = self.typeOf(ty_op.operand).childType(mod); |
| 6967 | 6882 | const payload_ty = optional_ty.optionalChild(mod); |
| 6968 | | const non_null_bit = (try o.builder.intConst(.i8, 1)).toLlvm(&o.builder); |
| 6883 | const non_null_bit = try o.builder.intValue(.i8, 1); |
| 6969 | 6884 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 6970 | 6885 | // We have a pointer to a i8. We need to set it to 1 and then return the same pointer. |
| 6971 | | _ = self.builder.buildStore(non_null_bit, operand); |
| 6886 | _ = try self.wip.store(.normal, non_null_bit, operand, .default); |
| 6972 | 6887 | return operand; |
| 6973 | 6888 | } |
| 6974 | 6889 | if (optional_ty.optionalReprIsPayload(mod)) { |
| ... | ... | @@ -6978,19 +6893,18 @@ pub const FuncGen = struct { |
| 6978 | 6893 | } |
| 6979 | 6894 | |
| 6980 | 6895 | // First set the non-null bit. |
| 6981 | | const optional_llvm_ty = (try o.lowerType(optional_ty)).toLlvm(&o.builder); |
| 6982 | | const non_null_ptr = self.builder.buildStructGEP(optional_llvm_ty, operand, 1, ""); |
| 6896 | const optional_llvm_ty = try o.lowerType(optional_ty); |
| 6897 | const non_null_ptr = try self.wip.gepStruct(optional_llvm_ty, operand, 1, ""); |
| 6983 | 6898 | // TODO set alignment on this store |
| 6984 | | _ = self.builder.buildStore(non_null_bit, non_null_ptr); |
| 6899 | _ = try self.wip.store(.normal, non_null_bit, non_null_ptr, .default); |
| 6985 | 6900 | |
| 6986 | 6901 | // Then return the payload pointer (only if it's used). |
| 6987 | | if (self.liveness.isUnused(inst)) |
| 6988 | | return null; |
| 6902 | if (self.liveness.isUnused(inst)) return .none; |
| 6989 | 6903 | |
| 6990 | | return self.builder.buildStructGEP(optional_llvm_ty, operand, 0, ""); |
| 6904 | return self.wip.gepStruct(optional_llvm_ty, operand, 0, ""); |
| 6991 | 6905 | } |
| 6992 | 6906 | |
| 6993 | | fn airOptionalPayload(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value { |
| 6907 | fn airOptionalPayload(self: *FuncGen, body_tail: []const Air.Inst.Index) !Builder.Value { |
| 6994 | 6908 | const o = self.dg.object; |
| 6995 | 6909 | const mod = o.module; |
| 6996 | 6910 | const inst = body_tail[0]; |
| ... | ... | @@ -6998,14 +6912,14 @@ pub const FuncGen = struct { |
| 6998 | 6912 | const operand = try self.resolveInst(ty_op.operand); |
| 6999 | 6913 | const optional_ty = self.typeOf(ty_op.operand); |
| 7000 | 6914 | const payload_ty = self.typeOfIndex(inst); |
| 7001 | | if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) return null; |
| 6915 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) return .none; |
| 7002 | 6916 | |
| 7003 | 6917 | if (optional_ty.optionalReprIsPayload(mod)) { |
| 7004 | 6918 | // Payload value is the same as the optional value. |
| 7005 | 6919 | return operand; |
| 7006 | 6920 | } |
| 7007 | 6921 | |
| 7008 | | const opt_llvm_ty = (try o.lowerType(optional_ty)).toLlvm(&o.builder); |
| 6922 | const opt_llvm_ty = try o.lowerType(optional_ty); |
| 7009 | 6923 | const can_elide_load = if (isByRef(payload_ty, mod)) self.canElideLoad(body_tail) else false; |
| 7010 | 6924 | return self.optPayloadHandle(opt_llvm_ty, operand, optional_ty, can_elide_load); |
| 7011 | 6925 | } |
| ... | ... | @@ -7014,7 +6928,7 @@ pub const FuncGen = struct { |
| 7014 | 6928 | self: *FuncGen, |
| 7015 | 6929 | body_tail: []const Air.Inst.Index, |
| 7016 | 6930 | operand_is_ptr: bool, |
| 7017 | | ) !?*llvm.Value { |
| 6931 | ) !Builder.Value { |
| 7018 | 6932 | const o = self.dg.object; |
| 7019 | 6933 | const mod = o.module; |
| 7020 | 6934 | const inst = body_tail[0]; |
| ... | ... | @@ -7026,32 +6940,30 @@ pub const FuncGen = struct { |
| 7026 | 6940 | const payload_ty = if (operand_is_ptr) result_ty.childType(mod) else result_ty; |
| 7027 | 6941 | |
| 7028 | 6942 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 7029 | | return if (operand_is_ptr) operand else null; |
| 6943 | return if (operand_is_ptr) operand else .none; |
| 7030 | 6944 | } |
| 7031 | 6945 | const offset = errUnionPayloadOffset(payload_ty, mod); |
| 7032 | | const err_union_llvm_ty = (try o.lowerType(err_union_ty)).toLlvm(&o.builder); |
| 6946 | const err_union_llvm_ty = try o.lowerType(err_union_ty); |
| 7033 | 6947 | if (operand_is_ptr) { |
| 7034 | | return self.builder.buildStructGEP(err_union_llvm_ty, operand, offset, ""); |
| 6948 | return self.wip.gepStruct(err_union_llvm_ty, operand, offset, ""); |
| 7035 | 6949 | } else if (isByRef(err_union_ty, mod)) { |
| 7036 | | const payload_ptr = self.builder.buildStructGEP(err_union_llvm_ty, operand, offset, ""); |
| 6950 | const payload_alignment = Builder.Alignment.fromByteUnits(payload_ty.abiAlignment(mod)); |
| 6951 | const payload_ptr = try self.wip.gepStruct(err_union_llvm_ty, operand, offset, ""); |
| 7037 | 6952 | if (isByRef(payload_ty, mod)) { |
| 7038 | | if (self.canElideLoad(body_tail)) |
| 7039 | | return payload_ptr; |
| 7040 | | |
| 7041 | | return self.loadByRef(payload_ptr, payload_ty, payload_ty.abiAlignment(mod), false); |
| 6953 | if (self.canElideLoad(body_tail)) return payload_ptr; |
| 6954 | return self.loadByRef(payload_ptr, payload_ty, payload_alignment, false); |
| 7042 | 6955 | } |
| 7043 | | const load_inst = self.builder.buildLoad(err_union_llvm_ty.structGetTypeAtIndex(offset), payload_ptr, ""); |
| 7044 | | load_inst.setAlignment(payload_ty.abiAlignment(mod)); |
| 7045 | | return load_inst; |
| 6956 | const payload_llvm_ty = err_union_llvm_ty.structFields(&o.builder)[offset]; |
| 6957 | return self.wip.load(.normal, payload_llvm_ty, payload_ptr, payload_alignment, ""); |
| 7046 | 6958 | } |
| 7047 | | return self.builder.buildExtractValue(operand, offset, ""); |
| 6959 | return self.wip.extractValue(operand, &.{offset}, ""); |
| 7048 | 6960 | } |
| 7049 | 6961 | |
| 7050 | 6962 | fn airErrUnionErr( |
| 7051 | 6963 | self: *FuncGen, |
| 7052 | 6964 | inst: Air.Inst.Index, |
| 7053 | 6965 | operand_is_ptr: bool, |
| 7054 | | ) !?*llvm.Value { |
| 6966 | ) !Builder.Value { |
| 7055 | 6967 | const o = self.dg.object; |
| 7056 | 6968 | const mod = o.module; |
| 7057 | 6969 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| ... | ... | @@ -7062,30 +6974,28 @@ pub const FuncGen = struct { |
| 7062 | 6974 | if (operand_is_ptr) { |
| 7063 | 6975 | return operand; |
| 7064 | 6976 | } else { |
| 7065 | | return (try o.builder.intConst(Builder.Type.err_int, 0)).toLlvm(&o.builder); |
| 6977 | return o.builder.intValue(Builder.Type.err_int, 0); |
| 7066 | 6978 | } |
| 7067 | 6979 | } |
| 7068 | 6980 | |
| 7069 | | const err_set_llvm_ty = (try o.lowerType(Type.anyerror)).toLlvm(&o.builder); |
| 7070 | | |
| 7071 | 6981 | const payload_ty = err_union_ty.errorUnionPayload(mod); |
| 7072 | 6982 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 7073 | 6983 | if (!operand_is_ptr) return operand; |
| 7074 | | return self.builder.buildLoad(err_set_llvm_ty, operand, ""); |
| 6984 | return self.wip.load(.normal, Builder.Type.err_int, operand, .default, ""); |
| 7075 | 6985 | } |
| 7076 | 6986 | |
| 7077 | 6987 | const offset = errUnionErrorOffset(payload_ty, mod); |
| 7078 | 6988 | |
| 7079 | 6989 | if (operand_is_ptr or isByRef(err_union_ty, mod)) { |
| 7080 | | const err_union_llvm_ty = (try o.lowerType(err_union_ty)).toLlvm(&o.builder); |
| 7081 | | const err_field_ptr = self.builder.buildStructGEP(err_union_llvm_ty, operand, offset, ""); |
| 7082 | | return self.builder.buildLoad(err_set_llvm_ty, err_field_ptr, ""); |
| 6990 | const err_union_llvm_ty = try o.lowerType(err_union_ty); |
| 6991 | const err_field_ptr = try self.wip.gepStruct(err_union_llvm_ty, operand, offset, ""); |
| 6992 | return self.wip.load(.normal, Builder.Type.err_int, err_field_ptr, .default, ""); |
| 7083 | 6993 | } |
| 7084 | 6994 | |
| 7085 | | return self.builder.buildExtractValue(operand, offset, ""); |
| 6995 | return self.wip.extractValue(operand, &.{offset}, ""); |
| 7086 | 6996 | } |
| 7087 | 6997 | |
| 7088 | | fn airErrUnionPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 6998 | fn airErrUnionPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7089 | 6999 | const o = self.dg.object; |
| 7090 | 7000 | const mod = o.module; |
| 7091 | 7001 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| ... | ... | @@ -7093,49 +7003,49 @@ pub const FuncGen = struct { |
| 7093 | 7003 | const err_union_ty = self.typeOf(ty_op.operand).childType(mod); |
| 7094 | 7004 | |
| 7095 | 7005 | const payload_ty = err_union_ty.errorUnionPayload(mod); |
| 7096 | | const non_error_val = try o.lowerValue((try mod.intValue(Type.err_int, 0)).toIntern()); |
| 7006 | const non_error_val = try o.builder.intValue(Builder.Type.err_int, 0); |
| 7097 | 7007 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 7098 | | _ = self.builder.buildStore(non_error_val.toLlvm(&o.builder), operand); |
| 7008 | _ = try self.wip.store(.normal, non_error_val, operand, .default); |
| 7099 | 7009 | return operand; |
| 7100 | 7010 | } |
| 7101 | | const err_union_llvm_ty = (try o.lowerType(err_union_ty)).toLlvm(&o.builder); |
| 7011 | const err_union_llvm_ty = try o.lowerType(err_union_ty); |
| 7102 | 7012 | { |
| 7013 | const error_alignment = Builder.Alignment.fromByteUnits(Type.err_int.abiAlignment(mod)); |
| 7103 | 7014 | const error_offset = errUnionErrorOffset(payload_ty, mod); |
| 7104 | 7015 | // First set the non-error value. |
| 7105 | | const non_null_ptr = self.builder.buildStructGEP(err_union_llvm_ty, operand, error_offset, ""); |
| 7106 | | const store_inst = self.builder.buildStore(non_error_val.toLlvm(&o.builder), non_null_ptr); |
| 7107 | | store_inst.setAlignment(Type.anyerror.abiAlignment(mod)); |
| 7016 | const non_null_ptr = try self.wip.gepStruct(err_union_llvm_ty, operand, error_offset, ""); |
| 7017 | _ = try self.wip.store(.normal, non_error_val, non_null_ptr, error_alignment); |
| 7108 | 7018 | } |
| 7109 | 7019 | // Then return the payload pointer (only if it is used). |
| 7110 | | if (self.liveness.isUnused(inst)) |
| 7111 | | return null; |
| 7020 | if (self.liveness.isUnused(inst)) return .none; |
| 7112 | 7021 | |
| 7113 | 7022 | const payload_offset = errUnionPayloadOffset(payload_ty, mod); |
| 7114 | | return self.builder.buildStructGEP(err_union_llvm_ty, operand, payload_offset, ""); |
| 7023 | return self.wip.gepStruct(err_union_llvm_ty, operand, payload_offset, ""); |
| 7115 | 7024 | } |
| 7116 | 7025 | |
| 7117 | | fn airErrReturnTrace(self: *FuncGen, _: Air.Inst.Index) !?*llvm.Value { |
| 7118 | | return self.err_ret_trace.?; |
| 7026 | fn airErrReturnTrace(self: *FuncGen, _: Air.Inst.Index) !Builder.Value { |
| 7027 | assert(self.err_ret_trace != .none); |
| 7028 | return self.err_ret_trace; |
| 7119 | 7029 | } |
| 7120 | 7030 | |
| 7121 | | fn airSetErrReturnTrace(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7031 | fn airSetErrReturnTrace(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7122 | 7032 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 7123 | | const operand = try self.resolveInst(un_op); |
| 7124 | | self.err_ret_trace = operand; |
| 7125 | | return null; |
| 7033 | self.err_ret_trace = try self.resolveInst(un_op); |
| 7034 | return .none; |
| 7126 | 7035 | } |
| 7127 | 7036 | |
| 7128 | | fn airSaveErrReturnTraceIndex(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7037 | fn airSaveErrReturnTraceIndex(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7129 | 7038 | const o = self.dg.object; |
| 7130 | 7039 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 7131 | | //const struct_ty = try self.resolveInst(ty_pl.ty); |
| 7132 | 7040 | const struct_ty = self.air.getRefType(ty_pl.ty); |
| 7133 | 7041 | const field_index = ty_pl.payload; |
| 7134 | 7042 | |
| 7135 | 7043 | const mod = o.module; |
| 7136 | 7044 | const llvm_field = llvmField(struct_ty, field_index, mod).?; |
| 7137 | | const struct_llvm_ty = (try o.lowerType(struct_ty)).toLlvm(&o.builder); |
| 7138 | | const field_ptr = self.builder.buildStructGEP(struct_llvm_ty, self.err_ret_trace.?, llvm_field.index, ""); |
| 7045 | const struct_llvm_ty = try o.lowerType(struct_ty); |
| 7046 | assert(self.err_ret_trace != .none); |
| 7047 | const field_ptr = |
| 7048 | try self.wip.gepStruct(struct_llvm_ty, self.err_ret_trace, llvm_field.index, ""); |
| 7139 | 7049 | const field_ptr_ty = try mod.ptrType(.{ |
| 7140 | 7050 | .child = llvm_field.ty.toIntern(), |
| 7141 | 7051 | .flags = .{ |
| ... | ... | @@ -7145,34 +7055,32 @@ pub const FuncGen = struct { |
| 7145 | 7055 | return self.load(field_ptr, field_ptr_ty); |
| 7146 | 7056 | } |
| 7147 | 7057 | |
| 7148 | | fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7058 | fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7149 | 7059 | const o = self.dg.object; |
| 7150 | 7060 | const mod = o.module; |
| 7151 | 7061 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 7152 | 7062 | const payload_ty = self.typeOf(ty_op.operand); |
| 7153 | | const non_null_bit = (try o.builder.intConst(.i8, 1)).toLlvm(&o.builder); |
| 7063 | const non_null_bit = try o.builder.intValue(.i8, 1); |
| 7154 | 7064 | comptime assert(optional_layout_version == 3); |
| 7155 | 7065 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) return non_null_bit; |
| 7156 | 7066 | const operand = try self.resolveInst(ty_op.operand); |
| 7157 | 7067 | const optional_ty = self.typeOfIndex(inst); |
| 7158 | | if (optional_ty.optionalReprIsPayload(mod)) { |
| 7159 | | return operand; |
| 7160 | | } |
| 7161 | | const llvm_optional_ty = (try o.lowerType(optional_ty)).toLlvm(&o.builder); |
| 7068 | if (optional_ty.optionalReprIsPayload(mod)) return operand; |
| 7069 | const llvm_optional_ty = try o.lowerType(optional_ty); |
| 7162 | 7070 | if (isByRef(optional_ty, mod)) { |
| 7163 | | const optional_ptr = try self.buildAlloca(llvm_optional_ty, optional_ty.abiAlignment(mod)); |
| 7164 | | const payload_ptr = self.builder.buildStructGEP(llvm_optional_ty, optional_ptr, 0, ""); |
| 7071 | const alignment = Builder.Alignment.fromByteUnits(optional_ty.abiAlignment(mod)); |
| 7072 | const optional_ptr = try self.buildAlloca(llvm_optional_ty, alignment); |
| 7073 | const payload_ptr = try self.wip.gepStruct(llvm_optional_ty, optional_ptr, 0, ""); |
| 7165 | 7074 | const payload_ptr_ty = try mod.singleMutPtrType(payload_ty); |
| 7166 | | try self.store(payload_ptr, payload_ptr_ty, operand, .NotAtomic); |
| 7167 | | const non_null_ptr = self.builder.buildStructGEP(llvm_optional_ty, optional_ptr, 1, ""); |
| 7168 | | _ = self.builder.buildStore(non_null_bit, non_null_ptr); |
| 7075 | try self.store(payload_ptr, payload_ptr_ty, operand, .none); |
| 7076 | const non_null_ptr = try self.wip.gepStruct(llvm_optional_ty, optional_ptr, 1, ""); |
| 7077 | _ = try self.wip.store(.normal, non_null_bit, non_null_ptr, .default); |
| 7169 | 7078 | return optional_ptr; |
| 7170 | 7079 | } |
| 7171 | | const partial = self.builder.buildInsertValue(llvm_optional_ty.getUndef(), operand, 0, ""); |
| 7172 | | return self.builder.buildInsertValue(partial, non_null_bit, 1, ""); |
| 7080 | return self.wip.buildAggregate(llvm_optional_ty, &.{ operand, non_null_bit }, ""); |
| 7173 | 7081 | } |
| 7174 | 7082 | |
| 7175 | | fn airWrapErrUnionPayload(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7083 | fn airWrapErrUnionPayload(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7176 | 7084 | const o = self.dg.object; |
| 7177 | 7085 | const mod = o.module; |
| 7178 | 7086 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| ... | ... | @@ -7182,46 +7090,47 @@ pub const FuncGen = struct { |
| 7182 | 7090 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 7183 | 7091 | return operand; |
| 7184 | 7092 | } |
| 7185 | | const ok_err_code = (try o.builder.intConst(Builder.Type.err_int, 0)).toLlvm(&o.builder); |
| 7186 | | const err_un_llvm_ty = (try o.lowerType(err_un_ty)).toLlvm(&o.builder); |
| 7093 | const ok_err_code = try o.builder.intValue(Builder.Type.err_int, 0); |
| 7094 | const err_un_llvm_ty = try o.lowerType(err_un_ty); |
| 7187 | 7095 | |
| 7188 | 7096 | const payload_offset = errUnionPayloadOffset(payload_ty, mod); |
| 7189 | 7097 | const error_offset = errUnionErrorOffset(payload_ty, mod); |
| 7190 | 7098 | if (isByRef(err_un_ty, mod)) { |
| 7191 | | const result_ptr = try self.buildAlloca(err_un_llvm_ty, err_un_ty.abiAlignment(mod)); |
| 7192 | | const err_ptr = self.builder.buildStructGEP(err_un_llvm_ty, result_ptr, error_offset, ""); |
| 7193 | | const store_inst = self.builder.buildStore(ok_err_code, err_ptr); |
| 7194 | | store_inst.setAlignment(Type.anyerror.abiAlignment(mod)); |
| 7195 | | const payload_ptr = self.builder.buildStructGEP(err_un_llvm_ty, result_ptr, payload_offset, ""); |
| 7099 | const alignment = Builder.Alignment.fromByteUnits(err_un_ty.abiAlignment(mod)); |
| 7100 | const result_ptr = try self.buildAlloca(err_un_llvm_ty, alignment); |
| 7101 | const err_ptr = try self.wip.gepStruct(err_un_llvm_ty, result_ptr, error_offset, ""); |
| 7102 | const error_alignment = Builder.Alignment.fromByteUnits(Type.err_int.abiAlignment(mod)); |
| 7103 | _ = try self.wip.store(.normal, ok_err_code, err_ptr, error_alignment); |
| 7104 | const payload_ptr = try self.wip.gepStruct(err_un_llvm_ty, result_ptr, payload_offset, ""); |
| 7196 | 7105 | const payload_ptr_ty = try mod.singleMutPtrType(payload_ty); |
| 7197 | | try self.store(payload_ptr, payload_ptr_ty, operand, .NotAtomic); |
| 7106 | try self.store(payload_ptr, payload_ptr_ty, operand, .none); |
| 7198 | 7107 | return result_ptr; |
| 7199 | 7108 | } |
| 7200 | | |
| 7201 | | const partial = self.builder.buildInsertValue(err_un_llvm_ty.getUndef(), ok_err_code, error_offset, ""); |
| 7202 | | return self.builder.buildInsertValue(partial, operand, payload_offset, ""); |
| 7109 | var fields: [2]Builder.Value = undefined; |
| 7110 | fields[payload_offset] = operand; |
| 7111 | fields[error_offset] = ok_err_code; |
| 7112 | return self.wip.buildAggregate(err_un_llvm_ty, &fields, ""); |
| 7203 | 7113 | } |
| 7204 | 7114 | |
| 7205 | | fn airWrapErrUnionErr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7115 | fn airWrapErrUnionErr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7206 | 7116 | const o = self.dg.object; |
| 7207 | 7117 | const mod = o.module; |
| 7208 | 7118 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 7209 | 7119 | const err_un_ty = self.typeOfIndex(inst); |
| 7210 | 7120 | const payload_ty = err_un_ty.errorUnionPayload(mod); |
| 7211 | 7121 | const operand = try self.resolveInst(ty_op.operand); |
| 7212 | | if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 7213 | | return operand; |
| 7214 | | } |
| 7215 | | const err_un_llvm_ty = (try o.lowerType(err_un_ty)).toLlvm(&o.builder); |
| 7122 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) return operand; |
| 7123 | const err_un_llvm_ty = try o.lowerType(err_un_ty); |
| 7216 | 7124 | |
| 7217 | 7125 | const payload_offset = errUnionPayloadOffset(payload_ty, mod); |
| 7218 | 7126 | const error_offset = errUnionErrorOffset(payload_ty, mod); |
| 7219 | 7127 | if (isByRef(err_un_ty, mod)) { |
| 7220 | | const result_ptr = try self.buildAlloca(err_un_llvm_ty, err_un_ty.abiAlignment(mod)); |
| 7221 | | const err_ptr = self.builder.buildStructGEP(err_un_llvm_ty, result_ptr, error_offset, ""); |
| 7222 | | const store_inst = self.builder.buildStore(operand, err_ptr); |
| 7223 | | store_inst.setAlignment(Type.anyerror.abiAlignment(mod)); |
| 7224 | | const payload_ptr = self.builder.buildStructGEP(err_un_llvm_ty, result_ptr, payload_offset, ""); |
| 7128 | const alignment = Builder.Alignment.fromByteUnits(err_un_ty.abiAlignment(mod)); |
| 7129 | const result_ptr = try self.buildAlloca(err_un_llvm_ty, alignment); |
| 7130 | const err_ptr = try self.wip.gepStruct(err_un_llvm_ty, result_ptr, error_offset, ""); |
| 7131 | const error_alignment = Builder.Alignment.fromByteUnits(Type.err_int.abiAlignment(mod)); |
| 7132 | _ = try self.wip.store(.normal, operand, err_ptr, error_alignment); |
| 7133 | const payload_ptr = try self.wip.gepStruct(err_un_llvm_ty, result_ptr, payload_offset, ""); |
| 7225 | 7134 | const payload_ptr_ty = try mod.singleMutPtrType(payload_ty); |
| 7226 | 7135 | // TODO store undef to payload_ptr |
| 7227 | 7136 | _ = payload_ptr; |
| ... | ... | @@ -7229,12 +7138,12 @@ pub const FuncGen = struct { |
| 7229 | 7138 | return result_ptr; |
| 7230 | 7139 | } |
| 7231 | 7140 | |
| 7232 | | const partial = self.builder.buildInsertValue(err_un_llvm_ty.getUndef(), operand, error_offset, ""); |
| 7233 | 7141 | // TODO set payload bytes to undef |
| 7234 | | return partial; |
| 7142 | const undef = try o.builder.undefValue(err_un_llvm_ty); |
| 7143 | return self.wip.insertValue(undef, operand, &.{error_offset}, ""); |
| 7235 | 7144 | } |
| 7236 | 7145 | |
| 7237 | | fn airWasmMemorySize(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7146 | fn airWasmMemorySize(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7238 | 7147 | const o = self.dg.object; |
| 7239 | 7148 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 7240 | 7149 | const index = pl_op.payload; |
| ... | ... | @@ -7242,10 +7151,18 @@ pub const FuncGen = struct { |
| 7242 | 7151 | const args: [1]*llvm.Value = .{ |
| 7243 | 7152 | (try o.builder.intConst(.i32, index)).toLlvm(&o.builder), |
| 7244 | 7153 | }; |
| 7245 | | return self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, ""); |
| 7154 | return (try self.wip.unimplemented(.i32, "")).finish(self.builder.buildCall( |
| 7155 | (try o.builder.fnType(.i32, &.{.i32}, .normal)).toLlvm(&o.builder), |
| 7156 | llvm_fn, |
| 7157 | &args, |
| 7158 | args.len, |
| 7159 | .Fast, |
| 7160 | .Auto, |
| 7161 | "", |
| 7162 | ), &self.wip); |
| 7246 | 7163 | } |
| 7247 | 7164 | |
| 7248 | | fn airWasmMemoryGrow(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7165 | fn airWasmMemoryGrow(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7249 | 7166 | const o = self.dg.object; |
| 7250 | 7167 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 7251 | 7168 | const index = pl_op.payload; |
| ... | ... | @@ -7253,12 +7170,20 @@ pub const FuncGen = struct { |
| 7253 | 7170 | const llvm_fn = try self.getIntrinsic("llvm.wasm.memory.grow", &.{.i32}); |
| 7254 | 7171 | const args: [2]*llvm.Value = .{ |
| 7255 | 7172 | (try o.builder.intConst(.i32, index)).toLlvm(&o.builder), |
| 7256 | | operand, |
| 7173 | operand.toLlvm(&self.wip), |
| 7257 | 7174 | }; |
| 7258 | | return self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, ""); |
| 7175 | return (try self.wip.unimplemented(.i32, "")).finish(self.builder.buildCall( |
| 7176 | (try o.builder.fnType(.i32, &.{ .i32, .i32 }, .normal)).toLlvm(&o.builder), |
| 7177 | llvm_fn, |
| 7178 | &args, |
| 7179 | args.len, |
| 7180 | .Fast, |
| 7181 | .Auto, |
| 7182 | "", |
| 7183 | ), &self.wip); |
| 7259 | 7184 | } |
| 7260 | 7185 | |
| 7261 | | fn airVectorStoreElem(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7186 | fn airVectorStoreElem(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7262 | 7187 | const o = self.dg.object; |
| 7263 | 7188 | const mod = o.module; |
| 7264 | 7189 | const data = self.air.instructions.items(.data)[inst].vector_store_elem; |
| ... | ... | @@ -7269,19 +7194,20 @@ pub const FuncGen = struct { |
| 7269 | 7194 | const index = try self.resolveInst(extra.lhs); |
| 7270 | 7195 | const operand = try self.resolveInst(extra.rhs); |
| 7271 | 7196 | |
| 7272 | | const loaded_vector = blk: { |
| 7273 | | const elem_llvm_ty = (try o.lowerType(vector_ptr_ty.childType(mod))).toLlvm(&o.builder); |
| 7274 | | const load_inst = self.builder.buildLoad(elem_llvm_ty, vector_ptr, ""); |
| 7275 | | load_inst.setAlignment(vector_ptr_ty.ptrAlignment(mod)); |
| 7276 | | load_inst.setVolatile(llvm.Bool.fromBool(vector_ptr_ty.isVolatilePtr(mod))); |
| 7277 | | break :blk load_inst; |
| 7197 | const kind: Builder.MemoryAccessKind = switch (vector_ptr_ty.isVolatilePtr(mod)) { |
| 7198 | false => .normal, |
| 7199 | true => .@"volatile", |
| 7278 | 7200 | }; |
| 7279 | | const modified_vector = self.builder.buildInsertElement(loaded_vector, operand, index, ""); |
| 7280 | | try self.store(vector_ptr, vector_ptr_ty, modified_vector, .NotAtomic); |
| 7281 | | return null; |
| 7201 | const elem_llvm_ty = try o.lowerType(vector_ptr_ty.childType(mod)); |
| 7202 | const alignment = Builder.Alignment.fromByteUnits(vector_ptr_ty.ptrAlignment(mod)); |
| 7203 | const loaded = try self.wip.load(kind, elem_llvm_ty, vector_ptr, alignment, ""); |
| 7204 | |
| 7205 | const new_vector = try self.wip.insertElement(loaded, operand, index, ""); |
| 7206 | _ = try self.store(vector_ptr, vector_ptr_ty, new_vector, .none); |
| 7207 | return .none; |
| 7282 | 7208 | } |
| 7283 | 7209 | |
| 7284 | | fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7210 | fn airMin(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7285 | 7211 | const o = self.dg.object; |
| 7286 | 7212 | const mod = o.module; |
| 7287 | 7213 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| ... | ... | @@ -7290,11 +7216,13 @@ pub const FuncGen = struct { |
| 7290 | 7216 | const scalar_ty = self.typeOfIndex(inst).scalarType(mod); |
| 7291 | 7217 | |
| 7292 | 7218 | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.fmin, scalar_ty, 2, .{ lhs, rhs }); |
| 7293 | | if (scalar_ty.isSignedInt(mod)) return self.builder.buildSMin(lhs, rhs, ""); |
| 7294 | | return self.builder.buildUMin(lhs, rhs, ""); |
| 7219 | return self.wip.bin(if (scalar_ty.isSignedInt(mod)) |
| 7220 | .@"llvm.smin." |
| 7221 | else |
| 7222 | .@"llvm.umin.", lhs, rhs, ""); |
| 7295 | 7223 | } |
| 7296 | 7224 | |
| 7297 | | fn airMax(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7225 | fn airMax(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7298 | 7226 | const o = self.dg.object; |
| 7299 | 7227 | const mod = o.module; |
| 7300 | 7228 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| ... | ... | @@ -7303,26 +7231,23 @@ pub const FuncGen = struct { |
| 7303 | 7231 | const scalar_ty = self.typeOfIndex(inst).scalarType(mod); |
| 7304 | 7232 | |
| 7305 | 7233 | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.fmax, scalar_ty, 2, .{ lhs, rhs }); |
| 7306 | | if (scalar_ty.isSignedInt(mod)) return self.builder.buildSMax(lhs, rhs, ""); |
| 7307 | | return self.builder.buildUMax(lhs, rhs, ""); |
| 7234 | return self.wip.bin(if (scalar_ty.isSignedInt(mod)) |
| 7235 | .@"llvm.smax." |
| 7236 | else |
| 7237 | .@"llvm.umax.", lhs, rhs, ""); |
| 7308 | 7238 | } |
| 7309 | 7239 | |
| 7310 | | fn airSlice(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7240 | fn airSlice(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7311 | 7241 | const o = self.dg.object; |
| 7312 | 7242 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 7313 | 7243 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 7314 | 7244 | const ptr = try self.resolveInst(bin_op.lhs); |
| 7315 | 7245 | const len = try self.resolveInst(bin_op.rhs); |
| 7316 | 7246 | const inst_ty = self.typeOfIndex(inst); |
| 7317 | | const llvm_slice_ty = (try o.lowerType(inst_ty)).toLlvm(&o.builder); |
| 7318 | | |
| 7319 | | // In case of slicing a global, the result type looks something like `{ i8*, i64 }` |
| 7320 | | // but `ptr` is pointing to the global directly. |
| 7321 | | const partial = self.builder.buildInsertValue(llvm_slice_ty.getUndef(), ptr, 0, ""); |
| 7322 | | return self.builder.buildInsertValue(partial, len, 1, ""); |
| 7247 | return self.wip.buildAggregate(try o.lowerType(inst_ty), &.{ ptr, len }, ""); |
| 7323 | 7248 | } |
| 7324 | 7249 | |
| 7325 | | fn airAdd(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value { |
| 7250 | fn airAdd(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !Builder.Value { |
| 7326 | 7251 | self.builder.setFastMath(want_fast_math); |
| 7327 | 7252 | |
| 7328 | 7253 | const o = self.dg.object; |
| ... | ... | @@ -7334,8 +7259,7 @@ pub const FuncGen = struct { |
| 7334 | 7259 | const scalar_ty = inst_ty.scalarType(mod); |
| 7335 | 7260 | |
| 7336 | 7261 | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.add, inst_ty, 2, .{ lhs, rhs }); |
| 7337 | | if (scalar_ty.isSignedInt(mod)) return self.builder.buildNSWAdd(lhs, rhs, ""); |
| 7338 | | return self.builder.buildNUWAdd(lhs, rhs, ""); |
| 7262 | return self.wip.bin(if (scalar_ty.isSignedInt(mod)) .@"add nsw" else .@"add nuw", lhs, rhs, ""); |
| 7339 | 7263 | } |
| 7340 | 7264 | |
| 7341 | 7265 | fn airSafeArithmetic( |
| ... | ... | @@ -7343,7 +7267,7 @@ pub const FuncGen = struct { |
| 7343 | 7267 | inst: Air.Inst.Index, |
| 7344 | 7268 | signed_intrinsic: []const u8, |
| 7345 | 7269 | unsigned_intrinsic: []const u8, |
| 7346 | | ) !?*llvm.Value { |
| 7270 | ) !Builder.Value { |
| 7347 | 7271 | const o = fg.dg.object; |
| 7348 | 7272 | const mod = o.module; |
| 7349 | 7273 | |
| ... | ... | @@ -7358,44 +7282,51 @@ pub const FuncGen = struct { |
| 7358 | 7282 | true => signed_intrinsic, |
| 7359 | 7283 | false => unsigned_intrinsic, |
| 7360 | 7284 | }; |
| 7361 | | const llvm_fn = try fg.getIntrinsic(intrinsic_name, &.{try o.lowerType(inst_ty)}); |
| 7362 | | const result_struct = fg.builder.buildCall( |
| 7363 | | llvm_fn.globalGetValueType(), |
| 7285 | const llvm_inst_ty = try o.lowerType(inst_ty); |
| 7286 | const llvm_ret_ty = try o.builder.structType(.normal, &.{ |
| 7287 | llvm_inst_ty, |
| 7288 | try llvm_inst_ty.changeScalar(.i1, &o.builder), |
| 7289 | }); |
| 7290 | const llvm_fn_ty = try o.builder.fnType(llvm_ret_ty, &.{ llvm_inst_ty, llvm_inst_ty }, .normal); |
| 7291 | const llvm_fn = try fg.getIntrinsic(intrinsic_name, &.{llvm_inst_ty}); |
| 7292 | const result_struct = (try fg.wip.unimplemented(llvm_ret_ty, "")).finish(fg.builder.buildCall( |
| 7293 | llvm_fn_ty.toLlvm(&o.builder), |
| 7364 | 7294 | llvm_fn, |
| 7365 | | &[_]*llvm.Value{ lhs, rhs }, |
| 7295 | &[_]*llvm.Value{ lhs.toLlvm(&fg.wip), rhs.toLlvm(&fg.wip) }, |
| 7366 | 7296 | 2, |
| 7367 | 7297 | .Fast, |
| 7368 | 7298 | .Auto, |
| 7369 | 7299 | "", |
| 7370 | | ); |
| 7371 | | const overflow_bit = fg.builder.buildExtractValue(result_struct, 1, ""); |
| 7300 | ), &fg.wip); |
| 7301 | const overflow_bit = try fg.wip.extractValue(result_struct, &.{1}, ""); |
| 7372 | 7302 | const scalar_overflow_bit = switch (is_scalar) { |
| 7373 | 7303 | true => overflow_bit, |
| 7374 | | false => fg.builder.buildOrReduce(overflow_bit), |
| 7304 | false => (try fg.wip.unimplemented(.i1, "")).finish( |
| 7305 | fg.builder.buildOrReduce(overflow_bit.toLlvm(&fg.wip)), |
| 7306 | &fg.wip, |
| 7307 | ), |
| 7375 | 7308 | }; |
| 7376 | 7309 | |
| 7377 | | const fail_block = try fg.wip.block("OverflowFail"); |
| 7378 | | const ok_block = try fg.wip.block("OverflowOk"); |
| 7379 | | _ = fg.builder.buildCondBr(scalar_overflow_bit, fail_block.toLlvm(&fg.wip), ok_block.toLlvm(&fg.wip)); |
| 7310 | const fail_block = try fg.wip.block(1, "OverflowFail"); |
| 7311 | const ok_block = try fg.wip.block(1, "OverflowOk"); |
| 7312 | _ = try fg.wip.brCond(scalar_overflow_bit, fail_block, ok_block); |
| 7380 | 7313 | |
| 7381 | 7314 | fg.wip.cursor = .{ .block = fail_block }; |
| 7382 | | fg.builder.positionBuilderAtEnd(fail_block.toLlvm(&fg.wip)); |
| 7383 | 7315 | try fg.buildSimplePanic(.integer_overflow); |
| 7384 | 7316 | |
| 7385 | 7317 | fg.wip.cursor = .{ .block = ok_block }; |
| 7386 | | fg.builder.positionBuilderAtEnd(ok_block.toLlvm(&fg.wip)); |
| 7387 | | return fg.builder.buildExtractValue(result_struct, 0, ""); |
| 7318 | return fg.wip.extractValue(result_struct, &.{0}, ""); |
| 7388 | 7319 | } |
| 7389 | 7320 | |
| 7390 | | fn airAddWrap(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7321 | fn airAddWrap(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7391 | 7322 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 7392 | 7323 | const lhs = try self.resolveInst(bin_op.lhs); |
| 7393 | 7324 | const rhs = try self.resolveInst(bin_op.rhs); |
| 7394 | 7325 | |
| 7395 | | return self.builder.buildAdd(lhs, rhs, ""); |
| 7326 | return self.wip.bin(.add, lhs, rhs, ""); |
| 7396 | 7327 | } |
| 7397 | 7328 | |
| 7398 | | fn airAddSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7329 | fn airAddSat(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7399 | 7330 | const o = self.dg.object; |
| 7400 | 7331 | const mod = o.module; |
| 7401 | 7332 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| ... | ... | @@ -7405,12 +7336,13 @@ pub const FuncGen = struct { |
| 7405 | 7336 | const scalar_ty = inst_ty.scalarType(mod); |
| 7406 | 7337 | |
| 7407 | 7338 | if (scalar_ty.isAnyFloat()) return self.todo("saturating float add", .{}); |
| 7408 | | if (scalar_ty.isSignedInt(mod)) return self.builder.buildSAddSat(lhs, rhs, ""); |
| 7409 | | |
| 7410 | | return self.builder.buildUAddSat(lhs, rhs, ""); |
| 7339 | return self.wip.bin(if (scalar_ty.isSignedInt(mod)) |
| 7340 | .@"llvm.sadd.sat." |
| 7341 | else |
| 7342 | .@"llvm.uadd.sat.", lhs, rhs, ""); |
| 7411 | 7343 | } |
| 7412 | 7344 | |
| 7413 | | fn airSub(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value { |
| 7345 | fn airSub(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !Builder.Value { |
| 7414 | 7346 | self.builder.setFastMath(want_fast_math); |
| 7415 | 7347 | |
| 7416 | 7348 | const o = self.dg.object; |
| ... | ... | @@ -7422,19 +7354,18 @@ pub const FuncGen = struct { |
| 7422 | 7354 | const scalar_ty = inst_ty.scalarType(mod); |
| 7423 | 7355 | |
| 7424 | 7356 | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.sub, inst_ty, 2, .{ lhs, rhs }); |
| 7425 | | if (scalar_ty.isSignedInt(mod)) return self.builder.buildNSWSub(lhs, rhs, ""); |
| 7426 | | return self.builder.buildNUWSub(lhs, rhs, ""); |
| 7357 | return self.wip.bin(if (scalar_ty.isSignedInt(mod)) .@"sub nsw" else .@"sub nuw", lhs, rhs, ""); |
| 7427 | 7358 | } |
| 7428 | 7359 | |
| 7429 | | fn airSubWrap(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7360 | fn airSubWrap(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7430 | 7361 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 7431 | 7362 | const lhs = try self.resolveInst(bin_op.lhs); |
| 7432 | 7363 | const rhs = try self.resolveInst(bin_op.rhs); |
| 7433 | 7364 | |
| 7434 | | return self.builder.buildSub(lhs, rhs, ""); |
| 7365 | return self.wip.bin(.sub, lhs, rhs, ""); |
| 7435 | 7366 | } |
| 7436 | 7367 | |
| 7437 | | fn airSubSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7368 | fn airSubSat(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7438 | 7369 | const o = self.dg.object; |
| 7439 | 7370 | const mod = o.module; |
| 7440 | 7371 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| ... | ... | @@ -7444,11 +7375,13 @@ pub const FuncGen = struct { |
| 7444 | 7375 | const scalar_ty = inst_ty.scalarType(mod); |
| 7445 | 7376 | |
| 7446 | 7377 | if (scalar_ty.isAnyFloat()) return self.todo("saturating float sub", .{}); |
| 7447 | | if (scalar_ty.isSignedInt(mod)) return self.builder.buildSSubSat(lhs, rhs, ""); |
| 7448 | | return self.builder.buildUSubSat(lhs, rhs, ""); |
| 7378 | return self.wip.bin(if (scalar_ty.isSignedInt(mod)) |
| 7379 | .@"llvm.ssub.sat." |
| 7380 | else |
| 7381 | .@"llvm.usub.sat.", lhs, rhs, ""); |
| 7449 | 7382 | } |
| 7450 | 7383 | |
| 7451 | | fn airMul(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value { |
| 7384 | fn airMul(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !Builder.Value { |
| 7452 | 7385 | self.builder.setFastMath(want_fast_math); |
| 7453 | 7386 | |
| 7454 | 7387 | const o = self.dg.object; |
| ... | ... | @@ -7460,19 +7393,18 @@ pub const FuncGen = struct { |
| 7460 | 7393 | const scalar_ty = inst_ty.scalarType(mod); |
| 7461 | 7394 | |
| 7462 | 7395 | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.mul, inst_ty, 2, .{ lhs, rhs }); |
| 7463 | | if (scalar_ty.isSignedInt(mod)) return self.builder.buildNSWMul(lhs, rhs, ""); |
| 7464 | | return self.builder.buildNUWMul(lhs, rhs, ""); |
| 7396 | return self.wip.bin(if (scalar_ty.isSignedInt(mod)) .@"mul nsw" else .@"mul nuw", lhs, rhs, ""); |
| 7465 | 7397 | } |
| 7466 | 7398 | |
| 7467 | | fn airMulWrap(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7399 | fn airMulWrap(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7468 | 7400 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 7469 | 7401 | const lhs = try self.resolveInst(bin_op.lhs); |
| 7470 | 7402 | const rhs = try self.resolveInst(bin_op.rhs); |
| 7471 | 7403 | |
| 7472 | | return self.builder.buildMul(lhs, rhs, ""); |
| 7404 | return self.wip.bin(.mul, lhs, rhs, ""); |
| 7473 | 7405 | } |
| 7474 | 7406 | |
| 7475 | | fn airMulSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7407 | fn airMulSat(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7476 | 7408 | const o = self.dg.object; |
| 7477 | 7409 | const mod = o.module; |
| 7478 | 7410 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| ... | ... | @@ -7482,11 +7414,13 @@ pub const FuncGen = struct { |
| 7482 | 7414 | const scalar_ty = inst_ty.scalarType(mod); |
| 7483 | 7415 | |
| 7484 | 7416 | if (scalar_ty.isAnyFloat()) return self.todo("saturating float mul", .{}); |
| 7485 | | if (scalar_ty.isSignedInt(mod)) return self.builder.buildSMulFixSat(lhs, rhs, ""); |
| 7486 | | return self.builder.buildUMulFixSat(lhs, rhs, ""); |
| 7417 | return self.wip.bin(if (scalar_ty.isSignedInt(mod)) |
| 7418 | .@"llvm.smul.fix.sat." |
| 7419 | else |
| 7420 | .@"llvm.umul.fix.sat.", lhs, rhs, ""); |
| 7487 | 7421 | } |
| 7488 | 7422 | |
| 7489 | | fn airDivFloat(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value { |
| 7423 | fn airDivFloat(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !Builder.Value { |
| 7490 | 7424 | self.builder.setFastMath(want_fast_math); |
| 7491 | 7425 | |
| 7492 | 7426 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| ... | ... | @@ -7497,7 +7431,7 @@ pub const FuncGen = struct { |
| 7497 | 7431 | return self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs }); |
| 7498 | 7432 | } |
| 7499 | 7433 | |
| 7500 | | fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value { |
| 7434 | fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !Builder.Value { |
| 7501 | 7435 | self.builder.setFastMath(want_fast_math); |
| 7502 | 7436 | |
| 7503 | 7437 | const o = self.dg.object; |
| ... | ... | @@ -7512,11 +7446,10 @@ pub const FuncGen = struct { |
| 7512 | 7446 | const result = try self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs }); |
| 7513 | 7447 | return self.buildFloatOp(.trunc, inst_ty, 1, .{result}); |
| 7514 | 7448 | } |
| 7515 | | if (scalar_ty.isSignedInt(mod)) return self.builder.buildSDiv(lhs, rhs, ""); |
| 7516 | | return self.builder.buildUDiv(lhs, rhs, ""); |
| 7449 | return self.wip.bin(if (scalar_ty.isSignedInt(mod)) .sdiv else .udiv, lhs, rhs, ""); |
| 7517 | 7450 | } |
| 7518 | 7451 | |
| 7519 | | fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value { |
| 7452 | fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !Builder.Value { |
| 7520 | 7453 | self.builder.setFastMath(want_fast_math); |
| 7521 | 7454 | |
| 7522 | 7455 | const o = self.dg.object; |
| ... | ... | @@ -7533,24 +7466,24 @@ pub const FuncGen = struct { |
| 7533 | 7466 | } |
| 7534 | 7467 | if (scalar_ty.isSignedInt(mod)) { |
| 7535 | 7468 | const inst_llvm_ty = try o.lowerType(inst_ty); |
| 7536 | | const bit_size_minus_one = try o.builder.splatConst(inst_llvm_ty, try o.builder.intConst( |
| 7469 | const bit_size_minus_one = try o.builder.splatValue(inst_llvm_ty, try o.builder.intConst( |
| 7537 | 7470 | inst_llvm_ty.scalarType(&o.builder), |
| 7538 | 7471 | inst_llvm_ty.scalarBits(&o.builder) - 1, |
| 7539 | 7472 | )); |
| 7540 | 7473 | |
| 7541 | | const div = self.builder.buildSDiv(lhs, rhs, ""); |
| 7542 | | const rem = self.builder.buildSRem(lhs, rhs, ""); |
| 7543 | | const div_sign = self.builder.buildXor(lhs, rhs, ""); |
| 7544 | | const div_sign_mask = self.builder.buildAShr(div_sign, bit_size_minus_one.toLlvm(&o.builder), ""); |
| 7545 | | const zero = try o.builder.zeroInitConst(inst_llvm_ty); |
| 7546 | | const rem_nonzero = self.builder.buildICmp(.NE, rem, zero.toLlvm(&o.builder), ""); |
| 7547 | | const correction = self.builder.buildSelect(rem_nonzero, div_sign_mask, zero.toLlvm(&o.builder), ""); |
| 7548 | | return self.builder.buildNSWAdd(div, correction, ""); |
| 7474 | const div = try self.wip.bin(.sdiv, lhs, rhs, ""); |
| 7475 | const rem = try self.wip.bin(.srem, lhs, rhs, ""); |
| 7476 | const div_sign = try self.wip.bin(.xor, lhs, rhs, ""); |
| 7477 | const div_sign_mask = try self.wip.bin(.ashr, div_sign, bit_size_minus_one, ""); |
| 7478 | const zero = try o.builder.zeroInitValue(inst_llvm_ty); |
| 7479 | const rem_nonzero = try self.wip.icmp(.ne, rem, zero, ""); |
| 7480 | const correction = try self.wip.select(rem_nonzero, div_sign_mask, zero, ""); |
| 7481 | return self.wip.bin(.@"add nsw", div, correction, ""); |
| 7549 | 7482 | } |
| 7550 | | return self.builder.buildUDiv(lhs, rhs, ""); |
| 7483 | return self.wip.bin(.udiv, lhs, rhs, ""); |
| 7551 | 7484 | } |
| 7552 | 7485 | |
| 7553 | | fn airDivExact(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value { |
| 7486 | fn airDivExact(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !Builder.Value { |
| 7554 | 7487 | self.builder.setFastMath(want_fast_math); |
| 7555 | 7488 | |
| 7556 | 7489 | const o = self.dg.object; |
| ... | ... | @@ -7562,11 +7495,13 @@ pub const FuncGen = struct { |
| 7562 | 7495 | const scalar_ty = inst_ty.scalarType(mod); |
| 7563 | 7496 | |
| 7564 | 7497 | if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs }); |
| 7565 | | if (scalar_ty.isSignedInt(mod)) return self.builder.buildExactSDiv(lhs, rhs, ""); |
| 7566 | | return self.builder.buildExactUDiv(lhs, rhs, ""); |
| 7498 | return self.wip.bin(if (scalar_ty.isSignedInt(mod)) |
| 7499 | .@"sdiv exact" |
| 7500 | else |
| 7501 | .@"udiv exact", lhs, rhs, ""); |
| 7567 | 7502 | } |
| 7568 | 7503 | |
| 7569 | | fn airRem(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value { |
| 7504 | fn airRem(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !Builder.Value { |
| 7570 | 7505 | self.builder.setFastMath(want_fast_math); |
| 7571 | 7506 | |
| 7572 | 7507 | const o = self.dg.object; |
| ... | ... | @@ -7578,11 +7513,13 @@ pub const FuncGen = struct { |
| 7578 | 7513 | const scalar_ty = inst_ty.scalarType(mod); |
| 7579 | 7514 | |
| 7580 | 7515 | if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.fmod, inst_ty, 2, .{ lhs, rhs }); |
| 7581 | | if (scalar_ty.isSignedInt(mod)) return self.builder.buildSRem(lhs, rhs, ""); |
| 7582 | | return self.builder.buildURem(lhs, rhs, ""); |
| 7516 | return self.wip.bin(if (scalar_ty.isSignedInt(mod)) |
| 7517 | .srem |
| 7518 | else |
| 7519 | .urem, lhs, rhs, ""); |
| 7583 | 7520 | } |
| 7584 | 7521 | |
| 7585 | | fn airMod(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value { |
| 7522 | fn airMod(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !Builder.Value { |
| 7586 | 7523 | self.builder.setFastMath(want_fast_math); |
| 7587 | 7524 | |
| 7588 | 7525 | const o = self.dg.object; |
| ... | ... | @@ -7598,29 +7535,29 @@ pub const FuncGen = struct { |
| 7598 | 7535 | const a = try self.buildFloatOp(.fmod, inst_ty, 2, .{ lhs, rhs }); |
| 7599 | 7536 | const b = try self.buildFloatOp(.add, inst_ty, 2, .{ a, rhs }); |
| 7600 | 7537 | const c = try self.buildFloatOp(.fmod, inst_ty, 2, .{ b, rhs }); |
| 7601 | | const zero = try o.builder.zeroInitConst(inst_llvm_ty); |
| 7602 | | const ltz = try self.buildFloatCmp(.lt, inst_ty, .{ lhs, zero.toLlvm(&o.builder) }); |
| 7603 | | return self.builder.buildSelect(ltz, c, a, ""); |
| 7538 | const zero = try o.builder.zeroInitValue(inst_llvm_ty); |
| 7539 | const ltz = try self.buildFloatCmp(.lt, inst_ty, .{ lhs, zero }); |
| 7540 | return self.wip.select(ltz, c, a, ""); |
| 7604 | 7541 | } |
| 7605 | 7542 | if (scalar_ty.isSignedInt(mod)) { |
| 7606 | | const bit_size_minus_one = try o.builder.splatConst(inst_llvm_ty, try o.builder.intConst( |
| 7543 | const bit_size_minus_one = try o.builder.splatValue(inst_llvm_ty, try o.builder.intConst( |
| 7607 | 7544 | inst_llvm_ty.scalarType(&o.builder), |
| 7608 | 7545 | inst_llvm_ty.scalarBits(&o.builder) - 1, |
| 7609 | 7546 | )); |
| 7610 | 7547 | |
| 7611 | | const rem = self.builder.buildSRem(lhs, rhs, ""); |
| 7612 | | const div_sign = self.builder.buildXor(lhs, rhs, ""); |
| 7613 | | const div_sign_mask = self.builder.buildAShr(div_sign, bit_size_minus_one.toLlvm(&o.builder), ""); |
| 7614 | | const rhs_masked = self.builder.buildAnd(rhs, div_sign_mask, ""); |
| 7615 | | const zero = try o.builder.zeroInitConst(inst_llvm_ty); |
| 7616 | | const rem_nonzero = self.builder.buildICmp(.NE, rem, zero.toLlvm(&o.builder), ""); |
| 7617 | | const correction = self.builder.buildSelect(rem_nonzero, rhs_masked, zero.toLlvm(&o.builder), ""); |
| 7618 | | return self.builder.buildNSWAdd(rem, correction, ""); |
| 7548 | const rem = try self.wip.bin(.srem, lhs, rhs, ""); |
| 7549 | const div_sign = try self.wip.bin(.xor, lhs, rhs, ""); |
| 7550 | const div_sign_mask = try self.wip.bin(.ashr, div_sign, bit_size_minus_one, ""); |
| 7551 | const rhs_masked = try self.wip.bin(.@"and", rhs, div_sign_mask, ""); |
| 7552 | const zero = try o.builder.zeroInitValue(inst_llvm_ty); |
| 7553 | const rem_nonzero = try self.wip.icmp(.ne, rem, zero, ""); |
| 7554 | const correction = try self.wip.select(rem_nonzero, rhs_masked, zero, ""); |
| 7555 | return self.wip.bin(.@"add nsw", rem, correction, ""); |
| 7619 | 7556 | } |
| 7620 | | return self.builder.buildURem(lhs, rhs, ""); |
| 7557 | return self.wip.bin(.urem, lhs, rhs, ""); |
| 7621 | 7558 | } |
| 7622 | 7559 | |
| 7623 | | fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7560 | fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7624 | 7561 | const o = self.dg.object; |
| 7625 | 7562 | const mod = o.module; |
| 7626 | 7563 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| ... | ... | @@ -7628,55 +7565,39 @@ pub const FuncGen = struct { |
| 7628 | 7565 | const ptr = try self.resolveInst(bin_op.lhs); |
| 7629 | 7566 | const offset = try self.resolveInst(bin_op.rhs); |
| 7630 | 7567 | const ptr_ty = self.typeOf(bin_op.lhs); |
| 7631 | | const llvm_elem_ty = (try o.lowerPtrElemTy(ptr_ty.childType(mod))).toLlvm(&o.builder); |
| 7568 | const llvm_elem_ty = try o.lowerPtrElemTy(ptr_ty.childType(mod)); |
| 7632 | 7569 | switch (ptr_ty.ptrSize(mod)) { |
| 7633 | | .One => { |
| 7634 | | // It's a pointer to an array, so according to LLVM we need an extra GEP index. |
| 7635 | | const indices: [2]*llvm.Value = .{ |
| 7636 | | (try o.builder.intConst(.i32, 0)).toLlvm(&o.builder), |
| 7637 | | offset, |
| 7638 | | }; |
| 7639 | | return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, ""); |
| 7640 | | }, |
| 7641 | | .C, .Many => { |
| 7642 | | const indices: [1]*llvm.Value = .{offset}; |
| 7643 | | return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, ""); |
| 7644 | | }, |
| 7570 | // It's a pointer to an array, so according to LLVM we need an extra GEP index. |
| 7571 | .One => return self.wip.gep(.inbounds, llvm_elem_ty, ptr, &.{ |
| 7572 | try o.builder.intValue(try o.lowerType(Type.usize), 0), offset, |
| 7573 | }, ""), |
| 7574 | .C, .Many => return self.wip.gep(.inbounds, llvm_elem_ty, ptr, &.{offset}, ""), |
| 7645 | 7575 | .Slice => { |
| 7646 | | const base = self.builder.buildExtractValue(ptr, 0, ""); |
| 7647 | | const indices: [1]*llvm.Value = .{offset}; |
| 7648 | | return self.builder.buildInBoundsGEP(llvm_elem_ty, base, &indices, indices.len, ""); |
| 7576 | const base = try self.wip.extractValue(ptr, &.{0}, ""); |
| 7577 | return self.wip.gep(.inbounds, llvm_elem_ty, base, &.{offset}, ""); |
| 7649 | 7578 | }, |
| 7650 | 7579 | } |
| 7651 | 7580 | } |
| 7652 | 7581 | |
| 7653 | | fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7582 | fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7654 | 7583 | const o = self.dg.object; |
| 7655 | 7584 | const mod = o.module; |
| 7656 | 7585 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 7657 | 7586 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 7658 | 7587 | const ptr = try self.resolveInst(bin_op.lhs); |
| 7659 | 7588 | const offset = try self.resolveInst(bin_op.rhs); |
| 7660 | | const negative_offset = self.builder.buildNeg(offset, ""); |
| 7589 | const negative_offset = try self.wip.neg(offset, ""); |
| 7661 | 7590 | const ptr_ty = self.typeOf(bin_op.lhs); |
| 7662 | | const llvm_elem_ty = (try o.lowerPtrElemTy(ptr_ty.childType(mod))).toLlvm(&o.builder); |
| 7591 | const llvm_elem_ty = try o.lowerPtrElemTy(ptr_ty.childType(mod)); |
| 7663 | 7592 | switch (ptr_ty.ptrSize(mod)) { |
| 7664 | | .One => { |
| 7665 | | // It's a pointer to an array, so according to LLVM we need an extra GEP index. |
| 7666 | | const indices: [2]*llvm.Value = .{ |
| 7667 | | (try o.builder.intConst(.i32, 0)).toLlvm(&o.builder), |
| 7668 | | negative_offset, |
| 7669 | | }; |
| 7670 | | return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, ""); |
| 7671 | | }, |
| 7672 | | .C, .Many => { |
| 7673 | | const indices: [1]*llvm.Value = .{negative_offset}; |
| 7674 | | return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, ""); |
| 7675 | | }, |
| 7593 | // It's a pointer to an array, so according to LLVM we need an extra GEP index. |
| 7594 | .One => return self.wip.gep(.inbounds, llvm_elem_ty, ptr, &.{ |
| 7595 | try o.builder.intValue(try o.lowerType(Type.usize), 0), negative_offset, |
| 7596 | }, ""), |
| 7597 | .C, .Many => return self.wip.gep(.inbounds, llvm_elem_ty, ptr, &.{negative_offset}, ""), |
| 7676 | 7598 | .Slice => { |
| 7677 | | const base = self.builder.buildExtractValue(ptr, 0, ""); |
| 7678 | | const indices: [1]*llvm.Value = .{negative_offset}; |
| 7679 | | return self.builder.buildInBoundsGEP(llvm_elem_ty, base, &indices, indices.len, ""); |
| 7599 | const base = try self.wip.extractValue(ptr, &.{0}, ""); |
| 7600 | return self.wip.gep(.inbounds, llvm_elem_ty, base, &.{negative_offset}, ""); |
| 7680 | 7601 | }, |
| 7681 | 7602 | } |
| 7682 | 7603 | } |
| ... | ... | @@ -7686,7 +7607,7 @@ pub const FuncGen = struct { |
| 7686 | 7607 | inst: Air.Inst.Index, |
| 7687 | 7608 | signed_intrinsic: []const u8, |
| 7688 | 7609 | unsigned_intrinsic: []const u8, |
| 7689 | | ) !?*llvm.Value { |
| 7610 | ) !Builder.Value { |
| 7690 | 7611 | const o = self.dg.object; |
| 7691 | 7612 | const mod = o.module; |
| 7692 | 7613 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| ... | ... | @@ -7701,59 +7622,91 @@ pub const FuncGen = struct { |
| 7701 | 7622 | |
| 7702 | 7623 | const intrinsic_name = if (scalar_ty.isSignedInt(mod)) signed_intrinsic else unsigned_intrinsic; |
| 7703 | 7624 | |
| 7704 | | const llvm_dest_ty = (try o.lowerType(dest_ty)).toLlvm(&o.builder); |
| 7625 | const llvm_dest_ty = try o.lowerType(dest_ty); |
| 7626 | const llvm_lhs_ty = try o.lowerType(lhs_ty); |
| 7705 | 7627 | |
| 7706 | | const llvm_fn = try self.getIntrinsic(intrinsic_name, &.{try o.lowerType(lhs_ty)}); |
| 7707 | | const result_struct = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &[_]*llvm.Value{ lhs, rhs }, 2, .Fast, .Auto, ""); |
| 7628 | const llvm_fn = try self.getIntrinsic(intrinsic_name, &.{llvm_lhs_ty}); |
| 7629 | const llvm_ret_ty = try o.builder.structType( |
| 7630 | .normal, |
| 7631 | &.{ llvm_lhs_ty, try llvm_lhs_ty.changeScalar(.i1, &o.builder) }, |
| 7632 | ); |
| 7633 | const llvm_fn_ty = try o.builder.fnType(llvm_ret_ty, &.{ llvm_lhs_ty, llvm_lhs_ty }, .normal); |
| 7634 | const result_struct = (try self.wip.unimplemented(llvm_ret_ty, "")).finish( |
| 7635 | self.builder.buildCall( |
| 7636 | llvm_fn_ty.toLlvm(&o.builder), |
| 7637 | llvm_fn, |
| 7638 | &[_]*llvm.Value{ lhs.toLlvm(&self.wip), rhs.toLlvm(&self.wip) }, |
| 7639 | 2, |
| 7640 | .Fast, |
| 7641 | .Auto, |
| 7642 | "", |
| 7643 | ), |
| 7644 | &self.wip, |
| 7645 | ); |
| 7708 | 7646 | |
| 7709 | | const result = self.builder.buildExtractValue(result_struct, 0, ""); |
| 7710 | | const overflow_bit = self.builder.buildExtractValue(result_struct, 1, ""); |
| 7647 | const result = try self.wip.extractValue(result_struct, &.{0}, ""); |
| 7648 | const overflow_bit = try self.wip.extractValue(result_struct, &.{1}, ""); |
| 7711 | 7649 | |
| 7712 | 7650 | const result_index = llvmField(dest_ty, 0, mod).?.index; |
| 7713 | 7651 | const overflow_index = llvmField(dest_ty, 1, mod).?.index; |
| 7714 | 7652 | |
| 7715 | 7653 | if (isByRef(dest_ty, mod)) { |
| 7716 | | const result_alignment = dest_ty.abiAlignment(mod); |
| 7654 | const result_alignment = Builder.Alignment.fromByteUnits(dest_ty.abiAlignment(mod)); |
| 7717 | 7655 | const alloca_inst = try self.buildAlloca(llvm_dest_ty, result_alignment); |
| 7718 | 7656 | { |
| 7719 | | const field_ptr = self.builder.buildStructGEP(llvm_dest_ty, alloca_inst, result_index, ""); |
| 7720 | | const store_inst = self.builder.buildStore(result, field_ptr); |
| 7721 | | store_inst.setAlignment(result_alignment); |
| 7657 | const field_ptr = try self.wip.gepStruct(llvm_dest_ty, alloca_inst, result_index, ""); |
| 7658 | _ = try self.wip.store(.normal, result, field_ptr, result_alignment); |
| 7722 | 7659 | } |
| 7723 | 7660 | { |
| 7724 | | const field_ptr = self.builder.buildStructGEP(llvm_dest_ty, alloca_inst, overflow_index, ""); |
| 7725 | | const store_inst = self.builder.buildStore(overflow_bit, field_ptr); |
| 7726 | | store_inst.setAlignment(1); |
| 7661 | const overflow_alignment = comptime Builder.Alignment.fromByteUnits(1); |
| 7662 | const field_ptr = try self.wip.gepStruct(llvm_dest_ty, alloca_inst, overflow_index, ""); |
| 7663 | _ = try self.wip.store(.normal, overflow_bit, field_ptr, overflow_alignment); |
| 7727 | 7664 | } |
| 7728 | 7665 | |
| 7729 | 7666 | return alloca_inst; |
| 7730 | 7667 | } |
| 7731 | 7668 | |
| 7732 | | const partial = self.builder.buildInsertValue(llvm_dest_ty.getUndef(), result, result_index, ""); |
| 7733 | | return self.builder.buildInsertValue(partial, overflow_bit, overflow_index, ""); |
| 7669 | var fields: [2]Builder.Value = undefined; |
| 7670 | fields[result_index] = result; |
| 7671 | fields[overflow_index] = overflow_bit; |
| 7672 | return self.wip.buildAggregate(llvm_dest_ty, &fields, ""); |
| 7734 | 7673 | } |
| 7735 | 7674 | |
| 7736 | 7675 | fn buildElementwiseCall( |
| 7737 | 7676 | self: *FuncGen, |
| 7738 | | llvm_fn: *llvm.Value, |
| 7739 | | args_vectors: []const *llvm.Value, |
| 7740 | | result_vector: *llvm.Value, |
| 7677 | llvm_fn: Builder.Function.Index, |
| 7678 | args_vectors: []const Builder.Value, |
| 7679 | result_vector: Builder.Value, |
| 7741 | 7680 | vector_len: usize, |
| 7742 | | ) !*llvm.Value { |
| 7681 | ) !Builder.Value { |
| 7743 | 7682 | const o = self.dg.object; |
| 7744 | 7683 | assert(args_vectors.len <= 3); |
| 7745 | 7684 | |
| 7685 | const llvm_fn_ty = llvm_fn.typeOf(&o.builder); |
| 7686 | const llvm_scalar_ty = llvm_fn_ty.functionReturn(&o.builder); |
| 7687 | |
| 7746 | 7688 | var i: usize = 0; |
| 7747 | 7689 | var result = result_vector; |
| 7748 | 7690 | while (i < vector_len) : (i += 1) { |
| 7749 | | const index_i32 = (try o.builder.intConst(.i32, i)).toLlvm(&o.builder); |
| 7691 | const index_i32 = try o.builder.intValue(.i32, i); |
| 7750 | 7692 | |
| 7751 | 7693 | var args: [3]*llvm.Value = undefined; |
| 7752 | | for (args_vectors, 0..) |arg_vector, k| { |
| 7753 | | args[k] = self.builder.buildExtractElement(arg_vector, index_i32, ""); |
| 7694 | for (args[0..args_vectors.len], args_vectors) |*arg_elem, arg_vector| { |
| 7695 | arg_elem.* = (try self.wip.extractElement(arg_vector, index_i32, "")).toLlvm(&self.wip); |
| 7754 | 7696 | } |
| 7755 | | const result_elem = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, @intCast(args_vectors.len), .C, .Auto, ""); |
| 7756 | | result = self.builder.buildInsertElement(result, result_elem, index_i32, ""); |
| 7697 | const result_elem = (try self.wip.unimplemented(llvm_scalar_ty, "")).finish( |
| 7698 | self.builder.buildCall( |
| 7699 | llvm_fn_ty.toLlvm(&o.builder), |
| 7700 | llvm_fn.toLlvm(&o.builder), |
| 7701 | &args, |
| 7702 | @intCast(args_vectors.len), |
| 7703 | .C, |
| 7704 | .Auto, |
| 7705 | "", |
| 7706 | ), |
| 7707 | &self.wip, |
| 7708 | ); |
| 7709 | result = try self.wip.insertElement(result, result_elem, index_i32, ""); |
| 7757 | 7710 | } |
| 7758 | 7711 | return result; |
| 7759 | 7712 | } |
| ... | ... | @@ -7763,29 +7716,29 @@ pub const FuncGen = struct { |
| 7763 | 7716 | fn_name: Builder.String, |
| 7764 | 7717 | param_types: []const Builder.Type, |
| 7765 | 7718 | return_type: Builder.Type, |
| 7766 | | ) Allocator.Error!*llvm.Value { |
| 7719 | ) Allocator.Error!Builder.Function.Index { |
| 7767 | 7720 | const o = self.dg.object; |
| 7768 | | const slice = fn_name.toSlice(&o.builder).?; |
| 7769 | | return o.llvm_module.getNamedFunction(slice) orelse b: { |
| 7770 | | const alias = o.llvm_module.getNamedGlobalAlias(slice.ptr, slice.len); |
| 7771 | | break :b if (alias) |a| a.getAliasee() else null; |
| 7772 | | } orelse b: { |
| 7773 | | const fn_type = try o.builder.fnType(return_type, param_types, .normal); |
| 7774 | | const f = o.llvm_module.addFunction(slice, fn_type.toLlvm(&o.builder)); |
| 7775 | | |
| 7776 | | var global = Builder.Global{ |
| 7777 | | .type = fn_type, |
| 7778 | | .kind = .{ .function = @enumFromInt(o.builder.functions.items.len) }, |
| 7779 | | }; |
| 7780 | | var function = Builder.Function{ |
| 7781 | | .global = @enumFromInt(o.builder.globals.count()), |
| 7782 | | }; |
| 7721 | if (o.builder.getGlobal(fn_name)) |global| return switch (global.ptrConst(&o.builder).kind) { |
| 7722 | .alias => |alias| alias.getAliasee(&o.builder).ptrConst(&o.builder).kind.function, |
| 7723 | .function => |function| function, |
| 7724 | else => unreachable, |
| 7725 | }; |
| 7783 | 7726 | |
| 7784 | | try o.builder.llvm.globals.append(self.gpa, f); |
| 7785 | | _ = try o.builder.addGlobal(fn_name, global); |
| 7786 | | try o.builder.functions.append(self.gpa, function); |
| 7787 | | break :b f; |
| 7727 | const fn_type = try o.builder.fnType(return_type, param_types, .normal); |
| 7728 | const f = o.llvm_module.addFunction(fn_name.toSlice(&o.builder).?, fn_type.toLlvm(&o.builder)); |
| 7729 | |
| 7730 | var global = Builder.Global{ |
| 7731 | .type = fn_type, |
| 7732 | .kind = .{ .function = @enumFromInt(o.builder.functions.items.len) }, |
| 7733 | }; |
| 7734 | var function = Builder.Function{ |
| 7735 | .global = @enumFromInt(o.builder.globals.count()), |
| 7788 | 7736 | }; |
| 7737 | |
| 7738 | try o.builder.llvm.globals.append(self.gpa, f); |
| 7739 | _ = try o.builder.addGlobal(fn_name, global); |
| 7740 | try o.builder.functions.append(self.gpa, function); |
| 7741 | return global.kind.function; |
| 7789 | 7742 | } |
| 7790 | 7743 | |
| 7791 | 7744 | /// Creates a floating point comparison by lowering to the appropriate |
| ... | ... | @@ -7794,8 +7747,8 @@ pub const FuncGen = struct { |
| 7794 | 7747 | self: *FuncGen, |
| 7795 | 7748 | pred: math.CompareOperator, |
| 7796 | 7749 | ty: Type, |
| 7797 | | params: [2]*llvm.Value, |
| 7798 | | ) !*llvm.Value { |
| 7750 | params: [2]Builder.Value, |
| 7751 | ) !Builder.Value { |
| 7799 | 7752 | const o = self.dg.object; |
| 7800 | 7753 | const mod = o.module; |
| 7801 | 7754 | const target = o.module.getTarget(); |
| ... | ... | @@ -7803,15 +7756,15 @@ pub const FuncGen = struct { |
| 7803 | 7756 | const scalar_llvm_ty = try o.lowerType(scalar_ty); |
| 7804 | 7757 | |
| 7805 | 7758 | if (intrinsicsAllowed(scalar_ty, target)) { |
| 7806 | | const llvm_predicate: llvm.RealPredicate = switch (pred) { |
| 7807 | | .eq => .OEQ, |
| 7808 | | .neq => .UNE, |
| 7809 | | .lt => .OLT, |
| 7810 | | .lte => .OLE, |
| 7811 | | .gt => .OGT, |
| 7812 | | .gte => .OGE, |
| 7759 | const cond: Builder.FloatCondition = switch (pred) { |
| 7760 | .eq => .oeq, |
| 7761 | .neq => .une, |
| 7762 | .lt => .olt, |
| 7763 | .lte => .ole, |
| 7764 | .gt => .ogt, |
| 7765 | .gte => .oge, |
| 7813 | 7766 | }; |
| 7814 | | return self.builder.buildFCmp(llvm_predicate, params[0], params[1], ""); |
| 7767 | return self.wip.fcmp(cond, params[0], params[1], ""); |
| 7815 | 7768 | } |
| 7816 | 7769 | |
| 7817 | 7770 | const float_bits = scalar_ty.floatBits(target); |
| ... | ... | @@ -7832,29 +7785,42 @@ pub const FuncGen = struct { |
| 7832 | 7785 | .i32, |
| 7833 | 7786 | ); |
| 7834 | 7787 | |
| 7835 | | const zero = (try o.builder.intConst(.i32, 0)).toLlvm(&o.builder); |
| 7836 | | const int_pred: llvm.IntPredicate = switch (pred) { |
| 7837 | | .eq => .EQ, |
| 7838 | | .neq => .NE, |
| 7839 | | .lt => .SLT, |
| 7840 | | .lte => .SLE, |
| 7841 | | .gt => .SGT, |
| 7842 | | .gte => .SGE, |
| 7788 | const zero = try o.builder.intConst(.i32, 0); |
| 7789 | const int_cond: Builder.IntegerCondition = switch (pred) { |
| 7790 | .eq => .eq, |
| 7791 | .neq => .ne, |
| 7792 | .lt => .slt, |
| 7793 | .lte => .sle, |
| 7794 | .gt => .sgt, |
| 7795 | .gte => .sge, |
| 7843 | 7796 | }; |
| 7844 | 7797 | |
| 7845 | 7798 | if (ty.zigTypeTag(mod) == .Vector) { |
| 7846 | 7799 | const vec_len = ty.vectorLen(mod); |
| 7847 | | const vector_result_ty = (try o.builder.vectorType(.normal, vec_len, .i32)).toLlvm(&o.builder); |
| 7800 | const vector_result_ty = try o.builder.vectorType(.normal, vec_len, .i32); |
| 7848 | 7801 | |
| 7849 | | var result = vector_result_ty.getUndef(); |
| 7850 | | result = try self.buildElementwiseCall(libc_fn, &params, result, vec_len); |
| 7802 | const init = try o.builder.poisonValue(vector_result_ty); |
| 7803 | const result = try self.buildElementwiseCall(libc_fn, &params, init, vec_len); |
| 7851 | 7804 | |
| 7852 | | const zero_vector = self.builder.buildVectorSplat(vec_len, zero, ""); |
| 7853 | | return self.builder.buildICmp(int_pred, result, zero_vector, ""); |
| 7805 | const zero_vector = try o.builder.splatValue(vector_result_ty, zero); |
| 7806 | return self.wip.icmp(int_cond, result, zero_vector, ""); |
| 7854 | 7807 | } |
| 7855 | 7808 | |
| 7856 | | const result = self.builder.buildCall(libc_fn.globalGetValueType(), libc_fn, &params, params.len, .C, .Auto, ""); |
| 7857 | | return self.builder.buildICmp(int_pred, result, zero, ""); |
| 7809 | const llvm_fn_ty = libc_fn.typeOf(&o.builder); |
| 7810 | const llvm_params = [2]*llvm.Value{ params[0].toLlvm(&self.wip), params[1].toLlvm(&self.wip) }; |
| 7811 | const result = (try self.wip.unimplemented( |
| 7812 | llvm_fn_ty.functionReturn(&o.builder), |
| 7813 | "", |
| 7814 | )).finish(self.builder.buildCall( |
| 7815 | libc_fn.typeOf(&o.builder).toLlvm(&o.builder), |
| 7816 | libc_fn.toLlvm(&o.builder), |
| 7817 | &llvm_params, |
| 7818 | llvm_params.len, |
| 7819 | .C, |
| 7820 | .Auto, |
| 7821 | "", |
| 7822 | ), &self.wip); |
| 7823 | return self.wip.icmp(int_cond, result, zero.toValue(), ""); |
| 7858 | 7824 | } |
| 7859 | 7825 | |
| 7860 | 7826 | const FloatOp = enum { |
| ... | ... | @@ -7896,26 +7862,25 @@ pub const FuncGen = struct { |
| 7896 | 7862 | comptime op: FloatOp, |
| 7897 | 7863 | ty: Type, |
| 7898 | 7864 | comptime params_len: usize, |
| 7899 | | params: [params_len]*llvm.Value, |
| 7900 | | ) !*llvm.Value { |
| 7865 | params: [params_len]Builder.Value, |
| 7866 | ) !Builder.Value { |
| 7901 | 7867 | const o = self.dg.object; |
| 7902 | 7868 | const mod = o.module; |
| 7903 | 7869 | const target = mod.getTarget(); |
| 7904 | 7870 | const scalar_ty = ty.scalarType(mod); |
| 7905 | 7871 | const llvm_ty = try o.lowerType(ty); |
| 7906 | | const scalar_llvm_ty = try o.lowerType(scalar_ty); |
| 7907 | 7872 | |
| 7908 | 7873 | const intrinsics_allowed = op != .tan and intrinsicsAllowed(scalar_ty, target); |
| 7909 | 7874 | const strat: FloatOpStrat = if (intrinsics_allowed) switch (op) { |
| 7910 | 7875 | // Some operations are dedicated LLVM instructions, not available as intrinsics |
| 7911 | | .neg => return self.builder.buildFNeg(params[0], ""), |
| 7912 | | .add => return self.builder.buildFAdd(params[0], params[1], ""), |
| 7913 | | .sub => return self.builder.buildFSub(params[0], params[1], ""), |
| 7914 | | .mul => return self.builder.buildFMul(params[0], params[1], ""), |
| 7915 | | .div => return self.builder.buildFDiv(params[0], params[1], ""), |
| 7916 | | .fmod => return self.builder.buildFRem(params[0], params[1], ""), |
| 7917 | | .fmax => return self.builder.buildMaxNum(params[0], params[1], ""), |
| 7918 | | .fmin => return self.builder.buildMinNum(params[0], params[1], ""), |
| 7876 | .neg => return self.wip.un(.fneg, params[0], ""), |
| 7877 | .add => return self.wip.bin(.fadd, params[0], params[1], ""), |
| 7878 | .sub => return self.wip.bin(.fsub, params[0], params[1], ""), |
| 7879 | .mul => return self.wip.bin(.fmul, params[0], params[1], ""), |
| 7880 | .div => return self.wip.bin(.fdiv, params[0], params[1], ""), |
| 7881 | .fmod => return self.wip.bin(.frem, params[0], params[1], ""), |
| 7882 | .fmax => return self.wip.bin(.@"llvm.maxnum.", params[0], params[1], ""), |
| 7883 | .fmin => return self.wip.bin(.@"llvm.minnum.", params[0], params[1], ""), |
| 7919 | 7884 | else => .{ .intrinsic = "llvm." ++ @tagName(op) }, |
| 7920 | 7885 | } else b: { |
| 7921 | 7886 | const float_bits = scalar_ty.floatBits(target); |
| ... | ... | @@ -7924,19 +7889,14 @@ pub const FuncGen = struct { |
| 7924 | 7889 | // In this case we can generate a softfloat negation by XORing the |
| 7925 | 7890 | // bits with a constant. |
| 7926 | 7891 | const int_ty = try o.builder.intType(@intCast(float_bits)); |
| 7927 | | const one = try o.builder.intConst(int_ty, 1); |
| 7928 | | const shift_amt = try o.builder.intConst(int_ty, float_bits - 1); |
| 7929 | | const sign_mask = try o.builder.binConst(.shl, one, shift_amt); |
| 7930 | | const result = if (ty.zigTypeTag(mod) == .Vector) blk: { |
| 7931 | | const splat_sign_mask = self.builder.buildVectorSplat(ty.vectorLen(mod), sign_mask.toLlvm(&o.builder), ""); |
| 7932 | | const cast_ty = try o.builder.vectorType(.normal, ty.vectorLen(mod), int_ty); |
| 7933 | | const bitcasted_operand = self.builder.buildBitCast(params[0], cast_ty.toLlvm(&o.builder), ""); |
| 7934 | | break :blk self.builder.buildXor(bitcasted_operand, splat_sign_mask, ""); |
| 7935 | | } else blk: { |
| 7936 | | const bitcasted_operand = self.builder.buildBitCast(params[0], int_ty.toLlvm(&o.builder), ""); |
| 7937 | | break :blk self.builder.buildXor(bitcasted_operand, sign_mask.toLlvm(&o.builder), ""); |
| 7938 | | }; |
| 7939 | | return self.builder.buildBitCast(result, llvm_ty.toLlvm(&o.builder), ""); |
| 7892 | const cast_ty = try llvm_ty.changeScalar(int_ty, &o.builder); |
| 7893 | const sign_mask = try o.builder.splatValue( |
| 7894 | cast_ty, |
| 7895 | try o.builder.intConst(int_ty, @as(u128, 1) << @intCast(float_bits - 1)), |
| 7896 | ); |
| 7897 | const bitcasted_operand = try self.wip.cast(.bitcast, params[0], cast_ty, ""); |
| 7898 | const result = try self.wip.bin(.xor, bitcasted_operand, sign_mask, ""); |
| 7899 | return self.wip.cast(.bitcast, result, llvm_ty, ""); |
| 7940 | 7900 | }, |
| 7941 | 7901 | .add, .sub, .div, .mul => .{ .libc = try o.builder.fmt("__{s}{s}f3", .{ |
| 7942 | 7902 | @tagName(op), compilerRtFloatAbbrev(float_bits), |
| ... | ... | @@ -7965,26 +7925,42 @@ pub const FuncGen = struct { |
| 7965 | 7925 | }; |
| 7966 | 7926 | }; |
| 7967 | 7927 | |
| 7968 | | const llvm_fn: *llvm.Value = switch (strat) { |
| 7928 | const llvm_fn = switch (strat) { |
| 7969 | 7929 | .intrinsic => |fn_name| try self.getIntrinsic(fn_name, &.{llvm_ty}), |
| 7970 | 7930 | .libc => |fn_name| b: { |
| 7931 | const scalar_llvm_ty = llvm_ty.scalarType(&o.builder); |
| 7971 | 7932 | const libc_fn = try self.getLibcFunction( |
| 7972 | 7933 | fn_name, |
| 7973 | 7934 | ([1]Builder.Type{scalar_llvm_ty} ** 3)[0..params.len], |
| 7974 | 7935 | scalar_llvm_ty, |
| 7975 | 7936 | ); |
| 7976 | 7937 | if (ty.zigTypeTag(mod) == .Vector) { |
| 7977 | | const result = llvm_ty.toLlvm(&o.builder).getUndef(); |
| 7938 | const result = try o.builder.poisonValue(llvm_ty); |
| 7978 | 7939 | return self.buildElementwiseCall(libc_fn, &params, result, ty.vectorLen(mod)); |
| 7979 | 7940 | } |
| 7980 | 7941 | |
| 7981 | | break :b libc_fn; |
| 7942 | break :b libc_fn.toLlvm(&o.builder); |
| 7982 | 7943 | }, |
| 7983 | 7944 | }; |
| 7984 | | return self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &params, params_len, .C, .Auto, ""); |
| 7945 | const llvm_fn_ty = try o.builder.fnType( |
| 7946 | llvm_ty, |
| 7947 | ([1]Builder.Type{llvm_ty} ** 3)[0..params.len], |
| 7948 | .normal, |
| 7949 | ); |
| 7950 | var llvm_params: [params_len]*llvm.Value = undefined; |
| 7951 | for (&llvm_params, params) |*llvm_param, param| llvm_param.* = param.toLlvm(&self.wip); |
| 7952 | return (try self.wip.unimplemented(llvm_ty, "")).finish(self.builder.buildCall( |
| 7953 | llvm_fn_ty.toLlvm(&o.builder), |
| 7954 | llvm_fn, |
| 7955 | &llvm_params, |
| 7956 | params_len, |
| 7957 | .C, |
| 7958 | .Auto, |
| 7959 | "", |
| 7960 | ), &self.wip); |
| 7985 | 7961 | } |
| 7986 | 7962 | |
| 7987 | | fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7963 | fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 7988 | 7964 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 7989 | 7965 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; |
| 7990 | 7966 | |
| ... | ... | @@ -7996,7 +7972,7 @@ pub const FuncGen = struct { |
| 7996 | 7972 | return self.buildFloatOp(.fma, ty, 3, .{ mulend1, mulend2, addend }); |
| 7997 | 7973 | } |
| 7998 | 7974 | |
| 7999 | | fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7975 | fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8000 | 7976 | const o = self.dg.object; |
| 8001 | 7977 | const mod = o.module; |
| 8002 | 7978 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| ... | ... | @@ -8006,72 +7982,67 @@ pub const FuncGen = struct { |
| 8006 | 7982 | const rhs = try self.resolveInst(extra.rhs); |
| 8007 | 7983 | |
| 8008 | 7984 | const lhs_ty = self.typeOf(extra.lhs); |
| 8009 | | const rhs_ty = self.typeOf(extra.rhs); |
| 8010 | 7985 | const lhs_scalar_ty = lhs_ty.scalarType(mod); |
| 8011 | | const rhs_scalar_ty = rhs_ty.scalarType(mod); |
| 8012 | 7986 | |
| 8013 | 7987 | const dest_ty = self.typeOfIndex(inst); |
| 8014 | | const llvm_dest_ty = (try o.lowerType(dest_ty)).toLlvm(&o.builder); |
| 7988 | const llvm_dest_ty = try o.lowerType(dest_ty); |
| 8015 | 7989 | |
| 8016 | | const casted_rhs = if (rhs_scalar_ty.bitSize(mod) < lhs_scalar_ty.bitSize(mod)) |
| 8017 | | self.builder.buildZExt(rhs, (try o.lowerType(lhs_ty)).toLlvm(&o.builder), "") |
| 8018 | | else |
| 8019 | | rhs; |
| 7990 | const casted_rhs = try self.wip.conv(.unsigned, rhs, try o.lowerType(lhs_ty), ""); |
| 8020 | 7991 | |
| 8021 | | const result = self.builder.buildShl(lhs, casted_rhs, ""); |
| 8022 | | const reconstructed = if (lhs_scalar_ty.isSignedInt(mod)) |
| 8023 | | self.builder.buildAShr(result, casted_rhs, "") |
| 7992 | const result = try self.wip.bin(.shl, lhs, casted_rhs, ""); |
| 7993 | const reconstructed = try self.wip.bin(if (lhs_scalar_ty.isSignedInt(mod)) |
| 7994 | .ashr |
| 8024 | 7995 | else |
| 8025 | | self.builder.buildLShr(result, casted_rhs, ""); |
| 7996 | .lshr, result, casted_rhs, ""); |
| 8026 | 7997 | |
| 8027 | | const overflow_bit = self.builder.buildICmp(.NE, lhs, reconstructed, ""); |
| 7998 | const overflow_bit = try self.wip.icmp(.ne, lhs, reconstructed, ""); |
| 8028 | 7999 | |
| 8029 | 8000 | const result_index = llvmField(dest_ty, 0, mod).?.index; |
| 8030 | 8001 | const overflow_index = llvmField(dest_ty, 1, mod).?.index; |
| 8031 | 8002 | |
| 8032 | 8003 | if (isByRef(dest_ty, mod)) { |
| 8033 | | const result_alignment = dest_ty.abiAlignment(mod); |
| 8004 | const result_alignment = Builder.Alignment.fromByteUnits(dest_ty.abiAlignment(mod)); |
| 8034 | 8005 | const alloca_inst = try self.buildAlloca(llvm_dest_ty, result_alignment); |
| 8035 | 8006 | { |
| 8036 | | const field_ptr = self.builder.buildStructGEP(llvm_dest_ty, alloca_inst, result_index, ""); |
| 8037 | | const store_inst = self.builder.buildStore(result, field_ptr); |
| 8038 | | store_inst.setAlignment(result_alignment); |
| 8007 | const field_ptr = try self.wip.gepStruct(llvm_dest_ty, alloca_inst, result_index, ""); |
| 8008 | _ = try self.wip.store(.normal, result, field_ptr, result_alignment); |
| 8039 | 8009 | } |
| 8040 | 8010 | { |
| 8041 | | const field_ptr = self.builder.buildStructGEP(llvm_dest_ty, alloca_inst, overflow_index, ""); |
| 8042 | | const store_inst = self.builder.buildStore(overflow_bit, field_ptr); |
| 8043 | | store_inst.setAlignment(1); |
| 8011 | const field_alignment = comptime Builder.Alignment.fromByteUnits(1); |
| 8012 | const field_ptr = try self.wip.gepStruct(llvm_dest_ty, alloca_inst, overflow_index, ""); |
| 8013 | _ = try self.wip.store(.normal, overflow_bit, field_ptr, field_alignment); |
| 8044 | 8014 | } |
| 8045 | | |
| 8046 | 8015 | return alloca_inst; |
| 8047 | 8016 | } |
| 8048 | 8017 | |
| 8049 | | const partial = self.builder.buildInsertValue(llvm_dest_ty.getUndef(), result, result_index, ""); |
| 8050 | | return self.builder.buildInsertValue(partial, overflow_bit, overflow_index, ""); |
| 8018 | var fields: [2]Builder.Value = undefined; |
| 8019 | fields[result_index] = result; |
| 8020 | fields[overflow_index] = overflow_bit; |
| 8021 | return self.wip.buildAggregate(llvm_dest_ty, &fields, ""); |
| 8051 | 8022 | } |
| 8052 | 8023 | |
| 8053 | | fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8024 | fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8054 | 8025 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 8055 | 8026 | const lhs = try self.resolveInst(bin_op.lhs); |
| 8056 | 8027 | const rhs = try self.resolveInst(bin_op.rhs); |
| 8057 | | return self.builder.buildAnd(lhs, rhs, ""); |
| 8028 | return self.wip.bin(.@"and", lhs, rhs, ""); |
| 8058 | 8029 | } |
| 8059 | 8030 | |
| 8060 | | fn airOr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8031 | fn airOr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8061 | 8032 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 8062 | 8033 | const lhs = try self.resolveInst(bin_op.lhs); |
| 8063 | 8034 | const rhs = try self.resolveInst(bin_op.rhs); |
| 8064 | | return self.builder.buildOr(lhs, rhs, ""); |
| 8035 | return self.wip.bin(.@"or", lhs, rhs, ""); |
| 8065 | 8036 | } |
| 8066 | 8037 | |
| 8067 | | fn airXor(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8038 | fn airXor(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8068 | 8039 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 8069 | 8040 | const lhs = try self.resolveInst(bin_op.lhs); |
| 8070 | 8041 | const rhs = try self.resolveInst(bin_op.rhs); |
| 8071 | | return self.builder.buildXor(lhs, rhs, ""); |
| 8042 | return self.wip.bin(.xor, lhs, rhs, ""); |
| 8072 | 8043 | } |
| 8073 | 8044 | |
| 8074 | | fn airShlExact(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8045 | fn airShlExact(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8075 | 8046 | const o = self.dg.object; |
| 8076 | 8047 | const mod = o.module; |
| 8077 | 8048 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| ... | ... | @@ -8080,39 +8051,29 @@ pub const FuncGen = struct { |
| 8080 | 8051 | const rhs = try self.resolveInst(bin_op.rhs); |
| 8081 | 8052 | |
| 8082 | 8053 | const lhs_ty = self.typeOf(bin_op.lhs); |
| 8083 | | const rhs_ty = self.typeOf(bin_op.rhs); |
| 8084 | 8054 | const lhs_scalar_ty = lhs_ty.scalarType(mod); |
| 8085 | | const rhs_scalar_ty = rhs_ty.scalarType(mod); |
| 8086 | 8055 | |
| 8087 | | const casted_rhs = if (rhs_scalar_ty.bitSize(mod) < lhs_scalar_ty.bitSize(mod)) |
| 8088 | | self.builder.buildZExt(rhs, (try o.lowerType(lhs_ty)).toLlvm(&o.builder), "") |
| 8056 | const casted_rhs = try self.wip.conv(.unsigned, rhs, try o.lowerType(lhs_ty), ""); |
| 8057 | return self.wip.bin(if (lhs_scalar_ty.isSignedInt(mod)) |
| 8058 | .@"shl nsw" |
| 8089 | 8059 | else |
| 8090 | | rhs; |
| 8091 | | if (lhs_scalar_ty.isSignedInt(mod)) return self.builder.buildNSWShl(lhs, casted_rhs, ""); |
| 8092 | | return self.builder.buildNUWShl(lhs, casted_rhs, ""); |
| 8060 | .@"shl nuw", lhs, casted_rhs, ""); |
| 8093 | 8061 | } |
| 8094 | 8062 | |
| 8095 | | fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8063 | fn airShl(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8096 | 8064 | const o = self.dg.object; |
| 8097 | | const mod = o.module; |
| 8098 | 8065 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 8099 | 8066 | |
| 8100 | 8067 | const lhs = try self.resolveInst(bin_op.lhs); |
| 8101 | 8068 | const rhs = try self.resolveInst(bin_op.rhs); |
| 8102 | 8069 | |
| 8103 | 8070 | const lhs_type = self.typeOf(bin_op.lhs); |
| 8104 | | const rhs_type = self.typeOf(bin_op.rhs); |
| 8105 | | const lhs_scalar_ty = lhs_type.scalarType(mod); |
| 8106 | | const rhs_scalar_ty = rhs_type.scalarType(mod); |
| 8107 | 8071 | |
| 8108 | | const casted_rhs = if (rhs_scalar_ty.bitSize(mod) < lhs_scalar_ty.bitSize(mod)) |
| 8109 | | self.builder.buildZExt(rhs, (try o.lowerType(lhs_type)).toLlvm(&o.builder), "") |
| 8110 | | else |
| 8111 | | rhs; |
| 8112 | | return self.builder.buildShl(lhs, casted_rhs, ""); |
| 8072 | const casted_rhs = try self.wip.conv(.unsigned, rhs, try o.lowerType(lhs_type), ""); |
| 8073 | return self.wip.bin(.shl, lhs, casted_rhs, ""); |
| 8113 | 8074 | } |
| 8114 | 8075 | |
| 8115 | | fn airShlSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8076 | fn airShlSat(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8116 | 8077 | const o = self.dg.object; |
| 8117 | 8078 | const mod = o.module; |
| 8118 | 8079 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| ... | ... | @@ -8121,42 +8082,36 @@ pub const FuncGen = struct { |
| 8121 | 8082 | const rhs = try self.resolveInst(bin_op.rhs); |
| 8122 | 8083 | |
| 8123 | 8084 | const lhs_ty = self.typeOf(bin_op.lhs); |
| 8124 | | const rhs_ty = self.typeOf(bin_op.rhs); |
| 8125 | 8085 | const lhs_scalar_ty = lhs_ty.scalarType(mod); |
| 8126 | | const rhs_scalar_ty = rhs_ty.scalarType(mod); |
| 8127 | 8086 | const lhs_bits = lhs_scalar_ty.bitSize(mod); |
| 8128 | 8087 | |
| 8129 | | const casted_rhs = if (rhs_scalar_ty.bitSize(mod) < lhs_bits) |
| 8130 | | self.builder.buildZExt(rhs, lhs.typeOf(), "") |
| 8131 | | else |
| 8132 | | rhs; |
| 8088 | const casted_rhs = try self.wip.conv(.unsigned, rhs, try o.lowerType(lhs_ty), ""); |
| 8133 | 8089 | |
| 8134 | | const result = if (lhs_scalar_ty.isSignedInt(mod)) |
| 8135 | | self.builder.buildSShlSat(lhs, casted_rhs, "") |
| 8090 | const result = try self.wip.bin(if (lhs_scalar_ty.isSignedInt(mod)) |
| 8091 | .@"llvm.sshl.sat." |
| 8136 | 8092 | else |
| 8137 | | self.builder.buildUShlSat(lhs, casted_rhs, ""); |
| 8093 | .@"llvm.ushl.sat.", lhs, casted_rhs, ""); |
| 8138 | 8094 | |
| 8139 | 8095 | // LLVM langref says "If b is (statically or dynamically) equal to or |
| 8140 | 8096 | // larger than the integer bit width of the arguments, the result is a |
| 8141 | 8097 | // poison value." |
| 8142 | 8098 | // However Zig semantics says that saturating shift left can never produce |
| 8143 | 8099 | // undefined; instead it saturates. |
| 8144 | | const lhs_scalar_llvm_ty = try o.lowerType(lhs_scalar_ty); |
| 8145 | | const bits = (try o.builder.intConst(lhs_scalar_llvm_ty, lhs_bits)).toLlvm(&o.builder); |
| 8146 | | const lhs_max = (try o.builder.intConst(lhs_scalar_llvm_ty, -1)).toLlvm(&o.builder); |
| 8147 | | if (rhs_ty.zigTypeTag(mod) == .Vector) { |
| 8148 | | const vec_len = rhs_ty.vectorLen(mod); |
| 8149 | | const bits_vec = self.builder.buildVectorSplat(vec_len, bits, ""); |
| 8150 | | const lhs_max_vec = self.builder.buildVectorSplat(vec_len, lhs_max, ""); |
| 8151 | | const in_range = self.builder.buildICmp(.ULT, rhs, bits_vec, ""); |
| 8152 | | return self.builder.buildSelect(in_range, result, lhs_max_vec, ""); |
| 8153 | | } else { |
| 8154 | | const in_range = self.builder.buildICmp(.ULT, rhs, bits, ""); |
| 8155 | | return self.builder.buildSelect(in_range, result, lhs_max, ""); |
| 8156 | | } |
| 8100 | const lhs_llvm_ty = try o.lowerType(lhs_ty); |
| 8101 | const lhs_scalar_llvm_ty = lhs_llvm_ty.scalarType(&o.builder); |
| 8102 | const bits = try o.builder.splatValue( |
| 8103 | lhs_llvm_ty, |
| 8104 | try o.builder.intConst(lhs_scalar_llvm_ty, lhs_bits), |
| 8105 | ); |
| 8106 | const lhs_max = try o.builder.splatValue( |
| 8107 | lhs_llvm_ty, |
| 8108 | try o.builder.intConst(lhs_scalar_llvm_ty, -1), |
| 8109 | ); |
| 8110 | const in_range = try self.wip.icmp(.ult, rhs, bits, ""); |
| 8111 | return self.wip.select(in_range, result, lhs_max, ""); |
| 8157 | 8112 | } |
| 8158 | 8113 | |
| 8159 | | fn airShr(self: *FuncGen, inst: Air.Inst.Index, is_exact: bool) !?*llvm.Value { |
| 8114 | fn airShr(self: *FuncGen, inst: Air.Inst.Index, is_exact: bool) !Builder.Value { |
| 8160 | 8115 | const o = self.dg.object; |
| 8161 | 8116 | const mod = o.module; |
| 8162 | 8117 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| ... | ... | @@ -8165,63 +8120,41 @@ pub const FuncGen = struct { |
| 8165 | 8120 | const rhs = try self.resolveInst(bin_op.rhs); |
| 8166 | 8121 | |
| 8167 | 8122 | const lhs_ty = self.typeOf(bin_op.lhs); |
| 8168 | | const rhs_ty = self.typeOf(bin_op.rhs); |
| 8169 | 8123 | const lhs_scalar_ty = lhs_ty.scalarType(mod); |
| 8170 | | const rhs_scalar_ty = rhs_ty.scalarType(mod); |
| 8171 | 8124 | |
| 8172 | | const casted_rhs = if (rhs_scalar_ty.bitSize(mod) < lhs_scalar_ty.bitSize(mod)) |
| 8173 | | self.builder.buildZExt(rhs, (try o.lowerType(lhs_ty)).toLlvm(&o.builder), "") |
| 8174 | | else |
| 8175 | | rhs; |
| 8125 | const casted_rhs = try self.wip.conv(.unsigned, rhs, try o.lowerType(lhs_ty), ""); |
| 8176 | 8126 | const is_signed_int = lhs_scalar_ty.isSignedInt(mod); |
| 8177 | 8127 | |
| 8178 | | if (is_exact) { |
| 8179 | | if (is_signed_int) { |
| 8180 | | return self.builder.buildAShrExact(lhs, casted_rhs, ""); |
| 8181 | | } else { |
| 8182 | | return self.builder.buildLShrExact(lhs, casted_rhs, ""); |
| 8183 | | } |
| 8184 | | } else { |
| 8185 | | if (is_signed_int) { |
| 8186 | | return self.builder.buildAShr(lhs, casted_rhs, ""); |
| 8187 | | } else { |
| 8188 | | return self.builder.buildLShr(lhs, casted_rhs, ""); |
| 8189 | | } |
| 8190 | | } |
| 8128 | return self.wip.bin(if (is_exact) |
| 8129 | if (is_signed_int) .@"ashr exact" else .@"lshr exact" |
| 8130 | else if (is_signed_int) .ashr else .lshr, lhs, casted_rhs, ""); |
| 8191 | 8131 | } |
| 8192 | 8132 | |
| 8193 | | fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8133 | fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8194 | 8134 | const o = self.dg.object; |
| 8195 | 8135 | const mod = o.module; |
| 8196 | 8136 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 8197 | 8137 | const dest_ty = self.typeOfIndex(inst); |
| 8198 | | const dest_info = dest_ty.intInfo(mod); |
| 8199 | | const dest_llvm_ty = (try o.lowerType(dest_ty)).toLlvm(&o.builder); |
| 8138 | const dest_llvm_ty = try o.lowerType(dest_ty); |
| 8200 | 8139 | const operand = try self.resolveInst(ty_op.operand); |
| 8201 | 8140 | const operand_ty = self.typeOf(ty_op.operand); |
| 8202 | 8141 | const operand_info = operand_ty.intInfo(mod); |
| 8203 | 8142 | |
| 8204 | | if (operand_info.bits < dest_info.bits) { |
| 8205 | | switch (operand_info.signedness) { |
| 8206 | | .signed => return self.builder.buildSExt(operand, dest_llvm_ty, ""), |
| 8207 | | .unsigned => return self.builder.buildZExt(operand, dest_llvm_ty, ""), |
| 8208 | | } |
| 8209 | | } else if (operand_info.bits > dest_info.bits) { |
| 8210 | | return self.builder.buildTrunc(operand, dest_llvm_ty, ""); |
| 8211 | | } else { |
| 8212 | | return operand; |
| 8213 | | } |
| 8143 | return self.wip.conv(switch (operand_info.signedness) { |
| 8144 | .signed => .signed, |
| 8145 | .unsigned => .unsigned, |
| 8146 | }, operand, dest_llvm_ty, ""); |
| 8214 | 8147 | } |
| 8215 | 8148 | |
| 8216 | | fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8149 | fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8217 | 8150 | const o = self.dg.object; |
| 8218 | 8151 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 8219 | 8152 | const operand = try self.resolveInst(ty_op.operand); |
| 8220 | | const dest_llvm_ty = (try o.lowerType(self.typeOfIndex(inst))).toLlvm(&o.builder); |
| 8221 | | return self.builder.buildTrunc(operand, dest_llvm_ty, ""); |
| 8153 | const dest_llvm_ty = try o.lowerType(self.typeOfIndex(inst)); |
| 8154 | return self.wip.cast(.trunc, operand, dest_llvm_ty, ""); |
| 8222 | 8155 | } |
| 8223 | 8156 | |
| 8224 | | fn airFptrunc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8157 | fn airFptrunc(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8225 | 8158 | const o = self.dg.object; |
| 8226 | 8159 | const mod = o.module; |
| 8227 | 8160 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| ... | ... | @@ -8233,8 +8166,7 @@ pub const FuncGen = struct { |
| 8233 | 8166 | const src_bits = operand_ty.floatBits(target); |
| 8234 | 8167 | |
| 8235 | 8168 | if (intrinsicsAllowed(dest_ty, target) and intrinsicsAllowed(operand_ty, target)) { |
| 8236 | | const dest_llvm_ty = (try o.lowerType(dest_ty)).toLlvm(&o.builder); |
| 8237 | | return self.builder.buildFPTrunc(operand, dest_llvm_ty, ""); |
| 8169 | return self.wip.cast(.fptrunc, operand, try o.lowerType(dest_ty), ""); |
| 8238 | 8170 | } else { |
| 8239 | 8171 | const operand_llvm_ty = try o.lowerType(operand_ty); |
| 8240 | 8172 | const dest_llvm_ty = try o.lowerType(dest_ty); |
| ... | ... | @@ -8243,14 +8175,21 @@ pub const FuncGen = struct { |
| 8243 | 8175 | compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits), |
| 8244 | 8176 | }); |
| 8245 | 8177 | |
| 8246 | | const params = [1]*llvm.Value{operand}; |
| 8247 | 8178 | const llvm_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty); |
| 8248 | | |
| 8249 | | return self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &params, params.len, .C, .Auto, ""); |
| 8179 | const params = [1]*llvm.Value{operand.toLlvm(&self.wip)}; |
| 8180 | return (try self.wip.unimplemented(dest_llvm_ty, "")).finish(self.builder.buildCall( |
| 8181 | llvm_fn.typeOf(&o.builder).toLlvm(&o.builder), |
| 8182 | llvm_fn.toLlvm(&o.builder), |
| 8183 | &params, |
| 8184 | params.len, |
| 8185 | .C, |
| 8186 | .Auto, |
| 8187 | "", |
| 8188 | ), &self.wip); |
| 8250 | 8189 | } |
| 8251 | 8190 | } |
| 8252 | 8191 | |
| 8253 | | fn airFpext(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8192 | fn airFpext(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8254 | 8193 | const o = self.dg.object; |
| 8255 | 8194 | const mod = o.module; |
| 8256 | 8195 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| ... | ... | @@ -8262,8 +8201,7 @@ pub const FuncGen = struct { |
| 8262 | 8201 | const src_bits = operand_ty.floatBits(target); |
| 8263 | 8202 | |
| 8264 | 8203 | if (intrinsicsAllowed(dest_ty, target) and intrinsicsAllowed(operand_ty, target)) { |
| 8265 | | const dest_llvm_ty = (try o.lowerType(dest_ty)).toLlvm(&o.builder); |
| 8266 | | return self.builder.buildFPExt(operand, dest_llvm_ty, ""); |
| 8204 | return self.wip.cast(.fpext, operand, try o.lowerType(dest_ty), ""); |
| 8267 | 8205 | } else { |
| 8268 | 8206 | const operand_llvm_ty = try o.lowerType(operand_ty); |
| 8269 | 8207 | const dest_llvm_ty = try o.lowerType(dest_ty); |
| ... | ... | @@ -8272,24 +8210,31 @@ pub const FuncGen = struct { |
| 8272 | 8210 | compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits), |
| 8273 | 8211 | }); |
| 8274 | 8212 | |
| 8275 | | const params = [1]*llvm.Value{operand}; |
| 8276 | 8213 | const llvm_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty); |
| 8277 | | |
| 8278 | | return self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &params, params.len, .C, .Auto, ""); |
| 8214 | const params = [1]*llvm.Value{operand.toLlvm(&self.wip)}; |
| 8215 | return (try self.wip.unimplemented(dest_llvm_ty, "")).finish(self.builder.buildCall( |
| 8216 | llvm_fn.typeOf(&o.builder).toLlvm(&o.builder), |
| 8217 | llvm_fn.toLlvm(&o.builder), |
| 8218 | &params, |
| 8219 | params.len, |
| 8220 | .C, |
| 8221 | .Auto, |
| 8222 | "", |
| 8223 | ), &self.wip); |
| 8279 | 8224 | } |
| 8280 | 8225 | } |
| 8281 | 8226 | |
| 8282 | | fn airIntFromPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8227 | fn airIntFromPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8283 | 8228 | const o = self.dg.object; |
| 8284 | 8229 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 8285 | 8230 | const operand = try self.resolveInst(un_op); |
| 8286 | 8231 | const ptr_ty = self.typeOf(un_op); |
| 8287 | | const operand_ptr = self.sliceOrArrayPtr(operand, ptr_ty); |
| 8288 | | const dest_llvm_ty = (try o.lowerType(self.typeOfIndex(inst))).toLlvm(&o.builder); |
| 8289 | | return self.builder.buildPtrToInt(operand_ptr, dest_llvm_ty, ""); |
| 8232 | const operand_ptr = try self.sliceOrArrayPtr(operand, ptr_ty); |
| 8233 | const dest_llvm_ty = try o.lowerType(self.typeOfIndex(inst)); |
| 8234 | return self.wip.cast(.ptrtoint, operand_ptr, dest_llvm_ty, ""); |
| 8290 | 8235 | } |
| 8291 | 8236 | |
| 8292 | | fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !*llvm.Value { |
| 8237 | fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8293 | 8238 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 8294 | 8239 | const operand_ty = self.typeOf(ty_op.operand); |
| 8295 | 8240 | const inst_ty = self.typeOfIndex(inst); |
| ... | ... | @@ -8297,26 +8242,26 @@ pub const FuncGen = struct { |
| 8297 | 8242 | return self.bitCast(operand, operand_ty, inst_ty); |
| 8298 | 8243 | } |
| 8299 | 8244 | |
| 8300 | | fn bitCast(self: *FuncGen, operand: *llvm.Value, operand_ty: Type, inst_ty: Type) !*llvm.Value { |
| 8245 | fn bitCast(self: *FuncGen, operand: Builder.Value, operand_ty: Type, inst_ty: Type) !Builder.Value { |
| 8301 | 8246 | const o = self.dg.object; |
| 8302 | 8247 | const mod = o.module; |
| 8303 | 8248 | const operand_is_ref = isByRef(operand_ty, mod); |
| 8304 | 8249 | const result_is_ref = isByRef(inst_ty, mod); |
| 8305 | | const llvm_dest_ty = (try o.lowerType(inst_ty)).toLlvm(&o.builder); |
| 8250 | const llvm_dest_ty = try o.lowerType(inst_ty); |
| 8306 | 8251 | |
| 8307 | 8252 | if (operand_is_ref and result_is_ref) { |
| 8308 | 8253 | // They are both pointers, so just return the same opaque pointer :) |
| 8309 | 8254 | return operand; |
| 8310 | 8255 | } |
| 8311 | 8256 | |
| 8312 | | if (llvm_dest_ty.getTypeKind() == .Integer and |
| 8313 | | operand.typeOf().getTypeKind() == .Integer) |
| 8257 | if (llvm_dest_ty.isInteger(&o.builder) and |
| 8258 | operand.typeOfWip(&self.wip).isInteger(&o.builder)) |
| 8314 | 8259 | { |
| 8315 | | return self.builder.buildZExtOrBitCast(operand, llvm_dest_ty, ""); |
| 8260 | return self.wip.conv(.unsigned, operand, llvm_dest_ty, ""); |
| 8316 | 8261 | } |
| 8317 | 8262 | |
| 8318 | 8263 | if (operand_ty.zigTypeTag(mod) == .Int and inst_ty.isPtrAtRuntime(mod)) { |
| 8319 | | return self.builder.buildIntToPtr(operand, llvm_dest_ty, ""); |
| 8264 | return self.wip.cast(.inttoptr, operand, llvm_dest_ty, ""); |
| 8320 | 8265 | } |
| 8321 | 8266 | |
| 8322 | 8267 | if (operand_ty.zigTypeTag(mod) == .Vector and inst_ty.zigTypeTag(mod) == .Array) { |
| ... | ... | @@ -8324,108 +8269,97 @@ pub const FuncGen = struct { |
| 8324 | 8269 | if (!result_is_ref) { |
| 8325 | 8270 | return self.dg.todo("implement bitcast vector to non-ref array", .{}); |
| 8326 | 8271 | } |
| 8327 | | const array_ptr = try self.buildAlloca(llvm_dest_ty, null); |
| 8272 | const array_ptr = try self.buildAlloca(llvm_dest_ty, .default); |
| 8328 | 8273 | const bitcast_ok = elem_ty.bitSize(mod) == elem_ty.abiSize(mod) * 8; |
| 8329 | 8274 | if (bitcast_ok) { |
| 8330 | | const llvm_store = self.builder.buildStore(operand, array_ptr); |
| 8331 | | llvm_store.setAlignment(inst_ty.abiAlignment(mod)); |
| 8275 | const alignment = Builder.Alignment.fromByteUnits(inst_ty.abiAlignment(mod)); |
| 8276 | _ = try self.wip.store(.normal, operand, array_ptr, alignment); |
| 8332 | 8277 | } else { |
| 8333 | 8278 | // If the ABI size of the element type is not evenly divisible by size in bits; |
| 8334 | 8279 | // a simple bitcast will not work, and we fall back to extractelement. |
| 8335 | 8280 | const llvm_usize = try o.lowerType(Type.usize); |
| 8336 | | const zero = try o.builder.intConst(llvm_usize, 0); |
| 8281 | const usize_zero = try o.builder.intValue(llvm_usize, 0); |
| 8337 | 8282 | const vector_len = operand_ty.arrayLen(mod); |
| 8338 | 8283 | var i: u64 = 0; |
| 8339 | 8284 | while (i < vector_len) : (i += 1) { |
| 8340 | | const index_usize = try o.builder.intConst(llvm_usize, i); |
| 8341 | | const index_u32 = try o.builder.intConst(.i32, i); |
| 8342 | | const indexes: [2]*llvm.Value = .{ |
| 8343 | | zero.toLlvm(&o.builder), |
| 8344 | | index_usize.toLlvm(&o.builder), |
| 8345 | | }; |
| 8346 | | const elem_ptr = self.builder.buildInBoundsGEP(llvm_dest_ty, array_ptr, &indexes, indexes.len, ""); |
| 8347 | | const elem = self.builder.buildExtractElement(operand, index_u32.toLlvm(&o.builder), ""); |
| 8348 | | _ = self.builder.buildStore(elem, elem_ptr); |
| 8285 | const elem_ptr = try self.wip.gep(.inbounds, llvm_dest_ty, array_ptr, &.{ |
| 8286 | usize_zero, try o.builder.intValue(llvm_usize, i), |
| 8287 | }, ""); |
| 8288 | const elem = |
| 8289 | try self.wip.extractElement(operand, try o.builder.intValue(.i32, i), ""); |
| 8290 | _ = try self.wip.store(.normal, elem, elem_ptr, .default); |
| 8349 | 8291 | } |
| 8350 | 8292 | } |
| 8351 | 8293 | return array_ptr; |
| 8352 | 8294 | } else if (operand_ty.zigTypeTag(mod) == .Array and inst_ty.zigTypeTag(mod) == .Vector) { |
| 8353 | 8295 | const elem_ty = operand_ty.childType(mod); |
| 8354 | | const llvm_vector_ty = (try o.lowerType(inst_ty)).toLlvm(&o.builder); |
| 8355 | | if (!operand_is_ref) { |
| 8356 | | return self.dg.todo("implement bitcast non-ref array to vector", .{}); |
| 8357 | | } |
| 8296 | const llvm_vector_ty = try o.lowerType(inst_ty); |
| 8297 | if (!operand_is_ref) return self.dg.todo("implement bitcast non-ref array to vector", .{}); |
| 8358 | 8298 | |
| 8359 | 8299 | const bitcast_ok = elem_ty.bitSize(mod) == elem_ty.abiSize(mod) * 8; |
| 8360 | 8300 | if (bitcast_ok) { |
| 8361 | | const vector = self.builder.buildLoad(llvm_vector_ty, operand, ""); |
| 8362 | 8301 | // The array is aligned to the element's alignment, while the vector might have a completely |
| 8363 | 8302 | // different alignment. This means we need to enforce the alignment of this load. |
| 8364 | | vector.setAlignment(elem_ty.abiAlignment(mod)); |
| 8365 | | return vector; |
| 8303 | const alignment = Builder.Alignment.fromByteUnits(elem_ty.abiAlignment(mod)); |
| 8304 | return self.wip.load(.normal, llvm_vector_ty, operand, alignment, ""); |
| 8366 | 8305 | } else { |
| 8367 | 8306 | // If the ABI size of the element type is not evenly divisible by size in bits; |
| 8368 | 8307 | // a simple bitcast will not work, and we fall back to extractelement. |
| 8369 | | const array_llvm_ty = (try o.lowerType(operand_ty)).toLlvm(&o.builder); |
| 8370 | | const elem_llvm_ty = (try o.lowerType(elem_ty)).toLlvm(&o.builder); |
| 8308 | const array_llvm_ty = try o.lowerType(operand_ty); |
| 8309 | const elem_llvm_ty = try o.lowerType(elem_ty); |
| 8371 | 8310 | const llvm_usize = try o.lowerType(Type.usize); |
| 8372 | | const zero = try o.builder.intConst(llvm_usize, 0); |
| 8311 | const usize_zero = try o.builder.intValue(llvm_usize, 0); |
| 8373 | 8312 | const vector_len = operand_ty.arrayLen(mod); |
| 8374 | | var vector = llvm_vector_ty.getUndef(); |
| 8313 | var vector = try o.builder.poisonValue(llvm_vector_ty); |
| 8375 | 8314 | var i: u64 = 0; |
| 8376 | 8315 | while (i < vector_len) : (i += 1) { |
| 8377 | | const index_usize = try o.builder.intConst(llvm_usize, i); |
| 8378 | | const index_u32 = try o.builder.intConst(.i32, i); |
| 8379 | | const indexes: [2]*llvm.Value = .{ |
| 8380 | | zero.toLlvm(&o.builder), |
| 8381 | | index_usize.toLlvm(&o.builder), |
| 8382 | | }; |
| 8383 | | const elem_ptr = self.builder.buildInBoundsGEP(array_llvm_ty, operand, &indexes, indexes.len, ""); |
| 8384 | | const elem = self.builder.buildLoad(elem_llvm_ty, elem_ptr, ""); |
| 8385 | | vector = self.builder.buildInsertElement(vector, elem, index_u32.toLlvm(&o.builder), ""); |
| 8316 | const elem_ptr = try self.wip.gep(.inbounds, array_llvm_ty, operand, &.{ |
| 8317 | usize_zero, try o.builder.intValue(llvm_usize, i), |
| 8318 | }, ""); |
| 8319 | const elem = try self.wip.load(.normal, elem_llvm_ty, elem_ptr, .default, ""); |
| 8320 | vector = |
| 8321 | try self.wip.insertElement(vector, elem, try o.builder.intValue(.i32, i), ""); |
| 8386 | 8322 | } |
| 8387 | | |
| 8388 | 8323 | return vector; |
| 8389 | 8324 | } |
| 8390 | 8325 | } |
| 8391 | 8326 | |
| 8392 | 8327 | if (operand_is_ref) { |
| 8393 | | const load_inst = self.builder.buildLoad(llvm_dest_ty, operand, ""); |
| 8394 | | load_inst.setAlignment(operand_ty.abiAlignment(mod)); |
| 8395 | | return load_inst; |
| 8328 | const alignment = Builder.Alignment.fromByteUnits(operand_ty.abiAlignment(mod)); |
| 8329 | return self.wip.load(.normal, llvm_dest_ty, operand, alignment, ""); |
| 8396 | 8330 | } |
| 8397 | 8331 | |
| 8398 | 8332 | if (result_is_ref) { |
| 8399 | | const alignment = @max(operand_ty.abiAlignment(mod), inst_ty.abiAlignment(mod)); |
| 8333 | const alignment = Builder.Alignment.fromByteUnits( |
| 8334 | @max(operand_ty.abiAlignment(mod), inst_ty.abiAlignment(mod)), |
| 8335 | ); |
| 8400 | 8336 | const result_ptr = try self.buildAlloca(llvm_dest_ty, alignment); |
| 8401 | | const store_inst = self.builder.buildStore(operand, result_ptr); |
| 8402 | | store_inst.setAlignment(alignment); |
| 8337 | _ = try self.wip.store(.normal, operand, result_ptr, alignment); |
| 8403 | 8338 | return result_ptr; |
| 8404 | 8339 | } |
| 8405 | 8340 | |
| 8406 | | if (llvm_dest_ty.getTypeKind() == .Struct) { |
| 8341 | if (llvm_dest_ty.isStruct(&o.builder)) { |
| 8407 | 8342 | // Both our operand and our result are values, not pointers, |
| 8408 | 8343 | // but LLVM won't let us bitcast struct values. |
| 8409 | 8344 | // Therefore, we store operand to alloca, then load for result. |
| 8410 | | const alignment = @max(operand_ty.abiAlignment(mod), inst_ty.abiAlignment(mod)); |
| 8345 | const alignment = Builder.Alignment.fromByteUnits( |
| 8346 | @max(operand_ty.abiAlignment(mod), inst_ty.abiAlignment(mod)), |
| 8347 | ); |
| 8411 | 8348 | const result_ptr = try self.buildAlloca(llvm_dest_ty, alignment); |
| 8412 | | const store_inst = self.builder.buildStore(operand, result_ptr); |
| 8413 | | store_inst.setAlignment(alignment); |
| 8414 | | const load_inst = self.builder.buildLoad(llvm_dest_ty, result_ptr, ""); |
| 8415 | | load_inst.setAlignment(alignment); |
| 8416 | | return load_inst; |
| 8349 | _ = try self.wip.store(.normal, operand, result_ptr, alignment); |
| 8350 | return self.wip.load(.normal, llvm_dest_ty, result_ptr, alignment, ""); |
| 8417 | 8351 | } |
| 8418 | 8352 | |
| 8419 | | return self.builder.buildBitCast(operand, llvm_dest_ty, ""); |
| 8353 | return self.wip.cast(.bitcast, operand, llvm_dest_ty, ""); |
| 8420 | 8354 | } |
| 8421 | 8355 | |
| 8422 | | fn airIntFromBool(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8356 | fn airIntFromBool(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8423 | 8357 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 8424 | 8358 | const operand = try self.resolveInst(un_op); |
| 8425 | 8359 | return operand; |
| 8426 | 8360 | } |
| 8427 | 8361 | |
| 8428 | | fn airArg(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8362 | fn airArg(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8429 | 8363 | const o = self.dg.object; |
| 8430 | 8364 | const mod = o.module; |
| 8431 | 8365 | const arg_val = self.args[self.arg_index]; |
| ... | ... | @@ -8433,9 +8367,7 @@ pub const FuncGen = struct { |
| 8433 | 8367 | |
| 8434 | 8368 | const inst_ty = self.typeOfIndex(inst); |
| 8435 | 8369 | if (o.di_builder) |dib| { |
| 8436 | | if (needDbgVarWorkaround(o)) { |
| 8437 | | return arg_val; |
| 8438 | | } |
| 8370 | if (needDbgVarWorkaround(o)) return arg_val; |
| 8439 | 8371 | |
| 8440 | 8372 | const src_index = self.air.instructions.items(.data)[inst].arg.src_index; |
| 8441 | 8373 | const func_index = self.dg.decl.getOwnedFunctionIndex(); |
| ... | ... | @@ -8450,62 +8382,64 @@ pub const FuncGen = struct { |
| 8450 | 8382 | try o.lowerDebugType(inst_ty, .full), |
| 8451 | 8383 | true, // always preserve |
| 8452 | 8384 | 0, // flags |
| 8453 | | self.arg_index, // includes +1 because 0 is return type |
| 8385 | @intCast(self.arg_index), // includes +1 because 0 is return type |
| 8454 | 8386 | ); |
| 8455 | 8387 | |
| 8456 | 8388 | const debug_loc = llvm.getDebugLoc(lbrace_line, lbrace_col, self.di_scope.?, null); |
| 8457 | | const insert_block = self.builder.getInsertBlock(); |
| 8389 | const insert_block = self.wip.cursor.block.toLlvm(&self.wip); |
| 8458 | 8390 | if (isByRef(inst_ty, mod)) { |
| 8459 | | _ = dib.insertDeclareAtEnd(arg_val, di_local_var, debug_loc, insert_block); |
| 8391 | _ = dib.insertDeclareAtEnd(arg_val.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); |
| 8460 | 8392 | } else if (o.module.comp.bin_file.options.optimize_mode == .Debug) { |
| 8461 | | const alignment = inst_ty.abiAlignment(mod); |
| 8462 | | const alloca = try self.buildAlloca(arg_val.typeOf(), alignment); |
| 8463 | | const store_inst = self.builder.buildStore(arg_val, alloca); |
| 8464 | | store_inst.setAlignment(alignment); |
| 8465 | | _ = dib.insertDeclareAtEnd(alloca, di_local_var, debug_loc, insert_block); |
| 8393 | const alignment = Builder.Alignment.fromByteUnits(inst_ty.abiAlignment(mod)); |
| 8394 | const alloca = try self.buildAlloca(arg_val.typeOfWip(&self.wip), alignment); |
| 8395 | _ = try self.wip.store(.normal, arg_val, alloca, alignment); |
| 8396 | _ = dib.insertDeclareAtEnd(alloca.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); |
| 8466 | 8397 | } else { |
| 8467 | | _ = dib.insertDbgValueIntrinsicAtEnd(arg_val, di_local_var, debug_loc, insert_block); |
| 8398 | _ = dib.insertDbgValueIntrinsicAtEnd(arg_val.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); |
| 8468 | 8399 | } |
| 8469 | 8400 | } |
| 8470 | 8401 | |
| 8471 | 8402 | return arg_val; |
| 8472 | 8403 | } |
| 8473 | 8404 | |
| 8474 | | fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8405 | fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8475 | 8406 | const o = self.dg.object; |
| 8476 | 8407 | const mod = o.module; |
| 8477 | 8408 | const ptr_ty = self.typeOfIndex(inst); |
| 8478 | 8409 | const pointee_type = ptr_ty.childType(mod); |
| 8479 | 8410 | if (!pointee_type.isFnOrHasRuntimeBitsIgnoreComptime(mod)) |
| 8480 | | return (try o.lowerPtrToVoid(ptr_ty)).toLlvm(&o.builder); |
| 8411 | return (try o.lowerPtrToVoid(ptr_ty)).toValue(); |
| 8481 | 8412 | |
| 8482 | | const pointee_llvm_ty = (try o.lowerType(pointee_type)).toLlvm(&o.builder); |
| 8483 | | const alignment = ptr_ty.ptrAlignment(mod); |
| 8413 | const pointee_llvm_ty = try o.lowerType(pointee_type); |
| 8414 | const alignment = Builder.Alignment.fromByteUnits(ptr_ty.ptrAlignment(mod)); |
| 8484 | 8415 | return self.buildAlloca(pointee_llvm_ty, alignment); |
| 8485 | 8416 | } |
| 8486 | 8417 | |
| 8487 | | fn airRetPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8418 | fn airRetPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8488 | 8419 | const o = self.dg.object; |
| 8489 | 8420 | const mod = o.module; |
| 8490 | 8421 | const ptr_ty = self.typeOfIndex(inst); |
| 8491 | 8422 | const ret_ty = ptr_ty.childType(mod); |
| 8492 | 8423 | if (!ret_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) |
| 8493 | | return (try o.lowerPtrToVoid(ptr_ty)).toLlvm(&o.builder); |
| 8494 | | if (self.ret_ptr) |ret_ptr| return ret_ptr; |
| 8495 | | const ret_llvm_ty = (try o.lowerType(ret_ty)).toLlvm(&o.builder); |
| 8496 | | return self.buildAlloca(ret_llvm_ty, ptr_ty.ptrAlignment(mod)); |
| 8424 | return (try o.lowerPtrToVoid(ptr_ty)).toValue(); |
| 8425 | if (self.ret_ptr != .none) return self.ret_ptr; |
| 8426 | const ret_llvm_ty = try o.lowerType(ret_ty); |
| 8427 | const alignment = Builder.Alignment.fromByteUnits(ptr_ty.ptrAlignment(mod)); |
| 8428 | return self.buildAlloca(ret_llvm_ty, alignment); |
| 8497 | 8429 | } |
| 8498 | 8430 | |
| 8499 | 8431 | /// Use this instead of builder.buildAlloca, because this function makes sure to |
| 8500 | 8432 | /// put the alloca instruction at the top of the function! |
| 8501 | | fn buildAlloca(self: *FuncGen, llvm_ty: *llvm.Type, alignment: ?c_uint) Allocator.Error!*llvm.Value { |
| 8502 | | const o = self.dg.object; |
| 8503 | | const mod = o.module; |
| 8504 | | const target = mod.getTarget(); |
| 8505 | | return o.buildAllocaInner(&self.wip, self.builder, self.llvm_func, self.di_scope != null, llvm_ty, alignment, target); |
| 8433 | fn buildAlloca( |
| 8434 | self: *FuncGen, |
| 8435 | llvm_ty: Builder.Type, |
| 8436 | alignment: Builder.Alignment, |
| 8437 | ) Allocator.Error!Builder.Value { |
| 8438 | const target = self.dg.object.module.getTarget(); |
| 8439 | return buildAllocaInner(&self.wip, self.di_scope != null, llvm_ty, alignment, target); |
| 8506 | 8440 | } |
| 8507 | 8441 | |
| 8508 | | fn airStore(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !?*llvm.Value { |
| 8442 | fn airStore(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !Builder.Value { |
| 8509 | 8443 | const o = self.dg.object; |
| 8510 | 8444 | const mod = o.module; |
| 8511 | 8445 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| ... | ... | @@ -8519,23 +8453,29 @@ pub const FuncGen = struct { |
| 8519 | 8453 | // extra information to LLVM. However, safety makes the difference between using |
| 8520 | 8454 | // 0xaa or actual undefined for the fill byte. |
| 8521 | 8455 | const fill_byte = if (safety) |
| 8522 | | (try o.builder.intConst(.i8, 0xaa)).toLlvm(&o.builder) |
| 8456 | try o.builder.intConst(.i8, 0xaa) |
| 8523 | 8457 | else |
| 8524 | | Builder.Type.i8.toLlvm(&o.builder).getUndef(); |
| 8458 | try o.builder.undefConst(.i8); |
| 8525 | 8459 | const operand_size = operand_ty.abiSize(mod); |
| 8526 | 8460 | const usize_ty = try o.lowerType(Type.usize); |
| 8527 | | const len = (try o.builder.intConst(usize_ty, operand_size)).toLlvm(&o.builder); |
| 8528 | | const dest_ptr_align = ptr_ty.ptrAlignment(mod); |
| 8529 | | _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, ptr_ty.isVolatilePtr(mod)); |
| 8461 | const len = try o.builder.intValue(usize_ty, operand_size); |
| 8462 | const dest_ptr_align = Builder.Alignment.fromByteUnits(ptr_ty.ptrAlignment(mod)); |
| 8463 | _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildMemSet( |
| 8464 | dest_ptr.toLlvm(&self.wip), |
| 8465 | fill_byte.toLlvm(&o.builder), |
| 8466 | len.toLlvm(&self.wip), |
| 8467 | @intCast(dest_ptr_align.toByteUnits() orelse 0), |
| 8468 | ptr_ty.isVolatilePtr(mod), |
| 8469 | ), &self.wip); |
| 8530 | 8470 | if (safety and mod.comp.bin_file.options.valgrind) { |
| 8531 | 8471 | try self.valgrindMarkUndef(dest_ptr, len); |
| 8532 | 8472 | } |
| 8533 | | return null; |
| 8473 | return .none; |
| 8534 | 8474 | } |
| 8535 | 8475 | |
| 8536 | 8476 | const src_operand = try self.resolveInst(bin_op.rhs); |
| 8537 | | try self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic); |
| 8538 | | return null; |
| 8477 | try self.store(dest_ptr, ptr_ty, src_operand, .none); |
| 8478 | return .none; |
| 8539 | 8479 | } |
| 8540 | 8480 | |
| 8541 | 8481 | /// As an optimization, we want to avoid unnecessary copies of isByRef=true |
| ... | ... | @@ -8560,7 +8500,7 @@ pub const FuncGen = struct { |
| 8560 | 8500 | return false; |
| 8561 | 8501 | } |
| 8562 | 8502 | |
| 8563 | | fn airLoad(fg: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value { |
| 8503 | fn airLoad(fg: *FuncGen, body_tail: []const Air.Inst.Index) !Builder.Value { |
| 8564 | 8504 | const o = fg.dg.object; |
| 8565 | 8505 | const mod = o.module; |
| 8566 | 8506 | const inst = body_tail[0]; |
| ... | ... | @@ -8577,22 +8517,40 @@ pub const FuncGen = struct { |
| 8577 | 8517 | return fg.load(ptr, ptr_ty); |
| 8578 | 8518 | } |
| 8579 | 8519 | |
| 8580 | | fn airTrap(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8520 | fn airTrap(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8581 | 8521 | _ = inst; |
| 8522 | const o = self.dg.object; |
| 8582 | 8523 | const llvm_fn = try self.getIntrinsic("llvm.trap", &.{}); |
| 8583 | | _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, undefined, 0, .Cold, .Auto, ""); |
| 8584 | | _ = self.builder.buildUnreachable(); |
| 8585 | | return null; |
| 8524 | _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall( |
| 8525 | (try o.builder.fnType(.void, &.{}, .normal)).toLlvm(&o.builder), |
| 8526 | llvm_fn, |
| 8527 | undefined, |
| 8528 | 0, |
| 8529 | .Cold, |
| 8530 | .Auto, |
| 8531 | "", |
| 8532 | ), &self.wip); |
| 8533 | _ = try self.wip.@"unreachable"(); |
| 8534 | return .none; |
| 8586 | 8535 | } |
| 8587 | 8536 | |
| 8588 | | fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8537 | fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8589 | 8538 | _ = inst; |
| 8539 | const o = self.dg.object; |
| 8590 | 8540 | const llvm_fn = try self.getIntrinsic("llvm.debugtrap", &.{}); |
| 8591 | | _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, undefined, 0, .C, .Auto, ""); |
| 8592 | | return null; |
| 8541 | _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall( |
| 8542 | (try o.builder.fnType(.void, &.{}, .normal)).toLlvm(&o.builder), |
| 8543 | llvm_fn, |
| 8544 | undefined, |
| 8545 | 0, |
| 8546 | .C, |
| 8547 | .Auto, |
| 8548 | "", |
| 8549 | ), &self.wip); |
| 8550 | return .none; |
| 8593 | 8551 | } |
| 8594 | 8552 | |
| 8595 | | fn airRetAddr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8553 | fn airRetAddr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8596 | 8554 | _ = inst; |
| 8597 | 8555 | const o = self.dg.object; |
| 8598 | 8556 | const mod = o.module; |
| ... | ... | @@ -8600,18 +8558,26 @@ pub const FuncGen = struct { |
| 8600 | 8558 | const target = mod.getTarget(); |
| 8601 | 8559 | if (!target_util.supportsReturnAddress(target)) { |
| 8602 | 8560 | // https://github.com/ziglang/zig/issues/11946 |
| 8603 | | return (try o.builder.intConst(llvm_usize, 0)).toLlvm(&o.builder); |
| 8561 | return o.builder.intValue(llvm_usize, 0); |
| 8604 | 8562 | } |
| 8605 | 8563 | |
| 8606 | 8564 | const llvm_fn = try self.getIntrinsic("llvm.returnaddress", &.{}); |
| 8607 | 8565 | const params = [_]*llvm.Value{ |
| 8608 | 8566 | (try o.builder.intConst(.i32, 0)).toLlvm(&o.builder), |
| 8609 | 8567 | }; |
| 8610 | | const ptr_val = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &params, params.len, .Fast, .Auto, ""); |
| 8611 | | return self.builder.buildPtrToInt(ptr_val, llvm_usize.toLlvm(&o.builder), ""); |
| 8568 | const ptr_val = (try self.wip.unimplemented(.ptr, "")).finish(self.builder.buildCall( |
| 8569 | (try o.builder.fnType(.ptr, &.{.i32}, .normal)).toLlvm(&o.builder), |
| 8570 | llvm_fn, |
| 8571 | &params, |
| 8572 | params.len, |
| 8573 | .Fast, |
| 8574 | .Auto, |
| 8575 | "", |
| 8576 | ), &self.wip); |
| 8577 | return self.wip.cast(.ptrtoint, ptr_val, llvm_usize, ""); |
| 8612 | 8578 | } |
| 8613 | 8579 | |
| 8614 | | fn airFrameAddress(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8580 | fn airFrameAddress(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8615 | 8581 | _ = inst; |
| 8616 | 8582 | const o = self.dg.object; |
| 8617 | 8583 | const llvm_fn_name = "llvm.frameaddress.p0"; |
| ... | ... | @@ -8619,24 +8585,34 @@ pub const FuncGen = struct { |
| 8619 | 8585 | const fn_type = try o.builder.fnType(.ptr, &.{.i32}, .normal); |
| 8620 | 8586 | break :blk o.llvm_module.addFunction(llvm_fn_name, fn_type.toLlvm(&o.builder)); |
| 8621 | 8587 | }; |
| 8588 | const llvm_fn_ty = try o.builder.fnType(.ptr, &.{.i32}, .normal); |
| 8622 | 8589 | |
| 8623 | 8590 | const params = [_]*llvm.Value{ |
| 8624 | 8591 | (try o.builder.intConst(.i32, 0)).toLlvm(&o.builder), |
| 8625 | 8592 | }; |
| 8626 | | const ptr_val = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &params, params.len, .Fast, .Auto, ""); |
| 8627 | | const llvm_usize = (try o.lowerType(Type.usize)).toLlvm(&o.builder); |
| 8628 | | return self.builder.buildPtrToInt(ptr_val, llvm_usize, ""); |
| 8593 | const ptr_val = (try self.wip.unimplemented(llvm_fn_ty.functionReturn(&o.builder), "")).finish( |
| 8594 | self.builder.buildCall( |
| 8595 | llvm_fn_ty.toLlvm(&o.builder), |
| 8596 | llvm_fn, |
| 8597 | &params, |
| 8598 | params.len, |
| 8599 | .Fast, |
| 8600 | .Auto, |
| 8601 | "", |
| 8602 | ), |
| 8603 | &self.wip, |
| 8604 | ); |
| 8605 | return self.wip.cast(.ptrtoint, ptr_val, try o.lowerType(Type.usize), ""); |
| 8629 | 8606 | } |
| 8630 | 8607 | |
| 8631 | | fn airFence(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8608 | fn airFence(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8632 | 8609 | const atomic_order = self.air.instructions.items(.data)[inst].fence; |
| 8633 | | const llvm_memory_order = toLlvmAtomicOrdering(atomic_order); |
| 8634 | | const single_threaded = llvm.Bool.fromBool(self.single_threaded); |
| 8635 | | _ = self.builder.buildFence(llvm_memory_order, single_threaded, ""); |
| 8636 | | return null; |
| 8610 | const ordering = toLlvmAtomicOrdering(atomic_order); |
| 8611 | _ = try self.wip.fence(self.sync_scope, ordering); |
| 8612 | return .none; |
| 8637 | 8613 | } |
| 8638 | 8614 | |
| 8639 | | fn airCmpxchg(self: *FuncGen, inst: Air.Inst.Index, is_weak: bool) !?*llvm.Value { |
| 8615 | fn airCmpxchg(self: *FuncGen, inst: Air.Inst.Index, is_weak: bool) !Builder.Value { |
| 8640 | 8616 | const o = self.dg.object; |
| 8641 | 8617 | const mod = o.module; |
| 8642 | 8618 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| ... | ... | @@ -8645,47 +8621,51 @@ pub const FuncGen = struct { |
| 8645 | 8621 | var expected_value = try self.resolveInst(extra.expected_value); |
| 8646 | 8622 | var new_value = try self.resolveInst(extra.new_value); |
| 8647 | 8623 | const operand_ty = self.typeOf(extra.ptr).childType(mod); |
| 8648 | | const abi_ty = try o.getAtomicAbiType(operand_ty, false); |
| 8649 | | if (abi_ty != .none) { |
| 8650 | | const llvm_abi_ty = abi_ty.toLlvm(&o.builder); |
| 8624 | const llvm_operand_ty = try o.lowerType(operand_ty); |
| 8625 | const llvm_abi_ty = try o.getAtomicAbiType(operand_ty, false); |
| 8626 | if (llvm_abi_ty != .none) { |
| 8651 | 8627 | // operand needs widening and truncating |
| 8652 | | if (operand_ty.isSignedInt(mod)) { |
| 8653 | | expected_value = self.builder.buildSExt(expected_value, llvm_abi_ty, ""); |
| 8654 | | new_value = self.builder.buildSExt(new_value, llvm_abi_ty, ""); |
| 8655 | | } else { |
| 8656 | | expected_value = self.builder.buildZExt(expected_value, llvm_abi_ty, ""); |
| 8657 | | new_value = self.builder.buildZExt(new_value, llvm_abi_ty, ""); |
| 8658 | | } |
| 8628 | const signedness: Builder.Function.Instruction.Cast.Signedness = |
| 8629 | if (operand_ty.isSignedInt(mod)) .signed else .unsigned; |
| 8630 | expected_value = try self.wip.conv(signedness, expected_value, llvm_abi_ty, ""); |
| 8631 | new_value = try self.wip.conv(signedness, new_value, llvm_abi_ty, ""); |
| 8659 | 8632 | } |
| 8660 | | const result = self.builder.buildAtomicCmpXchg( |
| 8661 | | ptr, |
| 8662 | | expected_value, |
| 8663 | | new_value, |
| 8664 | | toLlvmAtomicOrdering(extra.successOrder()), |
| 8665 | | toLlvmAtomicOrdering(extra.failureOrder()), |
| 8666 | | llvm.Bool.fromBool(self.single_threaded), |
| 8633 | |
| 8634 | const llvm_result_ty = try o.builder.structType(.normal, &.{ |
| 8635 | if (llvm_abi_ty != .none) llvm_abi_ty else llvm_operand_ty, |
| 8636 | .i1, |
| 8637 | }); |
| 8638 | const result = (try self.wip.unimplemented(llvm_result_ty, "")).finish( |
| 8639 | self.builder.buildAtomicCmpXchg( |
| 8640 | ptr.toLlvm(&self.wip), |
| 8641 | expected_value.toLlvm(&self.wip), |
| 8642 | new_value.toLlvm(&self.wip), |
| 8643 | @enumFromInt(@intFromEnum(toLlvmAtomicOrdering(extra.successOrder()))), |
| 8644 | @enumFromInt(@intFromEnum(toLlvmAtomicOrdering(extra.failureOrder()))), |
| 8645 | llvm.Bool.fromBool(self.sync_scope == .singlethread), |
| 8646 | ), |
| 8647 | &self.wip, |
| 8667 | 8648 | ); |
| 8668 | | result.setWeak(llvm.Bool.fromBool(is_weak)); |
| 8649 | result.toLlvm(&self.wip).setWeak(llvm.Bool.fromBool(is_weak)); |
| 8669 | 8650 | |
| 8670 | 8651 | const optional_ty = self.typeOfIndex(inst); |
| 8671 | 8652 | |
| 8672 | | var payload = self.builder.buildExtractValue(result, 0, ""); |
| 8673 | | if (abi_ty != .none) { |
| 8674 | | payload = self.builder.buildTrunc(payload, (try o.lowerType(operand_ty)).toLlvm(&o.builder), ""); |
| 8675 | | } |
| 8676 | | const success_bit = self.builder.buildExtractValue(result, 1, ""); |
| 8653 | var payload = try self.wip.extractValue(result, &.{0}, ""); |
| 8654 | if (llvm_abi_ty != .none) payload = try self.wip.cast(.trunc, payload, llvm_operand_ty, ""); |
| 8655 | const success_bit = try self.wip.extractValue(result, &.{1}, ""); |
| 8677 | 8656 | |
| 8678 | 8657 | if (optional_ty.optionalReprIsPayload(mod)) { |
| 8679 | | return self.builder.buildSelect(success_bit, payload.typeOf().constNull(), payload, ""); |
| 8658 | const zero = try o.builder.zeroInitValue(payload.typeOfWip(&self.wip)); |
| 8659 | return self.wip.select(success_bit, zero, payload, ""); |
| 8680 | 8660 | } |
| 8681 | 8661 | |
| 8682 | 8662 | comptime assert(optional_layout_version == 3); |
| 8683 | 8663 | |
| 8684 | | const non_null_bit = self.builder.buildNot(success_bit, ""); |
| 8664 | const non_null_bit = try self.wip.not(success_bit, ""); |
| 8685 | 8665 | return buildOptional(self, optional_ty, payload, non_null_bit); |
| 8686 | 8666 | } |
| 8687 | 8667 | |
| 8688 | | fn airAtomicRmw(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8668 | fn airAtomicRmw(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8689 | 8669 | const o = self.dg.object; |
| 8690 | 8670 | const mod = o.module; |
| 8691 | 8671 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| ... | ... | @@ -8698,121 +8678,146 @@ pub const FuncGen = struct { |
| 8698 | 8678 | const is_float = operand_ty.isRuntimeFloat(); |
| 8699 | 8679 | const op = toLlvmAtomicRmwBinOp(extra.op(), is_signed_int, is_float); |
| 8700 | 8680 | const ordering = toLlvmAtomicOrdering(extra.ordering()); |
| 8701 | | const single_threaded = llvm.Bool.fromBool(self.single_threaded); |
| 8702 | | const abi_ty = try o.getAtomicAbiType(operand_ty, op == .Xchg); |
| 8703 | | if (abi_ty != .none) { |
| 8704 | | const llvm_abi_ty = abi_ty.toLlvm(&o.builder); |
| 8681 | const single_threaded = llvm.Bool.fromBool(self.sync_scope == .singlethread); |
| 8682 | const llvm_abi_ty = try o.getAtomicAbiType(operand_ty, op == .Xchg); |
| 8683 | const llvm_operand_ty = try o.lowerType(operand_ty); |
| 8684 | if (llvm_abi_ty != .none) { |
| 8705 | 8685 | // operand needs widening and truncating or bitcasting. |
| 8706 | | const casted_operand = if (is_float) |
| 8707 | | self.builder.buildBitCast(operand, llvm_abi_ty, "") |
| 8708 | | else if (is_signed_int) |
| 8709 | | self.builder.buildSExt(operand, llvm_abi_ty, "") |
| 8710 | | else |
| 8711 | | self.builder.buildZExt(operand, llvm_abi_ty, ""); |
| 8686 | const casted_operand = try self.wip.cast( |
| 8687 | if (is_float) .bitcast else if (is_signed_int) .sext else .zext, |
| 8688 | @enumFromInt(@intFromEnum(operand)), |
| 8689 | llvm_abi_ty, |
| 8690 | "", |
| 8691 | ); |
| 8712 | 8692 | |
| 8713 | | const uncasted_result = self.builder.buildAtomicRmw( |
| 8714 | | op, |
| 8715 | | ptr, |
| 8716 | | casted_operand, |
| 8717 | | ordering, |
| 8718 | | single_threaded, |
| 8693 | const uncasted_result = (try self.wip.unimplemented(llvm_abi_ty, "")).finish( |
| 8694 | self.builder.buildAtomicRmw( |
| 8695 | op, |
| 8696 | ptr.toLlvm(&self.wip), |
| 8697 | casted_operand.toLlvm(&self.wip), |
| 8698 | @enumFromInt(@intFromEnum(ordering)), |
| 8699 | single_threaded, |
| 8700 | ), |
| 8701 | &self.wip, |
| 8719 | 8702 | ); |
| 8720 | | const operand_llvm_ty = (try o.lowerType(operand_ty)).toLlvm(&o.builder); |
| 8703 | |
| 8721 | 8704 | if (is_float) { |
| 8722 | | return self.builder.buildBitCast(uncasted_result, operand_llvm_ty, ""); |
| 8705 | return self.wip.cast(.bitcast, uncasted_result, llvm_operand_ty, ""); |
| 8723 | 8706 | } else { |
| 8724 | | return self.builder.buildTrunc(uncasted_result, operand_llvm_ty, ""); |
| 8707 | return self.wip.cast(.trunc, uncasted_result, llvm_operand_ty, ""); |
| 8725 | 8708 | } |
| 8726 | 8709 | } |
| 8727 | 8710 | |
| 8728 | | if (operand.typeOf().getTypeKind() != .Pointer) { |
| 8729 | | return self.builder.buildAtomicRmw(op, ptr, operand, ordering, single_threaded); |
| 8711 | if (!llvm_operand_ty.isPointer(&o.builder)) { |
| 8712 | return (try self.wip.unimplemented(llvm_operand_ty, "")).finish( |
| 8713 | self.builder.buildAtomicRmw( |
| 8714 | op, |
| 8715 | ptr.toLlvm(&self.wip), |
| 8716 | operand.toLlvm(&self.wip), |
| 8717 | @enumFromInt(@intFromEnum(ordering)), |
| 8718 | single_threaded, |
| 8719 | ), |
| 8720 | &self.wip, |
| 8721 | ); |
| 8730 | 8722 | } |
| 8731 | 8723 | |
| 8732 | 8724 | // It's a pointer but we need to treat it as an int. |
| 8733 | | const usize_llvm_ty = (try o.lowerType(Type.usize)).toLlvm(&o.builder); |
| 8734 | | const casted_operand = self.builder.buildPtrToInt(operand, usize_llvm_ty, ""); |
| 8735 | | const uncasted_result = self.builder.buildAtomicRmw( |
| 8736 | | op, |
| 8737 | | ptr, |
| 8738 | | casted_operand, |
| 8739 | | ordering, |
| 8740 | | single_threaded, |
| 8725 | const llvm_usize = try o.lowerType(Type.usize); |
| 8726 | const casted_operand = try self.wip.cast(.ptrtoint, operand, llvm_usize, ""); |
| 8727 | const uncasted_result = (try self.wip.unimplemented(llvm_usize, "")).finish( |
| 8728 | self.builder.buildAtomicRmw( |
| 8729 | op, |
| 8730 | ptr.toLlvm(&self.wip), |
| 8731 | casted_operand.toLlvm(&self.wip), |
| 8732 | @enumFromInt(@intFromEnum(ordering)), |
| 8733 | single_threaded, |
| 8734 | ), |
| 8735 | &self.wip, |
| 8741 | 8736 | ); |
| 8742 | | const operand_llvm_ty = (try o.lowerType(operand_ty)).toLlvm(&o.builder); |
| 8743 | | return self.builder.buildIntToPtr(uncasted_result, operand_llvm_ty, ""); |
| 8737 | return self.wip.cast(.inttoptr, uncasted_result, llvm_operand_ty, ""); |
| 8744 | 8738 | } |
| 8745 | 8739 | |
| 8746 | | fn airAtomicLoad(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8740 | fn airAtomicLoad(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8747 | 8741 | const o = self.dg.object; |
| 8748 | 8742 | const mod = o.module; |
| 8749 | 8743 | const atomic_load = self.air.instructions.items(.data)[inst].atomic_load; |
| 8750 | 8744 | const ptr = try self.resolveInst(atomic_load.ptr); |
| 8751 | 8745 | const ptr_ty = self.typeOf(atomic_load.ptr); |
| 8752 | | const ptr_info = ptr_ty.ptrInfo(mod); |
| 8753 | | const elem_ty = ptr_info.child.toType(); |
| 8754 | | if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) |
| 8755 | | return null; |
| 8746 | const info = ptr_ty.ptrInfo(mod); |
| 8747 | const elem_ty = info.child.toType(); |
| 8748 | if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) return .none; |
| 8756 | 8749 | const ordering = toLlvmAtomicOrdering(atomic_load.order); |
| 8757 | | const abi_ty = try o.getAtomicAbiType(elem_ty, false); |
| 8758 | | const ptr_alignment: u32 = @intCast(ptr_info.flags.alignment.toByteUnitsOptional() orelse |
| 8759 | | ptr_info.child.toType().abiAlignment(mod)); |
| 8760 | | const ptr_volatile = llvm.Bool.fromBool(ptr_info.flags.is_volatile); |
| 8761 | | const elem_llvm_ty = (try o.lowerType(elem_ty)).toLlvm(&o.builder); |
| 8762 | | |
| 8763 | | if (abi_ty != .none) { |
| 8764 | | const llvm_abi_ty = abi_ty.toLlvm(&o.builder); |
| 8750 | const llvm_abi_ty = try o.getAtomicAbiType(elem_ty, false); |
| 8751 | const ptr_alignment = Builder.Alignment.fromByteUnits( |
| 8752 | info.flags.alignment.toByteUnitsOptional() orelse info.child.toType().abiAlignment(mod), |
| 8753 | ); |
| 8754 | const ptr_kind: Builder.MemoryAccessKind = switch (info.flags.is_volatile) { |
| 8755 | false => .normal, |
| 8756 | true => .@"volatile", |
| 8757 | }; |
| 8758 | const elem_llvm_ty = try o.lowerType(elem_ty); |
| 8759 | |
| 8760 | if (llvm_abi_ty != .none) { |
| 8765 | 8761 | // operand needs widening and truncating |
| 8766 | | const load_inst = self.builder.buildLoad(llvm_abi_ty, ptr, ""); |
| 8767 | | load_inst.setAlignment(ptr_alignment); |
| 8768 | | load_inst.setVolatile(ptr_volatile); |
| 8769 | | load_inst.setOrdering(ordering); |
| 8770 | | return self.builder.buildTrunc(load_inst, elem_llvm_ty, ""); |
| 8762 | const loaded = try self.wip.loadAtomic( |
| 8763 | ptr_kind, |
| 8764 | llvm_abi_ty, |
| 8765 | ptr, |
| 8766 | self.sync_scope, |
| 8767 | ordering, |
| 8768 | ptr_alignment, |
| 8769 | "", |
| 8770 | ); |
| 8771 | return self.wip.cast(.trunc, loaded, elem_llvm_ty, ""); |
| 8771 | 8772 | } |
| 8772 | | const load_inst = self.builder.buildLoad(elem_llvm_ty, ptr, ""); |
| 8773 | | load_inst.setAlignment(ptr_alignment); |
| 8774 | | load_inst.setVolatile(ptr_volatile); |
| 8775 | | load_inst.setOrdering(ordering); |
| 8776 | | return load_inst; |
| 8773 | return self.wip.loadAtomic( |
| 8774 | ptr_kind, |
| 8775 | elem_llvm_ty, |
| 8776 | ptr, |
| 8777 | self.sync_scope, |
| 8778 | ordering, |
| 8779 | ptr_alignment, |
| 8780 | "", |
| 8781 | ); |
| 8777 | 8782 | } |
| 8778 | 8783 | |
| 8779 | 8784 | fn airAtomicStore( |
| 8780 | 8785 | self: *FuncGen, |
| 8781 | 8786 | inst: Air.Inst.Index, |
| 8782 | | ordering: llvm.AtomicOrdering, |
| 8783 | | ) !?*llvm.Value { |
| 8787 | ordering: Builder.AtomicOrdering, |
| 8788 | ) !Builder.Value { |
| 8784 | 8789 | const o = self.dg.object; |
| 8785 | 8790 | const mod = o.module; |
| 8786 | 8791 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 8787 | 8792 | const ptr_ty = self.typeOf(bin_op.lhs); |
| 8788 | 8793 | const operand_ty = ptr_ty.childType(mod); |
| 8789 | | if (!operand_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) return null; |
| 8794 | if (!operand_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) return .none; |
| 8790 | 8795 | const ptr = try self.resolveInst(bin_op.lhs); |
| 8791 | 8796 | var element = try self.resolveInst(bin_op.rhs); |
| 8792 | | const abi_ty = try o.getAtomicAbiType(operand_ty, false); |
| 8797 | const llvm_abi_ty = try o.getAtomicAbiType(operand_ty, false); |
| 8793 | 8798 | |
| 8794 | | if (abi_ty != .none) { |
| 8795 | | const llvm_abi_ty = abi_ty.toLlvm(&o.builder); |
| 8799 | if (llvm_abi_ty != .none) { |
| 8796 | 8800 | // operand needs widening |
| 8797 | | if (operand_ty.isSignedInt(mod)) { |
| 8798 | | element = self.builder.buildSExt(element, llvm_abi_ty, ""); |
| 8799 | | } else { |
| 8800 | | element = self.builder.buildZExt(element, llvm_abi_ty, ""); |
| 8801 | | } |
| 8801 | element = try self.wip.conv( |
| 8802 | if (operand_ty.isSignedInt(mod)) .signed else .unsigned, |
| 8803 | element, |
| 8804 | llvm_abi_ty, |
| 8805 | "", |
| 8806 | ); |
| 8802 | 8807 | } |
| 8803 | 8808 | try self.store(ptr, ptr_ty, element, ordering); |
| 8804 | | return null; |
| 8809 | return .none; |
| 8805 | 8810 | } |
| 8806 | 8811 | |
| 8807 | | fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !?*llvm.Value { |
| 8812 | fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !Builder.Value { |
| 8808 | 8813 | const o = self.dg.object; |
| 8809 | 8814 | const mod = o.module; |
| 8810 | 8815 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 8811 | 8816 | const dest_slice = try self.resolveInst(bin_op.lhs); |
| 8812 | 8817 | const ptr_ty = self.typeOf(bin_op.lhs); |
| 8813 | 8818 | const elem_ty = self.typeOf(bin_op.rhs); |
| 8814 | | const dest_ptr_align = ptr_ty.ptrAlignment(mod); |
| 8815 | | const dest_ptr = self.sliceOrArrayPtr(dest_slice, ptr_ty); |
| 8819 | const dest_ptr_align = Builder.Alignment.fromByteUnits(ptr_ty.ptrAlignment(mod)); |
| 8820 | const dest_ptr = try self.sliceOrArrayPtr(dest_slice, ptr_ty); |
| 8816 | 8821 | const is_volatile = ptr_ty.isVolatilePtr(mod); |
| 8817 | 8822 | |
| 8818 | 8823 | // Any WebAssembly runtime will trap when the destination pointer is out-of-bounds, regardless |
| ... | ... | @@ -8829,20 +8834,26 @@ pub const FuncGen = struct { |
| 8829 | 8834 | // extra information to LLVM. However, safety makes the difference between using |
| 8830 | 8835 | // 0xaa or actual undefined for the fill byte. |
| 8831 | 8836 | const fill_byte = if (safety) |
| 8832 | | (try o.builder.intConst(.i8, 0xaa)).toLlvm(&o.builder) |
| 8837 | try o.builder.intValue(.i8, 0xaa) |
| 8833 | 8838 | else |
| 8834 | | Builder.Type.i8.toLlvm(&o.builder).getUndef(); |
| 8839 | try o.builder.undefValue(.i8); |
| 8835 | 8840 | const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); |
| 8836 | 8841 | if (intrinsic_len0_traps) { |
| 8837 | 8842 | try self.safeWasmMemset(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile); |
| 8838 | 8843 | } else { |
| 8839 | | _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile); |
| 8844 | _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildMemSet( |
| 8845 | dest_ptr.toLlvm(&self.wip), |
| 8846 | fill_byte.toLlvm(&self.wip), |
| 8847 | len.toLlvm(&self.wip), |
| 8848 | @intCast(dest_ptr_align.toByteUnits() orelse 0), |
| 8849 | is_volatile, |
| 8850 | ), &self.wip); |
| 8840 | 8851 | } |
| 8841 | 8852 | |
| 8842 | 8853 | if (safety and mod.comp.bin_file.options.valgrind) { |
| 8843 | 8854 | try self.valgrindMarkUndef(dest_ptr, len); |
| 8844 | 8855 | } |
| 8845 | | return null; |
| 8856 | return .none; |
| 8846 | 8857 | } |
| 8847 | 8858 | |
| 8848 | 8859 | // Test if the element value is compile-time known to be a |
| ... | ... | @@ -8850,18 +8861,21 @@ pub const FuncGen = struct { |
| 8850 | 8861 | // repeating byte pattern of 0 bytes. In such case, the memset |
| 8851 | 8862 | // intrinsic can be used. |
| 8852 | 8863 | if (try elem_val.hasRepeatedByteRepr(elem_ty, mod)) |byte_val| { |
| 8853 | | const fill_byte = try self.resolveValue(.{ |
| 8854 | | .ty = Type.u8, |
| 8855 | | .val = byte_val, |
| 8856 | | }); |
| 8864 | const fill_byte = try self.resolveValue(.{ .ty = Type.u8, .val = byte_val }); |
| 8857 | 8865 | const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); |
| 8858 | 8866 | |
| 8859 | 8867 | if (intrinsic_len0_traps) { |
| 8860 | | try self.safeWasmMemset(dest_ptr, fill_byte.toLlvm(&o.builder), len, dest_ptr_align, is_volatile); |
| 8868 | try self.safeWasmMemset(dest_ptr, fill_byte.toValue(), len, dest_ptr_align, is_volatile); |
| 8861 | 8869 | } else { |
| 8862 | | _ = self.builder.buildMemSet(dest_ptr, fill_byte.toLlvm(&o.builder), len, dest_ptr_align, is_volatile); |
| 8870 | _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildMemSet( |
| 8871 | dest_ptr.toLlvm(&self.wip), |
| 8872 | fill_byte.toLlvm(&o.builder), |
| 8873 | len.toLlvm(&self.wip), |
| 8874 | @intCast(dest_ptr_align.toByteUnits() orelse 0), |
| 8875 | is_volatile, |
| 8876 | ), &self.wip); |
| 8863 | 8877 | } |
| 8864 | | return null; |
| 8878 | return .none; |
| 8865 | 8879 | } |
| 8866 | 8880 | } |
| 8867 | 8881 | |
| ... | ... | @@ -8876,9 +8890,15 @@ pub const FuncGen = struct { |
| 8876 | 8890 | if (intrinsic_len0_traps) { |
| 8877 | 8891 | try self.safeWasmMemset(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile); |
| 8878 | 8892 | } else { |
| 8879 | | _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile); |
| 8893 | _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildMemSet( |
| 8894 | dest_ptr.toLlvm(&self.wip), |
| 8895 | fill_byte.toLlvm(&self.wip), |
| 8896 | len.toLlvm(&self.wip), |
| 8897 | @intCast(dest_ptr_align.toByteUnits() orelse 0), |
| 8898 | is_volatile, |
| 8899 | ), &self.wip); |
| 8880 | 8900 | } |
| 8881 | | return null; |
| 8901 | return .none; |
| 8882 | 8902 | } |
| 8883 | 8903 | |
| 8884 | 8904 | // non-byte-sized element. lower with a loop. something like this: |
| ... | ... | @@ -8886,96 +8906,92 @@ pub const FuncGen = struct { |
| 8886 | 8906 | // entry: |
| 8887 | 8907 | // ... |
| 8888 | 8908 | // %end_ptr = getelementptr %ptr, %len |
| 8889 | | // br loop |
| 8909 | // br %loop |
| 8890 | 8910 | // loop: |
| 8891 | 8911 | // %it_ptr = phi body %next_ptr, entry %ptr |
| 8892 | 8912 | // %end = cmp eq %it_ptr, %end_ptr |
| 8893 | | // cond_br %end body, end |
| 8913 | // br %end, %body, %end |
| 8894 | 8914 | // body: |
| 8895 | 8915 | // store %it_ptr, %value |
| 8896 | 8916 | // %next_ptr = getelementptr %it_ptr, 1 |
| 8897 | | // br loop |
| 8917 | // br %loop |
| 8898 | 8918 | // end: |
| 8899 | 8919 | // ... |
| 8900 | | const entry_block = self.builder.getInsertBlock(); |
| 8901 | | const loop_block = try self.wip.block("InlineMemsetLoop"); |
| 8902 | | const body_block = try self.wip.block("InlineMemsetBody"); |
| 8903 | | const end_block = try self.wip.block("InlineMemsetEnd"); |
| 8920 | const entry_block = self.wip.cursor.block; |
| 8921 | const loop_block = try self.wip.block(2, "InlineMemsetLoop"); |
| 8922 | const body_block = try self.wip.block(1, "InlineMemsetBody"); |
| 8923 | const end_block = try self.wip.block(1, "InlineMemsetEnd"); |
| 8904 | 8924 | |
| 8905 | 8925 | const usize_ty = try o.lowerType(Type.usize); |
| 8906 | 8926 | const len = switch (ptr_ty.ptrSize(mod)) { |
| 8907 | | .Slice => self.builder.buildExtractValue(dest_slice, 1, ""), |
| 8908 | | .One => (try o.builder.intConst(usize_ty, ptr_ty.childType(mod).arrayLen(mod))).toLlvm(&o.builder), |
| 8927 | .Slice => try self.wip.extractValue(dest_slice, &.{1}, ""), |
| 8928 | .One => try o.builder.intValue(usize_ty, ptr_ty.childType(mod).arrayLen(mod)), |
| 8909 | 8929 | .Many, .C => unreachable, |
| 8910 | 8930 | }; |
| 8911 | | const elem_llvm_ty = (try o.lowerType(elem_ty)).toLlvm(&o.builder); |
| 8912 | | const len_gep = [_]*llvm.Value{len}; |
| 8913 | | const end_ptr = self.builder.buildInBoundsGEP(elem_llvm_ty, dest_ptr, &len_gep, len_gep.len, ""); |
| 8914 | | _ = self.builder.buildBr(loop_block.toLlvm(&self.wip)); |
| 8931 | const elem_llvm_ty = try o.lowerType(elem_ty); |
| 8932 | const end_ptr = try self.wip.gep(.inbounds, elem_llvm_ty, dest_ptr, &.{len}, ""); |
| 8933 | _ = try self.wip.br(loop_block); |
| 8915 | 8934 | |
| 8916 | 8935 | self.wip.cursor = .{ .block = loop_block }; |
| 8917 | | self.builder.positionBuilderAtEnd(loop_block.toLlvm(&self.wip)); |
| 8918 | | const it_ptr = self.builder.buildPhi(Builder.Type.ptr.toLlvm(&o.builder), ""); |
| 8919 | | const end = self.builder.buildICmp(.NE, it_ptr, end_ptr, ""); |
| 8920 | | _ = self.builder.buildCondBr(end, body_block.toLlvm(&self.wip), end_block.toLlvm(&self.wip)); |
| 8936 | const it_ptr = try self.wip.phi(.ptr, ""); |
| 8937 | const end = try self.wip.icmp(.ne, it_ptr.toValue(), end_ptr, ""); |
| 8938 | _ = try self.wip.brCond(end, body_block, end_block); |
| 8921 | 8939 | |
| 8922 | 8940 | self.wip.cursor = .{ .block = body_block }; |
| 8923 | | self.builder.positionBuilderAtEnd(body_block.toLlvm(&self.wip)); |
| 8924 | 8941 | const elem_abi_alignment = elem_ty.abiAlignment(mod); |
| 8925 | | const it_ptr_alignment = @min(elem_abi_alignment, dest_ptr_align); |
| 8942 | const it_ptr_alignment = Builder.Alignment.fromByteUnits( |
| 8943 | @min(elem_abi_alignment, dest_ptr_align.toByteUnits() orelse std.math.maxInt(u64)), |
| 8944 | ); |
| 8926 | 8945 | if (isByRef(elem_ty, mod)) { |
| 8927 | | _ = self.builder.buildMemCpy( |
| 8928 | | it_ptr, |
| 8929 | | it_ptr_alignment, |
| 8930 | | value, |
| 8946 | _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildMemCpy( |
| 8947 | it_ptr.toValue().toLlvm(&self.wip), |
| 8948 | @intCast(it_ptr_alignment.toByteUnits() orelse 0), |
| 8949 | value.toLlvm(&self.wip), |
| 8931 | 8950 | elem_abi_alignment, |
| 8932 | 8951 | (try o.builder.intConst(usize_ty, elem_abi_size)).toLlvm(&o.builder), |
| 8933 | 8952 | is_volatile, |
| 8934 | | ); |
| 8935 | | } else { |
| 8936 | | const store_inst = self.builder.buildStore(value, it_ptr); |
| 8937 | | store_inst.setAlignment(it_ptr_alignment); |
| 8938 | | store_inst.setVolatile(llvm.Bool.fromBool(is_volatile)); |
| 8939 | | } |
| 8940 | | const one_gep = [_]*llvm.Value{ |
| 8941 | | (try o.builder.intConst(usize_ty, 1)).toLlvm(&o.builder), |
| 8942 | | }; |
| 8943 | | const next_ptr = self.builder.buildInBoundsGEP(elem_llvm_ty, it_ptr, &one_gep, one_gep.len, ""); |
| 8944 | | _ = self.builder.buildBr(loop_block.toLlvm(&self.wip)); |
| 8953 | ), &self.wip); |
| 8954 | } else _ = try self.wip.store(switch (is_volatile) { |
| 8955 | false => .normal, |
| 8956 | true => .@"volatile", |
| 8957 | }, value, it_ptr.toValue(), it_ptr_alignment); |
| 8958 | const next_ptr = try self.wip.gep(.inbounds, elem_llvm_ty, it_ptr.toValue(), &.{ |
| 8959 | try o.builder.intValue(usize_ty, 1), |
| 8960 | }, ""); |
| 8961 | _ = try self.wip.br(loop_block); |
| 8945 | 8962 | |
| 8946 | 8963 | self.wip.cursor = .{ .block = end_block }; |
| 8947 | | self.builder.positionBuilderAtEnd(end_block.toLlvm(&self.wip)); |
| 8948 | | |
| 8949 | | const incoming_values: [2]*llvm.Value = .{ next_ptr, dest_ptr }; |
| 8950 | | const incoming_blocks: [2]*llvm.BasicBlock = .{ body_block.toLlvm(&self.wip), entry_block }; |
| 8951 | | it_ptr.addIncoming(&incoming_values, &incoming_blocks, 2); |
| 8952 | | |
| 8953 | | return null; |
| 8964 | try it_ptr.finish(&.{ next_ptr, dest_ptr }, &.{ body_block, entry_block }, &self.wip); |
| 8965 | return .none; |
| 8954 | 8966 | } |
| 8955 | 8967 | |
| 8956 | 8968 | fn safeWasmMemset( |
| 8957 | 8969 | self: *FuncGen, |
| 8958 | | dest_ptr: *llvm.Value, |
| 8959 | | fill_byte: *llvm.Value, |
| 8960 | | len: *llvm.Value, |
| 8961 | | dest_ptr_align: u32, |
| 8970 | dest_ptr: Builder.Value, |
| 8971 | fill_byte: Builder.Value, |
| 8972 | len: Builder.Value, |
| 8973 | dest_ptr_align: Builder.Alignment, |
| 8962 | 8974 | is_volatile: bool, |
| 8963 | 8975 | ) !void { |
| 8964 | 8976 | const o = self.dg.object; |
| 8965 | 8977 | const llvm_usize_ty = try o.lowerType(Type.usize); |
| 8966 | | const cond = try self.cmp(len, (try o.builder.intConst(llvm_usize_ty, 0)).toLlvm(&o.builder), Type.usize, .neq); |
| 8967 | | const memset_block = try self.wip.block("MemsetTrapSkip"); |
| 8968 | | const end_block = try self.wip.block("MemsetTrapEnd"); |
| 8969 | | _ = self.builder.buildCondBr(cond, memset_block.toLlvm(&self.wip), end_block.toLlvm(&self.wip)); |
| 8978 | const cond = try self.cmp(len, try o.builder.intValue(llvm_usize_ty, 0), Type.usize, .neq); |
| 8979 | const memset_block = try self.wip.block(1, "MemsetTrapSkip"); |
| 8980 | const end_block = try self.wip.block(2, "MemsetTrapEnd"); |
| 8981 | _ = try self.wip.brCond(cond, memset_block, end_block); |
| 8970 | 8982 | self.wip.cursor = .{ .block = memset_block }; |
| 8971 | | self.builder.positionBuilderAtEnd(memset_block.toLlvm(&self.wip)); |
| 8972 | | _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile); |
| 8973 | | _ = self.builder.buildBr(end_block.toLlvm(&self.wip)); |
| 8983 | _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildMemSet( |
| 8984 | dest_ptr.toLlvm(&self.wip), |
| 8985 | fill_byte.toLlvm(&self.wip), |
| 8986 | len.toLlvm(&self.wip), |
| 8987 | @intCast(dest_ptr_align.toByteUnits() orelse 0), |
| 8988 | is_volatile, |
| 8989 | ), &self.wip); |
| 8990 | _ = try self.wip.br(end_block); |
| 8974 | 8991 | self.wip.cursor = .{ .block = end_block }; |
| 8975 | | self.builder.positionBuilderAtEnd(end_block.toLlvm(&self.wip)); |
| 8976 | 8992 | } |
| 8977 | 8993 | |
| 8978 | | fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8994 | fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 8979 | 8995 | const o = self.dg.object; |
| 8980 | 8996 | const mod = o.module; |
| 8981 | 8997 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| ... | ... | @@ -8983,9 +8999,9 @@ pub const FuncGen = struct { |
| 8983 | 8999 | const dest_ptr_ty = self.typeOf(bin_op.lhs); |
| 8984 | 9000 | const src_slice = try self.resolveInst(bin_op.rhs); |
| 8985 | 9001 | const src_ptr_ty = self.typeOf(bin_op.rhs); |
| 8986 | | const src_ptr = self.sliceOrArrayPtr(src_slice, src_ptr_ty); |
| 9002 | const src_ptr = try self.sliceOrArrayPtr(src_slice, src_ptr_ty); |
| 8987 | 9003 | const len = try self.sliceOrArrayLenInBytes(dest_slice, dest_ptr_ty); |
| 8988 | | const dest_ptr = self.sliceOrArrayPtr(dest_slice, dest_ptr_ty); |
| 9004 | const dest_ptr = try self.sliceOrArrayPtr(dest_slice, dest_ptr_ty); |
| 8989 | 9005 | const is_volatile = src_ptr_ty.isVolatilePtr(mod) or dest_ptr_ty.isVolatilePtr(mod); |
| 8990 | 9006 | |
| 8991 | 9007 | // When bulk-memory is enabled, this will be lowered to WebAssembly's memory.copy instruction. |
| ... | ... | @@ -8997,86 +9013,81 @@ pub const FuncGen = struct { |
| 8997 | 9013 | std.Target.wasm.featureSetHas(o.target.cpu.features, .bulk_memory) and |
| 8998 | 9014 | dest_ptr_ty.isSlice(mod)) |
| 8999 | 9015 | { |
| 9000 | | const llvm_usize_ty = try o.lowerType(Type.usize); |
| 9001 | | const cond = try self.cmp(len, (try o.builder.intConst(llvm_usize_ty, 0)).toLlvm(&o.builder), Type.usize, .neq); |
| 9002 | | const memcpy_block = try self.wip.block("MemcpyTrapSkip"); |
| 9003 | | const end_block = try self.wip.block("MemcpyTrapEnd"); |
| 9004 | | _ = self.builder.buildCondBr(cond, memcpy_block.toLlvm(&self.wip), end_block.toLlvm(&self.wip)); |
| 9016 | const zero_usize = try o.builder.intValue(try o.lowerType(Type.usize), 0); |
| 9017 | const cond = try self.cmp(len, zero_usize, Type.usize, .neq); |
| 9018 | const memcpy_block = try self.wip.block(1, "MemcpyTrapSkip"); |
| 9019 | const end_block = try self.wip.block(2, "MemcpyTrapEnd"); |
| 9020 | _ = try self.wip.brCond(cond, memcpy_block, end_block); |
| 9005 | 9021 | self.wip.cursor = .{ .block = memcpy_block }; |
| 9006 | | self.builder.positionBuilderAtEnd(memcpy_block.toLlvm(&self.wip)); |
| 9007 | | _ = self.builder.buildMemCpy( |
| 9008 | | dest_ptr, |
| 9022 | _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildMemCpy( |
| 9023 | dest_ptr.toLlvm(&self.wip), |
| 9009 | 9024 | dest_ptr_ty.ptrAlignment(mod), |
| 9010 | | src_ptr, |
| 9025 | src_ptr.toLlvm(&self.wip), |
| 9011 | 9026 | src_ptr_ty.ptrAlignment(mod), |
| 9012 | | len, |
| 9027 | len.toLlvm(&self.wip), |
| 9013 | 9028 | is_volatile, |
| 9014 | | ); |
| 9015 | | _ = self.builder.buildBr(end_block.toLlvm(&self.wip)); |
| 9029 | ), &self.wip); |
| 9030 | _ = try self.wip.br(end_block); |
| 9016 | 9031 | self.wip.cursor = .{ .block = end_block }; |
| 9017 | | self.builder.positionBuilderAtEnd(end_block.toLlvm(&self.wip)); |
| 9018 | | return null; |
| 9032 | return .none; |
| 9019 | 9033 | } |
| 9020 | 9034 | |
| 9021 | | _ = self.builder.buildMemCpy( |
| 9022 | | dest_ptr, |
| 9035 | _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildMemCpy( |
| 9036 | dest_ptr.toLlvm(&self.wip), |
| 9023 | 9037 | dest_ptr_ty.ptrAlignment(mod), |
| 9024 | | src_ptr, |
| 9038 | src_ptr.toLlvm(&self.wip), |
| 9025 | 9039 | src_ptr_ty.ptrAlignment(mod), |
| 9026 | | len, |
| 9040 | len.toLlvm(&self.wip), |
| 9027 | 9041 | is_volatile, |
| 9028 | | ); |
| 9029 | | return null; |
| 9042 | ), &self.wip); |
| 9043 | return .none; |
| 9030 | 9044 | } |
| 9031 | 9045 | |
| 9032 | | fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 9046 | fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 9033 | 9047 | const o = self.dg.object; |
| 9034 | 9048 | const mod = o.module; |
| 9035 | 9049 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 9036 | 9050 | const un_ty = self.typeOf(bin_op.lhs).childType(mod); |
| 9037 | 9051 | const layout = un_ty.unionGetLayout(mod); |
| 9038 | | if (layout.tag_size == 0) return null; |
| 9052 | if (layout.tag_size == 0) return .none; |
| 9039 | 9053 | const union_ptr = try self.resolveInst(bin_op.lhs); |
| 9040 | 9054 | const new_tag = try self.resolveInst(bin_op.rhs); |
| 9041 | 9055 | if (layout.payload_size == 0) { |
| 9042 | 9056 | // TODO alignment on this store |
| 9043 | | _ = self.builder.buildStore(new_tag, union_ptr); |
| 9044 | | return null; |
| 9057 | _ = try self.wip.store(.normal, new_tag, union_ptr, .default); |
| 9058 | return .none; |
| 9045 | 9059 | } |
| 9046 | | const un_llvm_ty = (try o.lowerType(un_ty)).toLlvm(&o.builder); |
| 9047 | 9060 | const tag_index = @intFromBool(layout.tag_align < layout.payload_align); |
| 9048 | | const tag_field_ptr = self.builder.buildStructGEP(un_llvm_ty, union_ptr, tag_index, ""); |
| 9061 | const tag_field_ptr = try self.wip.gepStruct(try o.lowerType(un_ty), union_ptr, tag_index, ""); |
| 9049 | 9062 | // TODO alignment on this store |
| 9050 | | _ = self.builder.buildStore(new_tag, tag_field_ptr); |
| 9051 | | return null; |
| 9063 | _ = try self.wip.store(.normal, new_tag, tag_field_ptr, .default); |
| 9064 | return .none; |
| 9052 | 9065 | } |
| 9053 | 9066 | |
| 9054 | | fn airGetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 9067 | fn airGetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 9055 | 9068 | const o = self.dg.object; |
| 9056 | 9069 | const mod = o.module; |
| 9057 | 9070 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 9058 | 9071 | const un_ty = self.typeOf(ty_op.operand); |
| 9059 | 9072 | const layout = un_ty.unionGetLayout(mod); |
| 9060 | | if (layout.tag_size == 0) return null; |
| 9073 | if (layout.tag_size == 0) return .none; |
| 9061 | 9074 | const union_handle = try self.resolveInst(ty_op.operand); |
| 9062 | 9075 | if (isByRef(un_ty, mod)) { |
| 9063 | | const llvm_un_ty = (try o.lowerType(un_ty)).toLlvm(&o.builder); |
| 9064 | | if (layout.payload_size == 0) { |
| 9065 | | return self.builder.buildLoad(llvm_un_ty, union_handle, ""); |
| 9066 | | } |
| 9076 | const llvm_un_ty = try o.lowerType(un_ty); |
| 9077 | if (layout.payload_size == 0) |
| 9078 | return self.wip.load(.normal, llvm_un_ty, union_handle, .default, ""); |
| 9067 | 9079 | const tag_index = @intFromBool(layout.tag_align < layout.payload_align); |
| 9068 | | const tag_field_ptr = self.builder.buildStructGEP(llvm_un_ty, union_handle, tag_index, ""); |
| 9069 | | return self.builder.buildLoad(llvm_un_ty.structGetTypeAtIndex(tag_index), tag_field_ptr, ""); |
| 9080 | const tag_field_ptr = try self.wip.gepStruct(llvm_un_ty, union_handle, tag_index, ""); |
| 9081 | const llvm_tag_ty = llvm_un_ty.structFields(&o.builder)[tag_index]; |
| 9082 | return self.wip.load(.normal, llvm_tag_ty, tag_field_ptr, .default, ""); |
| 9070 | 9083 | } else { |
| 9071 | | if (layout.payload_size == 0) { |
| 9072 | | return union_handle; |
| 9073 | | } |
| 9084 | if (layout.payload_size == 0) return union_handle; |
| 9074 | 9085 | const tag_index = @intFromBool(layout.tag_align < layout.payload_align); |
| 9075 | | return self.builder.buildExtractValue(union_handle, tag_index, ""); |
| 9086 | return self.wip.extractValue(union_handle, &.{tag_index}, ""); |
| 9076 | 9087 | } |
| 9077 | 9088 | } |
| 9078 | 9089 | |
| 9079 | | fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, comptime op: FloatOp) !?*llvm.Value { |
| 9090 | fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, comptime op: FloatOp) !Builder.Value { |
| 9080 | 9091 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 9081 | 9092 | const operand = try self.resolveInst(un_op); |
| 9082 | 9093 | const operand_ty = self.typeOf(un_op); |
| ... | ... | @@ -9084,7 +9095,7 @@ pub const FuncGen = struct { |
| 9084 | 9095 | return self.buildFloatOp(op, operand_ty, 1, .{operand}); |
| 9085 | 9096 | } |
| 9086 | 9097 | |
| 9087 | | fn airNeg(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value { |
| 9098 | fn airNeg(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !Builder.Value { |
| 9088 | 9099 | self.builder.setFastMath(want_fast_math); |
| 9089 | 9100 | |
| 9090 | 9101 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| ... | ... | @@ -9094,57 +9105,64 @@ pub const FuncGen = struct { |
| 9094 | 9105 | return self.buildFloatOp(.neg, operand_ty, 1, .{operand}); |
| 9095 | 9106 | } |
| 9096 | 9107 | |
| 9097 | | fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*llvm.Value { |
| 9108 | fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !Builder.Value { |
| 9098 | 9109 | const o = self.dg.object; |
| 9099 | | const mod = o.module; |
| 9100 | 9110 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 9101 | 9111 | const operand_ty = self.typeOf(ty_op.operand); |
| 9102 | 9112 | const operand = try self.resolveInst(ty_op.operand); |
| 9103 | 9113 | |
| 9104 | | const fn_val = try self.getIntrinsic(llvm_fn_name, &.{try o.lowerType(operand_ty)}); |
| 9114 | const llvm_operand_ty = try o.lowerType(operand_ty); |
| 9115 | const llvm_fn_ty = try o.builder.fnType(llvm_operand_ty, &.{ llvm_operand_ty, .i1 }, .normal); |
| 9116 | const fn_val = try self.getIntrinsic(llvm_fn_name, &.{llvm_operand_ty}); |
| 9105 | 9117 | |
| 9106 | | const params = [_]*llvm.Value{ operand, Builder.Constant.false.toLlvm(&o.builder) }; |
| 9107 | | const wrong_size_result = self.builder.buildCall(fn_val.globalGetValueType(), fn_val, &params, params.len, .C, .Auto, ""); |
| 9118 | const params = [_]*llvm.Value{ |
| 9119 | operand.toLlvm(&self.wip), |
| 9120 | Builder.Constant.false.toLlvm(&o.builder), |
| 9121 | }; |
| 9122 | const wrong_size_result = (try self.wip.unimplemented(llvm_operand_ty, "")).finish( |
| 9123 | self.builder.buildCall( |
| 9124 | llvm_fn_ty.toLlvm(&o.builder), |
| 9125 | fn_val, |
| 9126 | &params, |
| 9127 | params.len, |
| 9128 | .C, |
| 9129 | .Auto, |
| 9130 | "", |
| 9131 | ), |
| 9132 | &self.wip, |
| 9133 | ); |
| 9108 | 9134 | const result_ty = self.typeOfIndex(inst); |
| 9109 | | const result_llvm_ty = (try o.lowerType(result_ty)).toLlvm(&o.builder); |
| 9110 | | |
| 9111 | | const bits = operand_ty.intInfo(mod).bits; |
| 9112 | | const result_bits = result_ty.intInfo(mod).bits; |
| 9113 | | if (bits > result_bits) { |
| 9114 | | return self.builder.buildTrunc(wrong_size_result, result_llvm_ty, ""); |
| 9115 | | } else if (bits < result_bits) { |
| 9116 | | return self.builder.buildZExt(wrong_size_result, result_llvm_ty, ""); |
| 9117 | | } else { |
| 9118 | | return wrong_size_result; |
| 9119 | | } |
| 9135 | return self.wip.conv(.unsigned, wrong_size_result, try o.lowerType(result_ty), ""); |
| 9120 | 9136 | } |
| 9121 | 9137 | |
| 9122 | | fn airBitOp(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*llvm.Value { |
| 9138 | fn airBitOp(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !Builder.Value { |
| 9123 | 9139 | const o = self.dg.object; |
| 9124 | | const mod = o.module; |
| 9125 | 9140 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 9126 | 9141 | const operand_ty = self.typeOf(ty_op.operand); |
| 9127 | 9142 | const operand = try self.resolveInst(ty_op.operand); |
| 9128 | 9143 | |
| 9129 | | const params = [_]*llvm.Value{operand}; |
| 9130 | | const fn_val = try self.getIntrinsic(llvm_fn_name, &.{try o.lowerType(operand_ty)}); |
| 9131 | | |
| 9132 | | const wrong_size_result = self.builder.buildCall(fn_val.globalGetValueType(), fn_val, &params, params.len, .C, .Auto, ""); |
| 9144 | const llvm_operand_ty = try o.lowerType(operand_ty); |
| 9145 | const llvm_fn_ty = try o.builder.fnType(llvm_operand_ty, &.{llvm_operand_ty}, .normal); |
| 9146 | const fn_val = try self.getIntrinsic(llvm_fn_name, &.{llvm_operand_ty}); |
| 9147 | |
| 9148 | const params = [_]*llvm.Value{operand.toLlvm(&self.wip)}; |
| 9149 | const wrong_size_result = (try self.wip.unimplemented(llvm_operand_ty, "")).finish( |
| 9150 | self.builder.buildCall( |
| 9151 | llvm_fn_ty.toLlvm(&o.builder), |
| 9152 | fn_val, |
| 9153 | &params, |
| 9154 | params.len, |
| 9155 | .C, |
| 9156 | .Auto, |
| 9157 | "", |
| 9158 | ), |
| 9159 | &self.wip, |
| 9160 | ); |
| 9133 | 9161 | const result_ty = self.typeOfIndex(inst); |
| 9134 | | const result_llvm_ty = (try o.lowerType(result_ty)).toLlvm(&o.builder); |
| 9135 | | |
| 9136 | | const bits = operand_ty.intInfo(mod).bits; |
| 9137 | | const result_bits = result_ty.intInfo(mod).bits; |
| 9138 | | if (bits > result_bits) { |
| 9139 | | return self.builder.buildTrunc(wrong_size_result, result_llvm_ty, ""); |
| 9140 | | } else if (bits < result_bits) { |
| 9141 | | return self.builder.buildZExt(wrong_size_result, result_llvm_ty, ""); |
| 9142 | | } else { |
| 9143 | | return wrong_size_result; |
| 9144 | | } |
| 9162 | return self.wip.conv(.unsigned, wrong_size_result, try o.lowerType(result_ty), ""); |
| 9145 | 9163 | } |
| 9146 | 9164 | |
| 9147 | | fn airByteSwap(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*llvm.Value { |
| 9165 | fn airByteSwap(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !Builder.Value { |
| 9148 | 9166 | const o = self.dg.object; |
| 9149 | 9167 | const mod = o.module; |
| 9150 | 9168 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| ... | ... | @@ -9153,7 +9171,7 @@ pub const FuncGen = struct { |
| 9153 | 9171 | assert(bits % 8 == 0); |
| 9154 | 9172 | |
| 9155 | 9173 | var operand = try self.resolveInst(ty_op.operand); |
| 9156 | | var operand_llvm_ty = try o.lowerType(operand_ty); |
| 9174 | var llvm_operand_ty = try o.lowerType(operand_ty); |
| 9157 | 9175 | |
| 9158 | 9176 | if (bits % 16 == 8) { |
| 9159 | 9177 | // If not an even byte-multiple, we need zero-extend + shift-left 1 byte |
| ... | ... | @@ -9161,35 +9179,39 @@ pub const FuncGen = struct { |
| 9161 | 9179 | const scalar_ty = try o.builder.intType(@intCast(bits + 8)); |
| 9162 | 9180 | if (operand_ty.zigTypeTag(mod) == .Vector) { |
| 9163 | 9181 | const vec_len = operand_ty.vectorLen(mod); |
| 9164 | | operand_llvm_ty = try o.builder.vectorType(.normal, vec_len, scalar_ty); |
| 9165 | | } else operand_llvm_ty = scalar_ty; |
| 9182 | llvm_operand_ty = try o.builder.vectorType(.normal, vec_len, scalar_ty); |
| 9183 | } else llvm_operand_ty = scalar_ty; |
| 9166 | 9184 | |
| 9167 | 9185 | const shift_amt = |
| 9168 | | try o.builder.splatConst(operand_llvm_ty, try o.builder.intConst(scalar_ty, 8)); |
| 9169 | | const extended = self.builder.buildZExt(operand, operand_llvm_ty.toLlvm(&o.builder), ""); |
| 9170 | | operand = self.builder.buildShl(extended, shift_amt.toLlvm(&o.builder), ""); |
| 9186 | try o.builder.splatValue(llvm_operand_ty, try o.builder.intConst(scalar_ty, 8)); |
| 9187 | const extended = try self.wip.cast(.zext, operand, llvm_operand_ty, ""); |
| 9188 | operand = try self.wip.bin(.shl, extended, shift_amt, ""); |
| 9171 | 9189 | |
| 9172 | 9190 | bits = bits + 8; |
| 9173 | 9191 | } |
| 9174 | 9192 | |
| 9175 | | const params = [_]*llvm.Value{operand}; |
| 9176 | | const fn_val = try self.getIntrinsic(llvm_fn_name, &.{operand_llvm_ty}); |
| 9177 | | |
| 9178 | | const wrong_size_result = self.builder.buildCall(fn_val.globalGetValueType(), fn_val, &params, params.len, .C, .Auto, ""); |
| 9193 | const llvm_fn_ty = try o.builder.fnType(llvm_operand_ty, &.{llvm_operand_ty}, .normal); |
| 9194 | const fn_val = try self.getIntrinsic(llvm_fn_name, &.{llvm_operand_ty}); |
| 9195 | |
| 9196 | const params = [_]*llvm.Value{operand.toLlvm(&self.wip)}; |
| 9197 | const wrong_size_result = (try self.wip.unimplemented(llvm_operand_ty, "")).finish( |
| 9198 | self.builder.buildCall( |
| 9199 | llvm_fn_ty.toLlvm(&o.builder), |
| 9200 | fn_val, |
| 9201 | &params, |
| 9202 | params.len, |
| 9203 | .C, |
| 9204 | .Auto, |
| 9205 | "", |
| 9206 | ), |
| 9207 | &self.wip, |
| 9208 | ); |
| 9179 | 9209 | |
| 9180 | 9210 | const result_ty = self.typeOfIndex(inst); |
| 9181 | | const result_llvm_ty = (try o.lowerType(result_ty)).toLlvm(&o.builder); |
| 9182 | | const result_bits = result_ty.intInfo(mod).bits; |
| 9183 | | if (bits > result_bits) { |
| 9184 | | return self.builder.buildTrunc(wrong_size_result, result_llvm_ty, ""); |
| 9185 | | } else if (bits < result_bits) { |
| 9186 | | return self.builder.buildZExt(wrong_size_result, result_llvm_ty, ""); |
| 9187 | | } else { |
| 9188 | | return wrong_size_result; |
| 9189 | | } |
| 9211 | return self.wip.conv(.unsigned, wrong_size_result, try o.lowerType(result_ty), ""); |
| 9190 | 9212 | } |
| 9191 | 9213 | |
| 9192 | | fn airErrorSetHasValue(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 9214 | fn airErrorSetHasValue(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 9193 | 9215 | const o = self.dg.object; |
| 9194 | 9216 | const mod = o.module; |
| 9195 | 9217 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| ... | ... | @@ -9197,58 +9219,60 @@ pub const FuncGen = struct { |
| 9197 | 9219 | const error_set_ty = self.air.getRefType(ty_op.ty); |
| 9198 | 9220 | |
| 9199 | 9221 | const names = error_set_ty.errorSetNames(mod); |
| 9200 | | const valid_block = try self.wip.block("Valid"); |
| 9201 | | const invalid_block = try self.wip.block("Invalid"); |
| 9202 | | const end_block = try self.wip.block("End"); |
| 9203 | | const switch_instr = self.builder.buildSwitch(operand, invalid_block.toLlvm(&self.wip), @intCast(names.len)); |
| 9222 | const valid_block = try self.wip.block(@intCast(names.len), "Valid"); |
| 9223 | const invalid_block = try self.wip.block(1, "Invalid"); |
| 9224 | const end_block = try self.wip.block(2, "End"); |
| 9225 | var wip_switch = try self.wip.@"switch"(operand, invalid_block, @intCast(names.len)); |
| 9226 | defer wip_switch.finish(&self.wip); |
| 9204 | 9227 | |
| 9205 | 9228 | for (names) |name| { |
| 9206 | 9229 | const err_int = mod.global_error_set.getIndex(name).?; |
| 9207 | | const this_tag_int_value = |
| 9208 | | try o.lowerValue((try mod.intValue(Type.err_int, err_int)).toIntern()); |
| 9209 | | switch_instr.addCase(this_tag_int_value.toLlvm(&o.builder), valid_block.toLlvm(&self.wip)); |
| 9230 | const this_tag_int_value = try o.builder.intConst(Builder.Type.err_int, err_int); |
| 9231 | try wip_switch.addCase(this_tag_int_value, valid_block, &self.wip); |
| 9210 | 9232 | } |
| 9211 | 9233 | self.wip.cursor = .{ .block = valid_block }; |
| 9212 | | self.builder.positionBuilderAtEnd(valid_block.toLlvm(&self.wip)); |
| 9213 | | _ = self.builder.buildBr(end_block.toLlvm(&self.wip)); |
| 9234 | _ = try self.wip.br(end_block); |
| 9214 | 9235 | |
| 9215 | 9236 | self.wip.cursor = .{ .block = invalid_block }; |
| 9216 | | self.builder.positionBuilderAtEnd(invalid_block.toLlvm(&self.wip)); |
| 9217 | | _ = self.builder.buildBr(end_block.toLlvm(&self.wip)); |
| 9237 | _ = try self.wip.br(end_block); |
| 9218 | 9238 | |
| 9219 | 9239 | self.wip.cursor = .{ .block = end_block }; |
| 9220 | | self.builder.positionBuilderAtEnd(end_block.toLlvm(&self.wip)); |
| 9221 | | |
| 9222 | | const incoming_values: [2]*llvm.Value = .{ |
| 9223 | | Builder.Constant.true.toLlvm(&o.builder), |
| 9224 | | Builder.Constant.false.toLlvm(&o.builder), |
| 9225 | | }; |
| 9226 | | const incoming_blocks: [2]*llvm.BasicBlock = .{ |
| 9227 | | valid_block.toLlvm(&self.wip), invalid_block.toLlvm(&self.wip), |
| 9228 | | }; |
| 9229 | | const phi_node = self.builder.buildPhi(Builder.Type.i1.toLlvm(&o.builder), ""); |
| 9230 | | phi_node.addIncoming(&incoming_values, &incoming_blocks, 2); |
| 9231 | | return phi_node; |
| 9240 | const phi = try self.wip.phi(.i1, ""); |
| 9241 | try phi.finish( |
| 9242 | &.{ Builder.Constant.true.toValue(), Builder.Constant.false.toValue() }, |
| 9243 | &.{ valid_block, invalid_block }, |
| 9244 | &self.wip, |
| 9245 | ); |
| 9246 | return phi.toValue(); |
| 9232 | 9247 | } |
| 9233 | 9248 | |
| 9234 | | fn airIsNamedEnumValue(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 9249 | fn airIsNamedEnumValue(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 9250 | const o = self.dg.object; |
| 9235 | 9251 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 9236 | 9252 | const operand = try self.resolveInst(un_op); |
| 9237 | 9253 | const enum_ty = self.typeOf(un_op); |
| 9238 | 9254 | |
| 9239 | 9255 | const llvm_fn = try self.getIsNamedEnumValueFunction(enum_ty); |
| 9240 | | const params = [_]*llvm.Value{operand}; |
| 9241 | | return self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &params, params.len, .Fast, .Auto, ""); |
| 9256 | const params = [_]*llvm.Value{operand.toLlvm(&self.wip)}; |
| 9257 | return (try self.wip.unimplemented(.i1, "")).finish(self.builder.buildCall( |
| 9258 | llvm_fn.typeOf(&o.builder).toLlvm(&o.builder), |
| 9259 | llvm_fn.toLlvm(&o.builder), |
| 9260 | &params, |
| 9261 | params.len, |
| 9262 | .Fast, |
| 9263 | .Auto, |
| 9264 | "", |
| 9265 | ), &self.wip); |
| 9242 | 9266 | } |
| 9243 | 9267 | |
| 9244 | | fn getIsNamedEnumValueFunction(self: *FuncGen, enum_ty: Type) !*llvm.Value { |
| 9268 | fn getIsNamedEnumValueFunction(self: *FuncGen, enum_ty: Type) !Builder.Function.Index { |
| 9245 | 9269 | const o = self.dg.object; |
| 9246 | 9270 | const mod = o.module; |
| 9247 | 9271 | const enum_type = mod.intern_pool.indexToKey(enum_ty.toIntern()).enum_type; |
| 9248 | 9272 | |
| 9249 | 9273 | // TODO: detect when the type changes and re-emit this function. |
| 9250 | 9274 | const gop = try o.named_enum_map.getOrPut(o.gpa, enum_type.decl); |
| 9251 | | if (gop.found_existing) return gop.value_ptr.toLlvm(&o.builder); |
| 9275 | if (gop.found_existing) return gop.value_ptr.*; |
| 9252 | 9276 | errdefer assert(o.named_enum_map.remove(enum_type.decl)); |
| 9253 | 9277 | |
| 9254 | 9278 | const fqn = try mod.declPtr(enum_type.decl).getFullyQualifiedName(mod); |
| ... | ... | @@ -9256,9 +9280,9 @@ pub const FuncGen = struct { |
| 9256 | 9280 | fqn.fmt(&mod.intern_pool), |
| 9257 | 9281 | }); |
| 9258 | 9282 | |
| 9259 | | const fn_type = try o.builder.fnType(.i1, &.{try o.lowerType( |
| 9260 | | enum_type.tag_ty.toType(), |
| 9261 | | )}, .normal); |
| 9283 | const fn_type = try o.builder.fnType(.i1, &.{ |
| 9284 | try o.lowerType(enum_type.tag_ty.toType()), |
| 9285 | }, .normal); |
| 9262 | 9286 | const fn_val = o.llvm_module.addFunction(llvm_fn_name.toSlice(&o.builder).?, fn_type.toLlvm(&o.builder)); |
| 9263 | 9287 | fn_val.setLinkage(.Internal); |
| 9264 | 9288 | fn_val.setFunctionCallConv(.Fast); |
| ... | ... | @@ -9277,63 +9301,63 @@ pub const FuncGen = struct { |
| 9277 | 9301 | try o.builder.functions.append(self.gpa, function); |
| 9278 | 9302 | gop.value_ptr.* = global.kind.function; |
| 9279 | 9303 | |
| 9280 | | const prev_block = self.builder.getInsertBlock(); |
| 9281 | | const prev_debug_location = self.builder.getCurrentDebugLocation2(); |
| 9282 | | defer { |
| 9283 | | self.builder.positionBuilderAtEnd(prev_block); |
| 9284 | | if (self.di_scope != null) { |
| 9285 | | self.builder.setCurrentDebugLocation2(prev_debug_location); |
| 9286 | | } |
| 9287 | | } |
| 9288 | | |
| 9289 | | var wip = Builder.WipFunction.init(&o.builder, global.kind.function); |
| 9304 | var wip = try Builder.WipFunction.init(&o.builder, global.kind.function); |
| 9290 | 9305 | defer wip.deinit(); |
| 9306 | wip.cursor = .{ .block = try wip.block(0, "Entry") }; |
| 9291 | 9307 | |
| 9292 | | const entry_block = try wip.block("Entry"); |
| 9293 | | wip.cursor = .{ .block = entry_block }; |
| 9294 | | self.builder.positionBuilderAtEnd(entry_block.toLlvm(&wip)); |
| 9295 | | self.builder.clearCurrentDebugLocation(); |
| 9296 | | |
| 9297 | | const named_block = try wip.block("Named"); |
| 9298 | | const unnamed_block = try wip.block("Unnamed"); |
| 9299 | | const tag_int_value = fn_val.getParam(0); |
| 9300 | | const switch_instr = self.builder.buildSwitch(tag_int_value, unnamed_block.toLlvm(&wip), @intCast(enum_type.names.len)); |
| 9308 | const named_block = try wip.block(@intCast(enum_type.names.len), "Named"); |
| 9309 | const unnamed_block = try wip.block(1, "Unnamed"); |
| 9310 | const tag_int_value = wip.arg(0); |
| 9311 | var wip_switch = try wip.@"switch"(tag_int_value, unnamed_block, @intCast(enum_type.names.len)); |
| 9312 | defer wip_switch.finish(&wip); |
| 9301 | 9313 | |
| 9302 | 9314 | for (0..enum_type.names.len) |field_index| { |
| 9303 | | const this_tag_int_value = |
| 9304 | | try o.lowerValue((try mod.enumValueFieldIndex(enum_ty, @intCast(field_index))).toIntern()); |
| 9305 | | switch_instr.addCase(this_tag_int_value.toLlvm(&o.builder), named_block.toLlvm(&wip)); |
| 9315 | const this_tag_int_value = try o.lowerValue( |
| 9316 | (try mod.enumValueFieldIndex(enum_ty, @intCast(field_index))).toIntern(), |
| 9317 | ); |
| 9318 | try wip_switch.addCase(this_tag_int_value, named_block, &wip); |
| 9306 | 9319 | } |
| 9307 | 9320 | wip.cursor = .{ .block = named_block }; |
| 9308 | | self.builder.positionBuilderAtEnd(named_block.toLlvm(&wip)); |
| 9309 | | _ = self.builder.buildRet(Builder.Constant.true.toLlvm(&o.builder)); |
| 9321 | _ = try wip.ret(Builder.Constant.true.toValue()); |
| 9310 | 9322 | |
| 9311 | 9323 | wip.cursor = .{ .block = unnamed_block }; |
| 9312 | | self.builder.positionBuilderAtEnd(unnamed_block.toLlvm(&wip)); |
| 9313 | | _ = self.builder.buildRet(Builder.Constant.false.toLlvm(&o.builder)); |
| 9324 | _ = try wip.ret(Builder.Constant.false.toValue()); |
| 9314 | 9325 | |
| 9315 | 9326 | try wip.finish(); |
| 9316 | | return fn_val; |
| 9327 | return global.kind.function; |
| 9317 | 9328 | } |
| 9318 | 9329 | |
| 9319 | | fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 9330 | fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 9331 | const o = self.dg.object; |
| 9320 | 9332 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 9321 | 9333 | const operand = try self.resolveInst(un_op); |
| 9322 | 9334 | const enum_ty = self.typeOf(un_op); |
| 9323 | 9335 | |
| 9324 | 9336 | const llvm_fn = try self.getEnumTagNameFunction(enum_ty); |
| 9325 | | const params = [_]*llvm.Value{operand}; |
| 9326 | | return self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &params, params.len, .Fast, .Auto, ""); |
| 9337 | const llvm_fn_ty = llvm_fn.typeOf(&o.builder); |
| 9338 | const params = [_]*llvm.Value{operand.toLlvm(&self.wip)}; |
| 9339 | return (try self.wip.unimplemented(llvm_fn_ty.functionReturn(&o.builder), "")).finish( |
| 9340 | self.builder.buildCall( |
| 9341 | llvm_fn_ty.toLlvm(&o.builder), |
| 9342 | llvm_fn.toLlvm(&o.builder), |
| 9343 | &params, |
| 9344 | params.len, |
| 9345 | .Fast, |
| 9346 | .Auto, |
| 9347 | "", |
| 9348 | ), |
| 9349 | &self.wip, |
| 9350 | ); |
| 9327 | 9351 | } |
| 9328 | 9352 | |
| 9329 | | fn getEnumTagNameFunction(self: *FuncGen, enum_ty: Type) !*llvm.Value { |
| 9353 | fn getEnumTagNameFunction(self: *FuncGen, enum_ty: Type) !Builder.Function.Index { |
| 9330 | 9354 | const o = self.dg.object; |
| 9331 | 9355 | const mod = o.module; |
| 9332 | 9356 | const enum_type = mod.intern_pool.indexToKey(enum_ty.toIntern()).enum_type; |
| 9333 | 9357 | |
| 9334 | 9358 | // TODO: detect when the type changes and re-emit this function. |
| 9335 | 9359 | const gop = try o.decl_map.getOrPut(o.gpa, enum_type.decl); |
| 9336 | | if (gop.found_existing) return gop.value_ptr.toLlvm(&o.builder); |
| 9360 | if (gop.found_existing) return gop.value_ptr.ptrConst(&o.builder).kind.function; |
| 9337 | 9361 | errdefer assert(o.decl_map.remove(enum_type.decl)); |
| 9338 | 9362 | |
| 9339 | 9363 | const fqn = try mod.declPtr(enum_type.decl).getFullyQualifiedName(mod); |
| ... | ... | @@ -9362,26 +9386,15 @@ pub const FuncGen = struct { |
| 9362 | 9386 | gop.value_ptr.* = try o.builder.addGlobal(llvm_fn_name, global); |
| 9363 | 9387 | try o.builder.functions.append(self.gpa, function); |
| 9364 | 9388 | |
| 9365 | | const prev_block = self.builder.getInsertBlock(); |
| 9366 | | const prev_debug_location = self.builder.getCurrentDebugLocation2(); |
| 9367 | | defer { |
| 9368 | | self.builder.positionBuilderAtEnd(prev_block); |
| 9369 | | if (self.di_scope != null) { |
| 9370 | | self.builder.setCurrentDebugLocation2(prev_debug_location); |
| 9371 | | } |
| 9372 | | } |
| 9373 | | |
| 9374 | | var wip = Builder.WipFunction.init(&o.builder, global.kind.function); |
| 9389 | var wip = try Builder.WipFunction.init(&o.builder, global.kind.function); |
| 9375 | 9390 | defer wip.deinit(); |
| 9391 | wip.cursor = .{ .block = try wip.block(0, "Entry") }; |
| 9376 | 9392 | |
| 9377 | | const entry_block = try wip.block("Entry"); |
| 9378 | | wip.cursor = .{ .block = entry_block }; |
| 9379 | | self.builder.positionBuilderAtEnd(entry_block.toLlvm(&wip)); |
| 9380 | | self.builder.clearCurrentDebugLocation(); |
| 9381 | | |
| 9382 | | const bad_value_block = try wip.block("BadValue"); |
| 9383 | | const tag_int_value = fn_val.getParam(0); |
| 9384 | | const switch_instr = self.builder.buildSwitch(tag_int_value, bad_value_block.toLlvm(&wip), @intCast(enum_type.names.len)); |
| 9393 | const bad_value_block = try wip.block(1, "BadValue"); |
| 9394 | const tag_int_value = wip.arg(0); |
| 9395 | var wip_switch = |
| 9396 | try wip.@"switch"(tag_int_value, bad_value_block, @intCast(enum_type.names.len)); |
| 9397 | defer wip_switch.finish(&wip); |
| 9385 | 9398 | |
| 9386 | 9399 | for (enum_type.names, 0..) |name_ip, field_index| { |
| 9387 | 9400 | const name = try o.builder.string(mod.intern_pool.stringToSlice(name_ip)); |
| ... | ... | @@ -9398,46 +9411,45 @@ pub const FuncGen = struct { |
| 9398 | 9411 | .linkage = .private, |
| 9399 | 9412 | .unnamed_addr = .unnamed_addr, |
| 9400 | 9413 | .type = str_ty, |
| 9401 | | .alignment = comptime Builder.Alignment.fromByteUnits(1), |
| 9402 | 9414 | .kind = .{ .variable = @enumFromInt(o.builder.variables.items.len) }, |
| 9403 | 9415 | }; |
| 9404 | 9416 | var str_variable = Builder.Variable{ |
| 9405 | 9417 | .global = @enumFromInt(o.builder.globals.count()), |
| 9406 | 9418 | .mutability = .constant, |
| 9407 | 9419 | .init = str_init, |
| 9420 | .alignment = comptime Builder.Alignment.fromByteUnits(1), |
| 9408 | 9421 | }; |
| 9409 | 9422 | try o.builder.llvm.globals.append(o.gpa, str_llvm_global); |
| 9410 | 9423 | const global_index = try o.builder.addGlobal(.empty, str_global); |
| 9411 | 9424 | try o.builder.variables.append(o.gpa, str_variable); |
| 9412 | 9425 | |
| 9413 | | const slice_val = try o.builder.structConst(ret_ty, &.{ |
| 9426 | const slice_val = try o.builder.structValue(ret_ty, &.{ |
| 9414 | 9427 | global_index.toConst(), |
| 9415 | 9428 | try o.builder.intConst(usize_ty, name.toSlice(&o.builder).?.len), |
| 9416 | 9429 | }); |
| 9417 | 9430 | |
| 9418 | | const return_block = try wip.block("Name"); |
| 9419 | | const this_tag_int_value = |
| 9420 | | try o.lowerValue((try mod.enumValueFieldIndex(enum_ty, @intCast(field_index))).toIntern()); |
| 9421 | | switch_instr.addCase(this_tag_int_value.toLlvm(&o.builder), return_block.toLlvm(&wip)); |
| 9431 | const return_block = try wip.block(1, "Name"); |
| 9432 | const this_tag_int_value = try o.lowerValue( |
| 9433 | (try mod.enumValueFieldIndex(enum_ty, @intCast(field_index))).toIntern(), |
| 9434 | ); |
| 9435 | try wip_switch.addCase(this_tag_int_value, return_block, &wip); |
| 9422 | 9436 | |
| 9423 | 9437 | wip.cursor = .{ .block = return_block }; |
| 9424 | | self.builder.positionBuilderAtEnd(return_block.toLlvm(&wip)); |
| 9425 | | _ = self.builder.buildRet(slice_val.toLlvm(&o.builder)); |
| 9438 | _ = try wip.ret(slice_val); |
| 9426 | 9439 | } |
| 9427 | 9440 | |
| 9428 | 9441 | wip.cursor = .{ .block = bad_value_block }; |
| 9429 | | self.builder.positionBuilderAtEnd(bad_value_block.toLlvm(&wip)); |
| 9430 | | _ = self.builder.buildUnreachable(); |
| 9442 | _ = try wip.@"unreachable"(); |
| 9431 | 9443 | |
| 9432 | 9444 | try wip.finish(); |
| 9433 | | return fn_val; |
| 9445 | return global.kind.function; |
| 9434 | 9446 | } |
| 9435 | 9447 | |
| 9436 | | fn getCmpLtErrorsLenFunction(self: *FuncGen) !*llvm.Value { |
| 9448 | fn getCmpLtErrorsLenFunction(self: *FuncGen) !Builder.Function.Index { |
| 9437 | 9449 | const o = self.dg.object; |
| 9438 | 9450 | |
| 9439 | 9451 | const name = try o.builder.string(lt_errors_fn_name); |
| 9440 | | if (o.builder.getGlobal(name)) |llvm_fn| return llvm_fn.toLlvm(&o.builder); |
| 9452 | if (o.builder.getGlobal(name)) |llvm_fn| return llvm_fn.ptrConst(&o.builder).kind.function; |
| 9441 | 9453 | |
| 9442 | 9454 | // Function signature: fn (anyerror) bool |
| 9443 | 9455 | |
| ... | ... | @@ -9458,47 +9470,45 @@ pub const FuncGen = struct { |
| 9458 | 9470 | }; |
| 9459 | 9471 | |
| 9460 | 9472 | try o.builder.llvm.globals.append(self.gpa, llvm_fn); |
| 9461 | | const global_index = try o.builder.addGlobal(name, global); |
| 9473 | _ = try o.builder.addGlobal(name, global); |
| 9462 | 9474 | try o.builder.functions.append(self.gpa, function); |
| 9463 | | return global_index.toLlvm(&o.builder); |
| 9475 | return global.kind.function; |
| 9464 | 9476 | } |
| 9465 | 9477 | |
| 9466 | | fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 9478 | fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 9467 | 9479 | const o = self.dg.object; |
| 9468 | 9480 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 9469 | 9481 | const operand = try self.resolveInst(un_op); |
| 9470 | 9482 | const slice_ty = self.typeOfIndex(inst); |
| 9471 | | const slice_llvm_ty = (try o.lowerType(slice_ty)).toLlvm(&o.builder); |
| 9483 | const slice_llvm_ty = try o.lowerType(slice_ty); |
| 9472 | 9484 | |
| 9473 | 9485 | const error_name_table_ptr = try self.getErrorNameTable(); |
| 9474 | | const ptr_slice_llvm_ty = self.context.pointerType(0); |
| 9475 | | const error_name_table = self.builder.buildLoad(ptr_slice_llvm_ty, error_name_table_ptr.toLlvm(&o.builder), ""); |
| 9476 | | const indices = [_]*llvm.Value{operand}; |
| 9477 | | const error_name_ptr = self.builder.buildInBoundsGEP(slice_llvm_ty, error_name_table, &indices, indices.len, ""); |
| 9478 | | return self.builder.buildLoad(slice_llvm_ty, error_name_ptr, ""); |
| 9486 | const error_name_table = |
| 9487 | try self.wip.load(.normal, .ptr, error_name_table_ptr.toValue(&o.builder), .default, ""); |
| 9488 | const error_name_ptr = |
| 9489 | try self.wip.gep(.inbounds, slice_llvm_ty, error_name_table, &.{operand}, ""); |
| 9490 | return self.wip.load(.normal, slice_llvm_ty, error_name_ptr, .default, ""); |
| 9479 | 9491 | } |
| 9480 | 9492 | |
| 9481 | | fn airSplat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 9493 | fn airSplat(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 9482 | 9494 | const o = self.dg.object; |
| 9483 | | const mod = o.module; |
| 9484 | 9495 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 9485 | 9496 | const scalar = try self.resolveInst(ty_op.operand); |
| 9486 | 9497 | const vector_ty = self.typeOfIndex(inst); |
| 9487 | | const len = vector_ty.vectorLen(mod); |
| 9488 | | return self.builder.buildVectorSplat(len, scalar, ""); |
| 9498 | return self.wip.splatVector(try o.lowerType(vector_ty), scalar, ""); |
| 9489 | 9499 | } |
| 9490 | 9500 | |
| 9491 | | fn airSelect(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 9501 | fn airSelect(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 9492 | 9502 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 9493 | 9503 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; |
| 9494 | 9504 | const pred = try self.resolveInst(pl_op.operand); |
| 9495 | 9505 | const a = try self.resolveInst(extra.lhs); |
| 9496 | 9506 | const b = try self.resolveInst(extra.rhs); |
| 9497 | 9507 | |
| 9498 | | return self.builder.buildSelect(pred, a, b, ""); |
| 9508 | return self.wip.select(pred, a, b, ""); |
| 9499 | 9509 | } |
| 9500 | 9510 | |
| 9501 | | fn airShuffle(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 9511 | fn airShuffle(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 9502 | 9512 | const o = self.dg.object; |
| 9503 | 9513 | const mod = o.module; |
| 9504 | 9514 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| ... | ... | @@ -9528,11 +9538,11 @@ pub const FuncGen = struct { |
| 9528 | 9538 | } |
| 9529 | 9539 | } |
| 9530 | 9540 | |
| 9531 | | const llvm_mask_value = try o.builder.vectorConst( |
| 9541 | const llvm_mask_value = try o.builder.vectorValue( |
| 9532 | 9542 | try o.builder.vectorType(.normal, mask_len, .i32), |
| 9533 | 9543 | values, |
| 9534 | 9544 | ); |
| 9535 | | return self.builder.buildShuffleVector(a, b, llvm_mask_value.toLlvm(&o.builder), ""); |
| 9545 | return self.wip.shuffleVector(a, b, llvm_mask_value, ""); |
| 9536 | 9546 | } |
| 9537 | 9547 | |
| 9538 | 9548 | /// Reduce a vector by repeatedly applying `llvm_fn` to produce an accumulated result. |
| ... | ... | @@ -9549,61 +9559,69 @@ pub const FuncGen = struct { |
| 9549 | 9559 | /// |
| 9550 | 9560 | fn buildReducedCall( |
| 9551 | 9561 | self: *FuncGen, |
| 9552 | | llvm_fn: *llvm.Value, |
| 9553 | | operand_vector: *llvm.Value, |
| 9562 | llvm_fn: Builder.Function.Index, |
| 9563 | operand_vector: Builder.Value, |
| 9554 | 9564 | vector_len: usize, |
| 9555 | | accum_init: *llvm.Value, |
| 9556 | | ) !*llvm.Value { |
| 9565 | accum_init: Builder.Value, |
| 9566 | ) !Builder.Value { |
| 9557 | 9567 | const o = self.dg.object; |
| 9558 | 9568 | const usize_ty = try o.lowerType(Type.usize); |
| 9559 | | const llvm_vector_len = try o.builder.intConst(usize_ty, vector_len); |
| 9560 | | const llvm_result_ty = accum_init.typeOf(); |
| 9569 | const llvm_vector_len = try o.builder.intValue(usize_ty, vector_len); |
| 9570 | const llvm_result_ty = accum_init.typeOfWip(&self.wip); |
| 9561 | 9571 | |
| 9562 | 9572 | // Allocate and initialize our mutable variables |
| 9563 | | const i_ptr = try self.buildAlloca(usize_ty.toLlvm(&o.builder), null); |
| 9564 | | _ = self.builder.buildStore((try o.builder.intConst(usize_ty, 0)).toLlvm(&o.builder), i_ptr); |
| 9565 | | const accum_ptr = try self.buildAlloca(llvm_result_ty, null); |
| 9566 | | _ = self.builder.buildStore(accum_init, accum_ptr); |
| 9573 | const i_ptr = try self.buildAlloca(usize_ty, .default); |
| 9574 | _ = try self.wip.store(.normal, try o.builder.intValue(usize_ty, 0), i_ptr, .default); |
| 9575 | const accum_ptr = try self.buildAlloca(llvm_result_ty, .default); |
| 9576 | _ = try self.wip.store(.normal, accum_init, accum_ptr, .default); |
| 9567 | 9577 | |
| 9568 | 9578 | // Setup the loop |
| 9569 | | const loop = try self.wip.block("ReduceLoop"); |
| 9570 | | const loop_exit = try self.wip.block("AfterReduce"); |
| 9571 | | _ = self.builder.buildBr(loop.toLlvm(&self.wip)); |
| 9579 | const loop = try self.wip.block(2, "ReduceLoop"); |
| 9580 | const loop_exit = try self.wip.block(1, "AfterReduce"); |
| 9581 | _ = try self.wip.br(loop); |
| 9572 | 9582 | { |
| 9573 | 9583 | self.wip.cursor = .{ .block = loop }; |
| 9574 | | self.builder.positionBuilderAtEnd(loop.toLlvm(&self.wip)); |
| 9575 | 9584 | |
| 9576 | 9585 | // while (i < vec.len) |
| 9577 | | const i = self.builder.buildLoad(usize_ty.toLlvm(&o.builder), i_ptr, ""); |
| 9578 | | const cond = self.builder.buildICmp(.ULT, i, llvm_vector_len.toLlvm(&o.builder), ""); |
| 9579 | | const loop_then = try self.wip.block("ReduceLoopThen"); |
| 9586 | const i = try self.wip.load(.normal, usize_ty, i_ptr, .default, ""); |
| 9587 | const cond = try self.wip.icmp(.ult, i, llvm_vector_len, ""); |
| 9588 | const loop_then = try self.wip.block(1, "ReduceLoopThen"); |
| 9580 | 9589 | |
| 9581 | | _ = self.builder.buildCondBr(cond, loop_then.toLlvm(&self.wip), loop_exit.toLlvm(&self.wip)); |
| 9590 | _ = try self.wip.brCond(cond, loop_then, loop_exit); |
| 9582 | 9591 | |
| 9583 | 9592 | { |
| 9584 | 9593 | self.wip.cursor = .{ .block = loop_then }; |
| 9585 | | self.builder.positionBuilderAtEnd(loop_then.toLlvm(&self.wip)); |
| 9586 | 9594 | |
| 9587 | 9595 | // accum = f(accum, vec[i]); |
| 9588 | | const accum = self.builder.buildLoad(llvm_result_ty, accum_ptr, ""); |
| 9589 | | const element = self.builder.buildExtractElement(operand_vector, i, ""); |
| 9590 | | const params = [2]*llvm.Value{ accum, element }; |
| 9591 | | const new_accum = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &params, params.len, .C, .Auto, ""); |
| 9592 | | _ = self.builder.buildStore(new_accum, accum_ptr); |
| 9596 | const accum = try self.wip.load(.normal, llvm_result_ty, accum_ptr, .default, ""); |
| 9597 | const element = try self.wip.extractElement(operand_vector, i, ""); |
| 9598 | const params = [2]*llvm.Value{ accum.toLlvm(&self.wip), element.toLlvm(&self.wip) }; |
| 9599 | const new_accum = (try self.wip.unimplemented(llvm_result_ty, "")).finish( |
| 9600 | self.builder.buildCall( |
| 9601 | llvm_fn.typeOf(&o.builder).toLlvm(&o.builder), |
| 9602 | llvm_fn.toLlvm(&o.builder), |
| 9603 | &params, |
| 9604 | params.len, |
| 9605 | .C, |
| 9606 | .Auto, |
| 9607 | "", |
| 9608 | ), |
| 9609 | &self.wip, |
| 9610 | ); |
| 9611 | _ = try self.wip.store(.normal, new_accum, accum_ptr, .default); |
| 9593 | 9612 | |
| 9594 | 9613 | // i += 1 |
| 9595 | | const new_i = self.builder.buildAdd(i, (try o.builder.intConst(usize_ty, 1)).toLlvm(&o.builder), ""); |
| 9596 | | _ = self.builder.buildStore(new_i, i_ptr); |
| 9597 | | _ = self.builder.buildBr(loop.toLlvm(&self.wip)); |
| 9614 | const new_i = try self.wip.bin(.add, i, try o.builder.intValue(usize_ty, 1), ""); |
| 9615 | _ = try self.wip.store(.normal, new_i, i_ptr, .default); |
| 9616 | _ = try self.wip.br(loop); |
| 9598 | 9617 | } |
| 9599 | 9618 | } |
| 9600 | 9619 | |
| 9601 | 9620 | self.wip.cursor = .{ .block = loop_exit }; |
| 9602 | | self.builder.positionBuilderAtEnd(loop_exit.toLlvm(&self.wip)); |
| 9603 | | return self.builder.buildLoad(llvm_result_ty, accum_ptr, ""); |
| 9621 | return self.wip.load(.normal, llvm_result_ty, accum_ptr, .default, ""); |
| 9604 | 9622 | } |
| 9605 | 9623 | |
| 9606 | | fn airReduce(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value { |
| 9624 | fn airReduce(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !Builder.Value { |
| 9607 | 9625 | self.builder.setFastMath(want_fast_math); |
| 9608 | 9626 | const o = self.dg.object; |
| 9609 | 9627 | const mod = o.module; |
| ... | ... | @@ -9613,40 +9631,70 @@ pub const FuncGen = struct { |
| 9613 | 9631 | const operand = try self.resolveInst(reduce.operand); |
| 9614 | 9632 | const operand_ty = self.typeOf(reduce.operand); |
| 9615 | 9633 | const scalar_ty = self.typeOfIndex(inst); |
| 9634 | const llvm_scalar_ty = try o.lowerType(scalar_ty); |
| 9616 | 9635 | |
| 9617 | 9636 | switch (reduce.operation) { |
| 9618 | | .And => return self.builder.buildAndReduce(operand), |
| 9619 | | .Or => return self.builder.buildOrReduce(operand), |
| 9620 | | .Xor => return self.builder.buildXorReduce(operand), |
| 9637 | .And => return (try self.wip.unimplemented(llvm_scalar_ty, "")) |
| 9638 | .finish(self.builder.buildAndReduce(operand.toLlvm(&self.wip)), &self.wip), |
| 9639 | .Or => return (try self.wip.unimplemented(llvm_scalar_ty, "")) |
| 9640 | .finish(self.builder.buildOrReduce(operand.toLlvm(&self.wip)), &self.wip), |
| 9641 | .Xor => return (try self.wip.unimplemented(llvm_scalar_ty, "")) |
| 9642 | .finish(self.builder.buildXorReduce(operand.toLlvm(&self.wip)), &self.wip), |
| 9621 | 9643 | .Min => switch (scalar_ty.zigTypeTag(mod)) { |
| 9622 | | .Int => return self.builder.buildIntMinReduce(operand, scalar_ty.isSignedInt(mod)), |
| 9644 | .Int => return (try self.wip.unimplemented(llvm_scalar_ty, "")).finish( |
| 9645 | self.builder.buildIntMinReduce( |
| 9646 | operand.toLlvm(&self.wip), |
| 9647 | scalar_ty.isSignedInt(mod), |
| 9648 | ), |
| 9649 | &self.wip, |
| 9650 | ), |
| 9623 | 9651 | .Float => if (intrinsicsAllowed(scalar_ty, target)) { |
| 9624 | | return self.builder.buildFPMinReduce(operand); |
| 9652 | return (try self.wip.unimplemented(llvm_scalar_ty, "")) |
| 9653 | .finish(self.builder.buildFPMinReduce(operand.toLlvm(&self.wip)), &self.wip); |
| 9625 | 9654 | }, |
| 9626 | 9655 | else => unreachable, |
| 9627 | 9656 | }, |
| 9628 | 9657 | .Max => switch (scalar_ty.zigTypeTag(mod)) { |
| 9629 | | .Int => return self.builder.buildIntMaxReduce(operand, scalar_ty.isSignedInt(mod)), |
| 9658 | .Int => return (try self.wip.unimplemented(llvm_scalar_ty, "")).finish( |
| 9659 | self.builder.buildIntMaxReduce( |
| 9660 | operand.toLlvm(&self.wip), |
| 9661 | scalar_ty.isSignedInt(mod), |
| 9662 | ), |
| 9663 | &self.wip, |
| 9664 | ), |
| 9630 | 9665 | .Float => if (intrinsicsAllowed(scalar_ty, target)) { |
| 9631 | | return self.builder.buildFPMaxReduce(operand); |
| 9666 | return (try self.wip.unimplemented(llvm_scalar_ty, "")) |
| 9667 | .finish(self.builder.buildFPMaxReduce(operand.toLlvm(&self.wip)), &self.wip); |
| 9632 | 9668 | }, |
| 9633 | 9669 | else => unreachable, |
| 9634 | 9670 | }, |
| 9635 | 9671 | .Add => switch (scalar_ty.zigTypeTag(mod)) { |
| 9636 | | .Int => return self.builder.buildAddReduce(operand), |
| 9672 | .Int => return (try self.wip.unimplemented(llvm_scalar_ty, "")) |
| 9673 | .finish(self.builder.buildAddReduce(operand.toLlvm(&self.wip)), &self.wip), |
| 9637 | 9674 | .Float => if (intrinsicsAllowed(scalar_ty, target)) { |
| 9638 | | const scalar_llvm_ty = try o.lowerType(scalar_ty); |
| 9639 | | const neutral_value = try o.builder.fpConst(scalar_llvm_ty, -0.0); |
| 9640 | | return self.builder.buildFPAddReduce(neutral_value.toLlvm(&o.builder), operand); |
| 9675 | const neutral_value = try o.builder.fpConst(llvm_scalar_ty, -0.0); |
| 9676 | return (try self.wip.unimplemented(llvm_scalar_ty, "")).finish( |
| 9677 | self.builder.buildFPAddReduce( |
| 9678 | neutral_value.toLlvm(&o.builder), |
| 9679 | operand.toLlvm(&self.wip), |
| 9680 | ), |
| 9681 | &self.wip, |
| 9682 | ); |
| 9641 | 9683 | }, |
| 9642 | 9684 | else => unreachable, |
| 9643 | 9685 | }, |
| 9644 | 9686 | .Mul => switch (scalar_ty.zigTypeTag(mod)) { |
| 9645 | | .Int => return self.builder.buildMulReduce(operand), |
| 9687 | .Int => return (try self.wip.unimplemented(llvm_scalar_ty, "")) |
| 9688 | .finish(self.builder.buildMulReduce(operand.toLlvm(&self.wip)), &self.wip), |
| 9646 | 9689 | .Float => if (intrinsicsAllowed(scalar_ty, target)) { |
| 9647 | | const scalar_llvm_ty = try o.lowerType(scalar_ty); |
| 9648 | | const neutral_value = try o.builder.fpConst(scalar_llvm_ty, 1.0); |
| 9649 | | return self.builder.buildFPMulReduce(neutral_value.toLlvm(&o.builder), operand); |
| 9690 | const neutral_value = try o.builder.fpConst(llvm_scalar_ty, 1.0); |
| 9691 | return (try self.wip.unimplemented(llvm_scalar_ty, "")).finish( |
| 9692 | self.builder.buildFPMulReduce( |
| 9693 | neutral_value.toLlvm(&o.builder), |
| 9694 | operand.toLlvm(&self.wip), |
| 9695 | ), |
| 9696 | &self.wip, |
| 9697 | ); |
| 9650 | 9698 | }, |
| 9651 | 9699 | else => unreachable, |
| 9652 | 9700 | }, |
| ... | ... | @@ -9671,34 +9719,54 @@ pub const FuncGen = struct { |
| 9671 | 9719 | else => unreachable, |
| 9672 | 9720 | }; |
| 9673 | 9721 | |
| 9674 | | const param_llvm_ty = try o.lowerType(scalar_ty); |
| 9675 | | const libc_fn = try self.getLibcFunction(fn_name, &(.{param_llvm_ty} ** 2), param_llvm_ty); |
| 9676 | | const init_value = try o.lowerValue((try mod.floatValue(scalar_ty, switch (reduce.operation) { |
| 9677 | | .Min => std.math.nan(f32), |
| 9678 | | .Max => std.math.nan(f32), |
| 9679 | | .Add => -0.0, |
| 9680 | | .Mul => 1.0, |
| 9722 | const libc_fn = |
| 9723 | try self.getLibcFunction(fn_name, &.{ llvm_scalar_ty, llvm_scalar_ty }, llvm_scalar_ty); |
| 9724 | const init_val = switch (llvm_scalar_ty) { |
| 9725 | .i16 => try o.builder.intValue(.i16, @as(i16, @bitCast( |
| 9726 | @as(f16, switch (reduce.operation) { |
| 9727 | .Min, .Max => std.math.nan(f16), |
| 9728 | .Add => -0.0, |
| 9729 | .Mul => 1.0, |
| 9730 | else => unreachable, |
| 9731 | }), |
| 9732 | ))), |
| 9733 | .i80 => try o.builder.intValue(.i80, @as(i80, @bitCast( |
| 9734 | @as(f80, switch (reduce.operation) { |
| 9735 | .Min, .Max => std.math.nan(f80), |
| 9736 | .Add => -0.0, |
| 9737 | .Mul => 1.0, |
| 9738 | else => unreachable, |
| 9739 | }), |
| 9740 | ))), |
| 9741 | .i128 => try o.builder.intValue(.i128, @as(i128, @bitCast( |
| 9742 | @as(f128, switch (reduce.operation) { |
| 9743 | .Min, .Max => std.math.nan(f128), |
| 9744 | .Add => -0.0, |
| 9745 | .Mul => 1.0, |
| 9746 | else => unreachable, |
| 9747 | }), |
| 9748 | ))), |
| 9681 | 9749 | else => unreachable, |
| 9682 | | })).toIntern()); |
| 9683 | | return self.buildReducedCall(libc_fn, operand, operand_ty.vectorLen(mod), init_value.toLlvm(&o.builder)); |
| 9750 | }; |
| 9751 | return self.buildReducedCall(libc_fn, operand, operand_ty.vectorLen(mod), init_val); |
| 9684 | 9752 | } |
| 9685 | 9753 | |
| 9686 | | fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 9754 | fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 9687 | 9755 | const o = self.dg.object; |
| 9688 | 9756 | const mod = o.module; |
| 9689 | 9757 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 9690 | 9758 | const result_ty = self.typeOfIndex(inst); |
| 9691 | 9759 | const len: usize = @intCast(result_ty.arrayLen(mod)); |
| 9692 | 9760 | const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra[ty_pl.payload..][0..len]); |
| 9693 | | const llvm_result_ty = (try o.lowerType(result_ty)).toLlvm(&o.builder); |
| 9761 | const llvm_result_ty = try o.lowerType(result_ty); |
| 9694 | 9762 | |
| 9695 | 9763 | switch (result_ty.zigTypeTag(mod)) { |
| 9696 | 9764 | .Vector => { |
| 9697 | | var vector = llvm_result_ty.getUndef(); |
| 9765 | var vector = try o.builder.poisonValue(llvm_result_ty); |
| 9698 | 9766 | for (elements, 0..) |elem, i| { |
| 9699 | | const index_u32 = try o.builder.intConst(.i32, i); |
| 9767 | const index_u32 = try o.builder.intValue(.i32, i); |
| 9700 | 9768 | const llvm_elem = try self.resolveInst(elem); |
| 9701 | | vector = self.builder.buildInsertElement(vector, llvm_elem, index_u32.toLlvm(&o.builder), ""); |
| 9769 | vector = try self.wip.insertElement(vector, llvm_elem, index_u32, ""); |
| 9702 | 9770 | } |
| 9703 | 9771 | return vector; |
| 9704 | 9772 | }, |
| ... | ... | @@ -9710,7 +9778,7 @@ pub const FuncGen = struct { |
| 9710 | 9778 | const int_ty = try o.builder.intType(@intCast(big_bits)); |
| 9711 | 9779 | const fields = struct_obj.fields.values(); |
| 9712 | 9780 | comptime assert(Type.packed_struct_layout_version == 2); |
| 9713 | | var running_int = (try o.builder.intConst(int_ty, 0)).toLlvm(&o.builder); |
| 9781 | var running_int = try o.builder.intValue(int_ty, 0); |
| 9714 | 9782 | var running_bits: u16 = 0; |
| 9715 | 9783 | for (elements, 0..) |elem, i| { |
| 9716 | 9784 | const field = fields[i]; |
| ... | ... | @@ -9718,18 +9786,18 @@ pub const FuncGen = struct { |
| 9718 | 9786 | |
| 9719 | 9787 | const non_int_val = try self.resolveInst(elem); |
| 9720 | 9788 | const ty_bit_size: u16 = @intCast(field.ty.bitSize(mod)); |
| 9721 | | const small_int_ty = (try o.builder.intType(ty_bit_size)).toLlvm(&o.builder); |
| 9789 | const small_int_ty = try o.builder.intType(ty_bit_size); |
| 9722 | 9790 | const small_int_val = if (field.ty.isPtrAtRuntime(mod)) |
| 9723 | | self.builder.buildPtrToInt(non_int_val, small_int_ty, "") |
| 9791 | try self.wip.cast(.ptrtoint, non_int_val, small_int_ty, "") |
| 9724 | 9792 | else |
| 9725 | | self.builder.buildBitCast(non_int_val, small_int_ty, ""); |
| 9726 | | const shift_rhs = try o.builder.intConst(int_ty, running_bits); |
| 9793 | try self.wip.cast(.bitcast, non_int_val, small_int_ty, ""); |
| 9794 | const shift_rhs = try o.builder.intValue(int_ty, running_bits); |
| 9727 | 9795 | // If the field is as large as the entire packed struct, this |
| 9728 | 9796 | // zext would go from, e.g. i16 to i16. This is legal with |
| 9729 | 9797 | // constZExtOrBitCast but not legal with constZExt. |
| 9730 | | const extended_int_val = self.builder.buildZExtOrBitCast(small_int_val, int_ty.toLlvm(&o.builder), ""); |
| 9731 | | const shifted = self.builder.buildShl(extended_int_val, shift_rhs.toLlvm(&o.builder), ""); |
| 9732 | | running_int = self.builder.buildOr(running_int, shifted, ""); |
| 9798 | const extended_int_val = try self.wip.conv(.unsigned, small_int_val, int_ty, ""); |
| 9799 | const shifted = try self.wip.bin(.shl, extended_int_val, shift_rhs, ""); |
| 9800 | running_int = try self.wip.bin(.@"or", running_int, shifted, ""); |
| 9733 | 9801 | running_bits += ty_bit_size; |
| 9734 | 9802 | } |
| 9735 | 9803 | return running_int; |
| ... | ... | @@ -9738,19 +9806,16 @@ pub const FuncGen = struct { |
| 9738 | 9806 | if (isByRef(result_ty, mod)) { |
| 9739 | 9807 | // TODO in debug builds init to undef so that the padding will be 0xaa |
| 9740 | 9808 | // even if we fully populate the fields. |
| 9741 | | const alloca_inst = try self.buildAlloca(llvm_result_ty, result_ty.abiAlignment(mod)); |
| 9809 | const alignment = Builder.Alignment.fromByteUnits(result_ty.abiAlignment(mod)); |
| 9810 | const alloca_inst = try self.buildAlloca(llvm_result_ty, alignment); |
| 9742 | 9811 | |
| 9743 | | var indices: [2]*llvm.Value = .{ |
| 9744 | | (try o.builder.intConst(.i32, 0)).toLlvm(&o.builder), |
| 9745 | | undefined, |
| 9746 | | }; |
| 9747 | 9812 | for (elements, 0..) |elem, i| { |
| 9748 | 9813 | if ((try result_ty.structFieldValueComptime(mod, i)) != null) continue; |
| 9749 | 9814 | |
| 9750 | 9815 | const llvm_elem = try self.resolveInst(elem); |
| 9751 | 9816 | const llvm_i = llvmField(result_ty, i, mod).?.index; |
| 9752 | | indices[1] = (try o.builder.intConst(.i32, llvm_i)).toLlvm(&o.builder); |
| 9753 | | const field_ptr = self.builder.buildInBoundsGEP(llvm_result_ty, alloca_inst, &indices, indices.len, ""); |
| 9817 | const field_ptr = |
| 9818 | try self.wip.gepStruct(llvm_result_ty, alloca_inst, llvm_i, ""); |
| 9754 | 9819 | const field_ptr_ty = try mod.ptrType(.{ |
| 9755 | 9820 | .child = self.typeOf(elem).toIntern(), |
| 9756 | 9821 | .flags = .{ |
| ... | ... | @@ -9759,18 +9824,18 @@ pub const FuncGen = struct { |
| 9759 | 9824 | ), |
| 9760 | 9825 | }, |
| 9761 | 9826 | }); |
| 9762 | | try self.store(field_ptr, field_ptr_ty, llvm_elem, .NotAtomic); |
| 9827 | try self.store(field_ptr, field_ptr_ty, llvm_elem, .none); |
| 9763 | 9828 | } |
| 9764 | 9829 | |
| 9765 | 9830 | return alloca_inst; |
| 9766 | 9831 | } else { |
| 9767 | | var result = llvm_result_ty.getUndef(); |
| 9832 | var result = try o.builder.poisonValue(llvm_result_ty); |
| 9768 | 9833 | for (elements, 0..) |elem, i| { |
| 9769 | 9834 | if ((try result_ty.structFieldValueComptime(mod, i)) != null) continue; |
| 9770 | 9835 | |
| 9771 | 9836 | const llvm_elem = try self.resolveInst(elem); |
| 9772 | 9837 | const llvm_i = llvmField(result_ty, i, mod).?.index; |
| 9773 | | result = self.builder.buildInsertValue(result, llvm_elem, llvm_i, ""); |
| 9838 | result = try self.wip.insertValue(result, llvm_elem, &.{llvm_i}, ""); |
| 9774 | 9839 | } |
| 9775 | 9840 | return result; |
| 9776 | 9841 | } |
| ... | ... | @@ -9778,8 +9843,10 @@ pub const FuncGen = struct { |
| 9778 | 9843 | .Array => { |
| 9779 | 9844 | assert(isByRef(result_ty, mod)); |
| 9780 | 9845 | |
| 9781 | | const usize_ty = try o.lowerType(Type.usize); |
| 9782 | | const alloca_inst = try self.buildAlloca(llvm_result_ty, result_ty.abiAlignment(mod)); |
| 9846 | const llvm_usize = try o.lowerType(Type.usize); |
| 9847 | const usize_zero = try o.builder.intValue(llvm_usize, 0); |
| 9848 | const alignment = Builder.Alignment.fromByteUnits(result_ty.abiAlignment(mod)); |
| 9849 | const alloca_inst = try self.buildAlloca(llvm_result_ty, alignment); |
| 9783 | 9850 | |
| 9784 | 9851 | const array_info = result_ty.arrayInfo(mod); |
| 9785 | 9852 | const elem_ptr_ty = try mod.ptrType(.{ |
| ... | ... | @@ -9787,26 +9854,21 @@ pub const FuncGen = struct { |
| 9787 | 9854 | }); |
| 9788 | 9855 | |
| 9789 | 9856 | for (elements, 0..) |elem, i| { |
| 9790 | | const indices: [2]*llvm.Value = .{ |
| 9791 | | (try o.builder.intConst(usize_ty, 0)).toLlvm(&o.builder), |
| 9792 | | (try o.builder.intConst(usize_ty, i)).toLlvm(&o.builder), |
| 9793 | | }; |
| 9794 | | const elem_ptr = self.builder.buildInBoundsGEP(llvm_result_ty, alloca_inst, &indices, indices.len, ""); |
| 9857 | const elem_ptr = try self.wip.gep(.inbounds, llvm_result_ty, alloca_inst, &.{ |
| 9858 | usize_zero, try o.builder.intValue(llvm_usize, i), |
| 9859 | }, ""); |
| 9795 | 9860 | const llvm_elem = try self.resolveInst(elem); |
| 9796 | | try self.store(elem_ptr, elem_ptr_ty, llvm_elem, .NotAtomic); |
| 9861 | try self.store(elem_ptr, elem_ptr_ty, llvm_elem, .none); |
| 9797 | 9862 | } |
| 9798 | 9863 | if (array_info.sentinel) |sent_val| { |
| 9799 | | const indices: [2]*llvm.Value = .{ |
| 9800 | | (try o.builder.intConst(usize_ty, 0)).toLlvm(&o.builder), |
| 9801 | | (try o.builder.intConst(usize_ty, array_info.len)).toLlvm(&o.builder), |
| 9802 | | }; |
| 9803 | | const elem_ptr = self.builder.buildInBoundsGEP(llvm_result_ty, alloca_inst, &indices, indices.len, ""); |
| 9864 | const elem_ptr = try self.wip.gep(.inbounds, llvm_result_ty, alloca_inst, &.{ |
| 9865 | usize_zero, try o.builder.intValue(llvm_usize, array_info.len), |
| 9866 | }, ""); |
| 9804 | 9867 | const llvm_elem = try self.resolveValue(.{ |
| 9805 | 9868 | .ty = array_info.elem_type, |
| 9806 | 9869 | .val = sent_val, |
| 9807 | 9870 | }); |
| 9808 | | |
| 9809 | | try self.store(elem_ptr, elem_ptr_ty, llvm_elem.toLlvm(&o.builder), .NotAtomic); |
| 9871 | try self.store(elem_ptr, elem_ptr_ty, llvm_elem.toValue(), .none); |
| 9810 | 9872 | } |
| 9811 | 9873 | |
| 9812 | 9874 | return alloca_inst; |
| ... | ... | @@ -9815,7 +9877,7 @@ pub const FuncGen = struct { |
| 9815 | 9877 | } |
| 9816 | 9878 | } |
| 9817 | 9879 | |
| 9818 | | fn airUnionInit(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 9880 | fn airUnionInit(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 9819 | 9881 | const o = self.dg.object; |
| 9820 | 9882 | const mod = o.module; |
| 9821 | 9883 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| ... | ... | @@ -9827,15 +9889,15 @@ pub const FuncGen = struct { |
| 9827 | 9889 | |
| 9828 | 9890 | if (union_obj.layout == .Packed) { |
| 9829 | 9891 | const big_bits = union_ty.bitSize(mod); |
| 9830 | | const int_llvm_ty = (try o.builder.intType(@intCast(big_bits))).toLlvm(&o.builder); |
| 9892 | const int_llvm_ty = try o.builder.intType(@intCast(big_bits)); |
| 9831 | 9893 | const field = union_obj.fields.values()[extra.field_index]; |
| 9832 | 9894 | const non_int_val = try self.resolveInst(extra.init); |
| 9833 | | const small_int_ty = (try o.builder.intType(@intCast(field.ty.bitSize(mod)))).toLlvm(&o.builder); |
| 9895 | const small_int_ty = try o.builder.intType(@intCast(field.ty.bitSize(mod))); |
| 9834 | 9896 | const small_int_val = if (field.ty.isPtrAtRuntime(mod)) |
| 9835 | | self.builder.buildPtrToInt(non_int_val, small_int_ty, "") |
| 9897 | try self.wip.cast(.ptrtoint, non_int_val, small_int_ty, "") |
| 9836 | 9898 | else |
| 9837 | | self.builder.buildBitCast(non_int_val, small_int_ty, ""); |
| 9838 | | return self.builder.buildZExtOrBitCast(small_int_val, int_llvm_ty, ""); |
| 9899 | try self.wip.cast(.bitcast, non_int_val, small_int_ty, ""); |
| 9900 | return self.wip.conv(.unsigned, small_int_val, int_llvm_ty, ""); |
| 9839 | 9901 | } |
| 9840 | 9902 | |
| 9841 | 9903 | const tag_int = blk: { |
| ... | ... | @@ -9848,25 +9910,29 @@ pub const FuncGen = struct { |
| 9848 | 9910 | }; |
| 9849 | 9911 | if (layout.payload_size == 0) { |
| 9850 | 9912 | if (layout.tag_size == 0) { |
| 9851 | | return null; |
| 9913 | return .none; |
| 9852 | 9914 | } |
| 9853 | 9915 | assert(!isByRef(union_ty, mod)); |
| 9854 | | return (try o.builder.intConst(union_llvm_ty, tag_int)).toLlvm(&o.builder); |
| 9916 | return o.builder.intValue(union_llvm_ty, tag_int); |
| 9855 | 9917 | } |
| 9856 | 9918 | assert(isByRef(union_ty, mod)); |
| 9857 | 9919 | // The llvm type of the alloca will be the named LLVM union type, and will not |
| 9858 | 9920 | // necessarily match the format that we need, depending on which tag is active. |
| 9859 | 9921 | // We must construct the correct unnamed struct type here, in order to then set |
| 9860 | 9922 | // the fields appropriately. |
| 9861 | | const result_ptr = try self.buildAlloca(union_llvm_ty.toLlvm(&o.builder), layout.abi_align); |
| 9923 | const alignment = Builder.Alignment.fromByteUnits(layout.abi_align); |
| 9924 | const result_ptr = try self.buildAlloca(union_llvm_ty, alignment); |
| 9862 | 9925 | const llvm_payload = try self.resolveInst(extra.init); |
| 9863 | 9926 | assert(union_obj.haveFieldTypes()); |
| 9864 | 9927 | const field = union_obj.fields.values()[extra.field_index]; |
| 9865 | 9928 | const field_llvm_ty = try o.lowerType(field.ty); |
| 9866 | 9929 | const field_size = field.ty.abiSize(mod); |
| 9867 | 9930 | const field_align = field.normalAlignment(mod); |
| 9931 | const llvm_usize = try o.lowerType(Type.usize); |
| 9932 | const usize_zero = try o.builder.intValue(llvm_usize, 0); |
| 9933 | const i32_zero = try o.builder.intValue(.i32, 0); |
| 9868 | 9934 | |
| 9869 | | const llvm_union_ty = (t: { |
| 9935 | const llvm_union_ty = t: { |
| 9870 | 9936 | const payload_ty = p: { |
| 9871 | 9937 | if (!field.ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 9872 | 9938 | const padding_len = layout.payload_size; |
| ... | ... | @@ -9894,52 +9960,46 @@ pub const FuncGen = struct { |
| 9894 | 9960 | fields_len += 1; |
| 9895 | 9961 | } |
| 9896 | 9962 | break :t try o.builder.structType(.normal, fields[0..fields_len]); |
| 9897 | | }).toLlvm(&o.builder); |
| 9963 | }; |
| 9898 | 9964 | |
| 9899 | 9965 | // Now we follow the layout as expressed above with GEP instructions to set the |
| 9900 | 9966 | // tag and the payload. |
| 9901 | 9967 | const field_ptr_ty = try mod.ptrType(.{ |
| 9902 | 9968 | .child = field.ty.toIntern(), |
| 9903 | | .flags = .{ |
| 9904 | | .alignment = InternPool.Alignment.fromNonzeroByteUnits(field_align), |
| 9905 | | }, |
| 9969 | .flags = .{ .alignment = InternPool.Alignment.fromNonzeroByteUnits(field_align) }, |
| 9906 | 9970 | }); |
| 9907 | 9971 | if (layout.tag_size == 0) { |
| 9908 | | const indices: [3]*llvm.Value = .{ |
| 9909 | | (try o.builder.intConst(.i32, 0)).toLlvm(&o.builder), |
| 9910 | | } ** 3; |
| 9911 | | const len: c_uint = if (field_size == layout.payload_size) 2 else 3; |
| 9912 | | const field_ptr = self.builder.buildInBoundsGEP(llvm_union_ty, result_ptr, &indices, len, ""); |
| 9913 | | try self.store(field_ptr, field_ptr_ty, llvm_payload, .NotAtomic); |
| 9972 | const indices = [3]Builder.Value{ usize_zero, i32_zero, i32_zero }; |
| 9973 | const len: usize = if (field_size == layout.payload_size) 2 else 3; |
| 9974 | const field_ptr = |
| 9975 | try self.wip.gep(.inbounds, llvm_union_ty, result_ptr, indices[0..len], ""); |
| 9976 | try self.store(field_ptr, field_ptr_ty, llvm_payload, .none); |
| 9914 | 9977 | return result_ptr; |
| 9915 | 9978 | } |
| 9916 | 9979 | |
| 9917 | 9980 | { |
| 9918 | | const indices: [3]*llvm.Value = .{ |
| 9919 | | (try o.builder.intConst(.i32, 0)).toLlvm(&o.builder), |
| 9920 | | (try o.builder.intConst(.i32, @intFromBool(layout.tag_align >= layout.payload_align))).toLlvm(&o.builder), |
| 9921 | | (try o.builder.intConst(.i32, 0)).toLlvm(&o.builder), |
| 9922 | | }; |
| 9923 | | const len: c_uint = if (field_size == layout.payload_size) 2 else 3; |
| 9924 | | const field_ptr = self.builder.buildInBoundsGEP(llvm_union_ty, result_ptr, &indices, len, ""); |
| 9925 | | try self.store(field_ptr, field_ptr_ty, llvm_payload, .NotAtomic); |
| 9981 | const payload_index = @intFromBool(layout.tag_align >= layout.payload_align); |
| 9982 | const indices: [3]Builder.Value = |
| 9983 | .{ usize_zero, try o.builder.intValue(.i32, payload_index), i32_zero }; |
| 9984 | const len: usize = if (field_size == layout.payload_size) 2 else 3; |
| 9985 | const field_ptr = |
| 9986 | try self.wip.gep(.inbounds, llvm_union_ty, result_ptr, indices[0..len], ""); |
| 9987 | try self.store(field_ptr, field_ptr_ty, llvm_payload, .none); |
| 9926 | 9988 | } |
| 9927 | 9989 | { |
| 9928 | | const indices: [2]*llvm.Value = .{ |
| 9929 | | (try o.builder.intConst(.i32, 0)).toLlvm(&o.builder), |
| 9930 | | (try o.builder.intConst(.i32, @intFromBool(layout.tag_align < layout.payload_align))).toLlvm(&o.builder), |
| 9931 | | }; |
| 9932 | | const field_ptr = self.builder.buildInBoundsGEP(llvm_union_ty, result_ptr, &indices, indices.len, ""); |
| 9990 | const tag_index = @intFromBool(layout.tag_align < layout.payload_align); |
| 9991 | const indices: [2]Builder.Value = .{ usize_zero, try o.builder.intValue(.i32, tag_index) }; |
| 9992 | const field_ptr = try self.wip.gep(.inbounds, llvm_union_ty, result_ptr, &indices, ""); |
| 9933 | 9993 | const tag_ty = try o.lowerType(union_obj.tag_ty); |
| 9934 | | const llvm_tag = try o.builder.intConst(tag_ty, tag_int); |
| 9935 | | const store_inst = self.builder.buildStore(llvm_tag.toLlvm(&o.builder), field_ptr); |
| 9936 | | store_inst.setAlignment(union_obj.tag_ty.abiAlignment(mod)); |
| 9994 | const llvm_tag = try o.builder.intValue(tag_ty, tag_int); |
| 9995 | const tag_alignment = Builder.Alignment.fromByteUnits(union_obj.tag_ty.abiAlignment(mod)); |
| 9996 | _ = try self.wip.store(.normal, llvm_tag, field_ptr, tag_alignment); |
| 9937 | 9997 | } |
| 9938 | 9998 | |
| 9939 | 9999 | return result_ptr; |
| 9940 | 10000 | } |
| 9941 | 10001 | |
| 9942 | | fn airPrefetch(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 10002 | fn airPrefetch(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 9943 | 10003 | const o = self.dg.object; |
| 9944 | 10004 | const prefetch = self.air.instructions.items(.data)[inst].prefetch; |
| 9945 | 10005 | |
| ... | ... | @@ -9968,10 +10028,10 @@ pub const FuncGen = struct { |
| 9968 | 10028 | .powerpcle, |
| 9969 | 10029 | .powerpc64, |
| 9970 | 10030 | .powerpc64le, |
| 9971 | | => return null, |
| 10031 | => return .none, |
| 9972 | 10032 | .arm, .armeb, .thumb, .thumbeb => { |
| 9973 | 10033 | switch (prefetch.rw) { |
| 9974 | | .write => return null, |
| 10034 | .write => return .none, |
| 9975 | 10035 | else => {}, |
| 9976 | 10036 | } |
| 9977 | 10037 | }, |
| ... | ... | @@ -9981,48 +10041,63 @@ pub const FuncGen = struct { |
| 9981 | 10041 | } |
| 9982 | 10042 | |
| 9983 | 10043 | const llvm_fn_name = "llvm.prefetch.p0"; |
| 9984 | | const fn_val = o.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: { |
| 9985 | | // declare void @llvm.prefetch(i8*, i32, i32, i32) |
| 9986 | | const fn_type = try o.builder.fnType(.void, &.{ .ptr, .i32, .i32, .i32 }, .normal); |
| 9987 | | break :blk o.llvm_module.addFunction(llvm_fn_name, fn_type.toLlvm(&o.builder)); |
| 9988 | | }; |
| 10044 | // declare void @llvm.prefetch(i8*, i32, i32, i32) |
| 10045 | const llvm_fn_ty = try o.builder.fnType(.void, &.{ .ptr, .i32, .i32, .i32 }, .normal); |
| 10046 | const fn_val = o.llvm_module.getNamedFunction(llvm_fn_name) orelse |
| 10047 | o.llvm_module.addFunction(llvm_fn_name, llvm_fn_ty.toLlvm(&o.builder)); |
| 9989 | 10048 | |
| 9990 | 10049 | const ptr = try self.resolveInst(prefetch.ptr); |
| 9991 | 10050 | |
| 9992 | 10051 | const params = [_]*llvm.Value{ |
| 9993 | | ptr, |
| 10052 | ptr.toLlvm(&self.wip), |
| 9994 | 10053 | (try o.builder.intConst(.i32, @intFromEnum(prefetch.rw))).toLlvm(&o.builder), |
| 9995 | 10054 | (try o.builder.intConst(.i32, prefetch.locality)).toLlvm(&o.builder), |
| 9996 | 10055 | (try o.builder.intConst(.i32, @intFromEnum(prefetch.cache))).toLlvm(&o.builder), |
| 9997 | 10056 | }; |
| 9998 | | _ = self.builder.buildCall(fn_val.globalGetValueType(), fn_val, &params, params.len, .C, .Auto, ""); |
| 9999 | | return null; |
| 10057 | _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall( |
| 10058 | llvm_fn_ty.toLlvm(&o.builder), |
| 10059 | fn_val, |
| 10060 | &params, |
| 10061 | params.len, |
| 10062 | .C, |
| 10063 | .Auto, |
| 10064 | "", |
| 10065 | ), &self.wip); |
| 10066 | return .none; |
| 10000 | 10067 | } |
| 10001 | 10068 | |
| 10002 | | fn airAddrSpaceCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 10069 | fn airAddrSpaceCast(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 10003 | 10070 | const o = self.dg.object; |
| 10004 | 10071 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 10005 | 10072 | const inst_ty = self.typeOfIndex(inst); |
| 10006 | 10073 | const operand = try self.resolveInst(ty_op.operand); |
| 10007 | 10074 | |
| 10008 | | const llvm_dest_ty = (try o.lowerType(inst_ty)).toLlvm(&o.builder); |
| 10009 | | return self.builder.buildAddrSpaceCast(operand, llvm_dest_ty, ""); |
| 10075 | return self.wip.cast(.addrspacecast, operand, try o.lowerType(inst_ty), ""); |
| 10010 | 10076 | } |
| 10011 | 10077 | |
| 10012 | | fn amdgcnWorkIntrinsic(self: *FuncGen, dimension: u32, default: u32, comptime basename: []const u8) !?*llvm.Value { |
| 10078 | fn amdgcnWorkIntrinsic(self: *FuncGen, dimension: u32, default: u32, comptime basename: []const u8) !Builder.Value { |
| 10079 | const o = self.dg.object; |
| 10013 | 10080 | const llvm_fn_name = switch (dimension) { |
| 10014 | 10081 | 0 => basename ++ ".x", |
| 10015 | 10082 | 1 => basename ++ ".y", |
| 10016 | 10083 | 2 => basename ++ ".z", |
| 10017 | | else => return (try self.dg.object.builder.intConst(.i32, default)).toLlvm(&self.dg.object.builder), |
| 10084 | else => return o.builder.intValue(.i32, default), |
| 10018 | 10085 | }; |
| 10019 | 10086 | |
| 10020 | 10087 | const args: [0]*llvm.Value = .{}; |
| 10021 | 10088 | const llvm_fn = try self.getIntrinsic(llvm_fn_name, &.{}); |
| 10022 | | return self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, ""); |
| 10089 | return (try self.wip.unimplemented(.i32, "")).finish(self.builder.buildCall( |
| 10090 | (try o.builder.fnType(.i32, &.{}, .normal)).toLlvm(&o.builder), |
| 10091 | llvm_fn, |
| 10092 | &args, |
| 10093 | args.len, |
| 10094 | .Fast, |
| 10095 | .Auto, |
| 10096 | "", |
| 10097 | ), &self.wip); |
| 10023 | 10098 | } |
| 10024 | 10099 | |
| 10025 | | fn airWorkItemId(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 10100 | fn airWorkItemId(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 10026 | 10101 | const o = self.dg.object; |
| 10027 | 10102 | const target = o.module.getTarget(); |
| 10028 | 10103 | assert(target.cpu.arch == .amdgcn); // TODO is to port this function to other GPU architectures |
| ... | ... | @@ -10032,38 +10107,41 @@ pub const FuncGen = struct { |
| 10032 | 10107 | return self.amdgcnWorkIntrinsic(dimension, 0, "llvm.amdgcn.workitem.id"); |
| 10033 | 10108 | } |
| 10034 | 10109 | |
| 10035 | | fn airWorkGroupSize(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 10110 | fn airWorkGroupSize(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 10036 | 10111 | const o = self.dg.object; |
| 10037 | 10112 | const target = o.module.getTarget(); |
| 10038 | 10113 | assert(target.cpu.arch == .amdgcn); // TODO is to port this function to other GPU architectures |
| 10039 | 10114 | |
| 10040 | 10115 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 10041 | 10116 | const dimension = pl_op.payload; |
| 10042 | | if (dimension >= 3) { |
| 10043 | | return (try o.builder.intConst(.i32, 1)).toLlvm(&o.builder); |
| 10044 | | } |
| 10117 | if (dimension >= 3) return o.builder.intValue(.i32, 1); |
| 10045 | 10118 | |
| 10046 | 10119 | // Fetch the dispatch pointer, which points to this structure: |
| 10047 | 10120 | // https://github.com/RadeonOpenCompute/ROCR-Runtime/blob/adae6c61e10d371f7cbc3d0e94ae2c070cab18a4/src/inc/hsa.h#L2913 |
| 10048 | 10121 | const llvm_fn = try self.getIntrinsic("llvm.amdgcn.dispatch.ptr", &.{}); |
| 10049 | 10122 | const args: [0]*llvm.Value = .{}; |
| 10050 | | const dispatch_ptr = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, ""); |
| 10051 | | dispatch_ptr.setAlignment(4); |
| 10123 | const llvm_ret_ty = try o.builder.ptrType(Builder.AddrSpace.amdgpu.constant); |
| 10124 | const dispatch_ptr = (try self.wip.unimplemented(llvm_ret_ty, "")).finish(self.builder.buildCall( |
| 10125 | (try o.builder.fnType(llvm_ret_ty, &.{}, .normal)).toLlvm(&o.builder), |
| 10126 | llvm_fn, |
| 10127 | &args, |
| 10128 | args.len, |
| 10129 | .Fast, |
| 10130 | .Auto, |
| 10131 | "", |
| 10132 | ), &self.wip); |
| 10133 | o.addAttrInt(dispatch_ptr.toLlvm(&self.wip), 0, "align", 4); |
| 10052 | 10134 | |
| 10053 | 10135 | // Load the work_group_* member from the struct as u16. |
| 10054 | 10136 | // Just treat the dispatch pointer as an array of u16 to keep things simple. |
| 10055 | | const offset = 2 + dimension; |
| 10056 | | const index = [_]*llvm.Value{ |
| 10057 | | (try o.builder.intConst(.i32, offset)).toLlvm(&o.builder), |
| 10058 | | }; |
| 10059 | | const llvm_u16 = Builder.Type.i16.toLlvm(&o.builder); |
| 10060 | | const workgroup_size_ptr = self.builder.buildInBoundsGEP(llvm_u16, dispatch_ptr, &index, index.len, ""); |
| 10061 | | const workgroup_size = self.builder.buildLoad(llvm_u16, workgroup_size_ptr, ""); |
| 10062 | | workgroup_size.setAlignment(2); |
| 10063 | | return workgroup_size; |
| 10137 | const workgroup_size_ptr = try self.wip.gep(.inbounds, .i16, dispatch_ptr, &.{ |
| 10138 | try o.builder.intValue(try o.lowerType(Type.usize), 2 + dimension), |
| 10139 | }, ""); |
| 10140 | const workgroup_size_alignment = comptime Builder.Alignment.fromByteUnits(2); |
| 10141 | return self.wip.load(.normal, .i16, workgroup_size_ptr, workgroup_size_alignment, ""); |
| 10064 | 10142 | } |
| 10065 | 10143 | |
| 10066 | | fn airWorkGroupId(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 10144 | fn airWorkGroupId(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 10067 | 10145 | const o = self.dg.object; |
| 10068 | 10146 | const target = o.module.getTarget(); |
| 10069 | 10147 | assert(target.cpu.arch == .amdgcn); // TODO is to port this function to other GPU architectures |
| ... | ... | @@ -10095,13 +10173,13 @@ pub const FuncGen = struct { |
| 10095 | 10173 | .linkage = .private, |
| 10096 | 10174 | .unnamed_addr = .unnamed_addr, |
| 10097 | 10175 | .type = .ptr, |
| 10098 | | .alignment = Builder.Alignment.fromByteUnits(slice_alignment), |
| 10099 | 10176 | .kind = .{ .variable = @enumFromInt(o.builder.variables.items.len) }, |
| 10100 | 10177 | }; |
| 10101 | 10178 | var variable = Builder.Variable{ |
| 10102 | 10179 | .global = @enumFromInt(o.builder.globals.count()), |
| 10103 | 10180 | .mutability = .constant, |
| 10104 | 10181 | .init = undef_init, |
| 10182 | .alignment = Builder.Alignment.fromByteUnits(slice_alignment), |
| 10105 | 10183 | }; |
| 10106 | 10184 | try o.builder.llvm.globals.append(o.gpa, error_name_table_global); |
| 10107 | 10185 | _ = try o.builder.addGlobal(name, global); |
| ... | ... | @@ -10112,97 +10190,95 @@ pub const FuncGen = struct { |
| 10112 | 10190 | } |
| 10113 | 10191 | |
| 10114 | 10192 | /// Assumes the optional is not pointer-like and payload has bits. |
| 10115 | | fn optIsNonNull( |
| 10193 | fn optCmpNull( |
| 10116 | 10194 | self: *FuncGen, |
| 10117 | | opt_llvm_ty: *llvm.Type, |
| 10118 | | opt_handle: *llvm.Value, |
| 10195 | cond: Builder.IntegerCondition, |
| 10196 | opt_llvm_ty: Builder.Type, |
| 10197 | opt_handle: Builder.Value, |
| 10119 | 10198 | is_by_ref: bool, |
| 10120 | | ) Allocator.Error!*llvm.Value { |
| 10199 | ) Allocator.Error!Builder.Value { |
| 10200 | const o = self.dg.object; |
| 10121 | 10201 | const field = b: { |
| 10122 | 10202 | if (is_by_ref) { |
| 10123 | | const field_ptr = self.builder.buildStructGEP(opt_llvm_ty, opt_handle, 1, ""); |
| 10124 | | break :b self.builder.buildLoad(Builder.Type.i8.toLlvm(&self.dg.object.builder), field_ptr, ""); |
| 10203 | const field_ptr = try self.wip.gepStruct(opt_llvm_ty, opt_handle, 1, ""); |
| 10204 | break :b try self.wip.load(.normal, .i8, field_ptr, .default, ""); |
| 10125 | 10205 | } |
| 10126 | | break :b self.builder.buildExtractValue(opt_handle, 1, ""); |
| 10206 | break :b try self.wip.extractValue(opt_handle, &.{1}, ""); |
| 10127 | 10207 | }; |
| 10128 | 10208 | comptime assert(optional_layout_version == 3); |
| 10129 | 10209 | |
| 10130 | | return self.builder.buildICmp(.NE, field, (try self.dg.object.builder.intConst(.i8, 0)).toLlvm(&self.dg.object.builder), ""); |
| 10210 | return self.wip.icmp(cond, field, try o.builder.intValue(.i8, 0), ""); |
| 10131 | 10211 | } |
| 10132 | 10212 | |
| 10133 | 10213 | /// Assumes the optional is not pointer-like and payload has bits. |
| 10134 | 10214 | fn optPayloadHandle( |
| 10135 | 10215 | fg: *FuncGen, |
| 10136 | | opt_llvm_ty: *llvm.Type, |
| 10137 | | opt_handle: *llvm.Value, |
| 10216 | opt_llvm_ty: Builder.Type, |
| 10217 | opt_handle: Builder.Value, |
| 10138 | 10218 | opt_ty: Type, |
| 10139 | 10219 | can_elide_load: bool, |
| 10140 | | ) !*llvm.Value { |
| 10220 | ) !Builder.Value { |
| 10141 | 10221 | const o = fg.dg.object; |
| 10142 | 10222 | const mod = o.module; |
| 10143 | 10223 | const payload_ty = opt_ty.optionalChild(mod); |
| 10144 | 10224 | |
| 10145 | 10225 | if (isByRef(opt_ty, mod)) { |
| 10146 | 10226 | // We have a pointer and we need to return a pointer to the first field. |
| 10147 | | const payload_ptr = fg.builder.buildStructGEP(opt_llvm_ty, opt_handle, 0, ""); |
| 10227 | const payload_ptr = try fg.wip.gepStruct(opt_llvm_ty, opt_handle, 0, ""); |
| 10148 | 10228 | |
| 10149 | | const payload_alignment = payload_ty.abiAlignment(mod); |
| 10229 | const payload_alignment = Builder.Alignment.fromByteUnits(payload_ty.abiAlignment(mod)); |
| 10150 | 10230 | if (isByRef(payload_ty, mod)) { |
| 10151 | 10231 | if (can_elide_load) |
| 10152 | 10232 | return payload_ptr; |
| 10153 | 10233 | |
| 10154 | 10234 | return fg.loadByRef(payload_ptr, payload_ty, payload_alignment, false); |
| 10155 | 10235 | } |
| 10156 | | const payload_llvm_ty = (try o.lowerType(payload_ty)).toLlvm(&o.builder); |
| 10157 | | const load_inst = fg.builder.buildLoad(payload_llvm_ty, payload_ptr, ""); |
| 10158 | | load_inst.setAlignment(payload_alignment); |
| 10159 | | return load_inst; |
| 10236 | const payload_llvm_ty = try o.lowerType(payload_ty); |
| 10237 | return fg.wip.load(.normal, payload_llvm_ty, payload_ptr, payload_alignment, ""); |
| 10160 | 10238 | } |
| 10161 | 10239 | |
| 10162 | 10240 | assert(!isByRef(payload_ty, mod)); |
| 10163 | | return fg.builder.buildExtractValue(opt_handle, 0, ""); |
| 10241 | return fg.wip.extractValue(opt_handle, &.{0}, ""); |
| 10164 | 10242 | } |
| 10165 | 10243 | |
| 10166 | 10244 | fn buildOptional( |
| 10167 | 10245 | self: *FuncGen, |
| 10168 | 10246 | optional_ty: Type, |
| 10169 | | payload: *llvm.Value, |
| 10170 | | non_null_bit: *llvm.Value, |
| 10171 | | ) !?*llvm.Value { |
| 10247 | payload: Builder.Value, |
| 10248 | non_null_bit: Builder.Value, |
| 10249 | ) !Builder.Value { |
| 10172 | 10250 | const o = self.dg.object; |
| 10173 | | const optional_llvm_ty = (try o.lowerType(optional_ty)).toLlvm(&o.builder); |
| 10174 | | const non_null_field = self.builder.buildZExt(non_null_bit, Builder.Type.i8.toLlvm(&o.builder), ""); |
| 10251 | const optional_llvm_ty = try o.lowerType(optional_ty); |
| 10252 | const non_null_field = try self.wip.cast(.zext, non_null_bit, .i8, ""); |
| 10175 | 10253 | const mod = o.module; |
| 10176 | 10254 | |
| 10177 | 10255 | if (isByRef(optional_ty, mod)) { |
| 10178 | | const payload_alignment = optional_ty.abiAlignment(mod); |
| 10256 | const payload_alignment = Builder.Alignment.fromByteUnits(optional_ty.abiAlignment(mod)); |
| 10179 | 10257 | const alloca_inst = try self.buildAlloca(optional_llvm_ty, payload_alignment); |
| 10180 | 10258 | |
| 10181 | 10259 | { |
| 10182 | | const field_ptr = self.builder.buildStructGEP(optional_llvm_ty, alloca_inst, 0, ""); |
| 10183 | | const store_inst = self.builder.buildStore(payload, field_ptr); |
| 10184 | | store_inst.setAlignment(payload_alignment); |
| 10260 | const field_ptr = try self.wip.gepStruct(optional_llvm_ty, alloca_inst, 0, ""); |
| 10261 | _ = try self.wip.store(.normal, payload, field_ptr, payload_alignment); |
| 10185 | 10262 | } |
| 10186 | 10263 | { |
| 10187 | | const field_ptr = self.builder.buildStructGEP(optional_llvm_ty, alloca_inst, 1, ""); |
| 10188 | | const store_inst = self.builder.buildStore(non_null_field, field_ptr); |
| 10189 | | store_inst.setAlignment(1); |
| 10264 | const non_null_alignment = comptime Builder.Alignment.fromByteUnits(1); |
| 10265 | const field_ptr = try self.wip.gepStruct(optional_llvm_ty, alloca_inst, 1, ""); |
| 10266 | _ = try self.wip.store(.normal, non_null_field, field_ptr, non_null_alignment); |
| 10190 | 10267 | } |
| 10191 | 10268 | |
| 10192 | 10269 | return alloca_inst; |
| 10193 | 10270 | } |
| 10194 | 10271 | |
| 10195 | | const partial = self.builder.buildInsertValue(optional_llvm_ty.getUndef(), payload, 0, ""); |
| 10196 | | return self.builder.buildInsertValue(partial, non_null_field, 1, ""); |
| 10272 | return self.wip.buildAggregate(optional_llvm_ty, &.{ payload, non_null_field }, ""); |
| 10197 | 10273 | } |
| 10198 | 10274 | |
| 10199 | 10275 | fn fieldPtr( |
| 10200 | 10276 | self: *FuncGen, |
| 10201 | 10277 | inst: Air.Inst.Index, |
| 10202 | | struct_ptr: *llvm.Value, |
| 10278 | struct_ptr: Builder.Value, |
| 10203 | 10279 | struct_ptr_ty: Type, |
| 10204 | 10280 | field_index: u32, |
| 10205 | | ) !?*llvm.Value { |
| 10281 | ) !Builder.Value { |
| 10206 | 10282 | const o = self.dg.object; |
| 10207 | 10283 | const mod = o.module; |
| 10208 | 10284 | const struct_ty = struct_ptr_ty.childType(mod); |
| ... | ... | @@ -10224,25 +10300,25 @@ pub const FuncGen = struct { |
| 10224 | 10300 | // Offset our operand pointer by the correct number of bytes. |
| 10225 | 10301 | const byte_offset = struct_ty.packedStructFieldByteOffset(field_index, mod); |
| 10226 | 10302 | if (byte_offset == 0) return struct_ptr; |
| 10227 | | const byte_llvm_ty = Builder.Type.i8.toLlvm(&o.builder); |
| 10228 | 10303 | const usize_ty = try o.lowerType(Type.usize); |
| 10229 | | const llvm_index = try o.builder.intConst(usize_ty, byte_offset); |
| 10230 | | const indices: [1]*llvm.Value = .{llvm_index.toLlvm(&o.builder)}; |
| 10231 | | return self.builder.buildInBoundsGEP(byte_llvm_ty, struct_ptr, &indices, indices.len, ""); |
| 10304 | const llvm_index = try o.builder.intValue(usize_ty, byte_offset); |
| 10305 | return self.wip.gep(.inbounds, .i8, struct_ptr, &.{llvm_index}, ""); |
| 10232 | 10306 | }, |
| 10233 | 10307 | else => { |
| 10234 | | const struct_llvm_ty = (try o.lowerPtrElemTy(struct_ty)).toLlvm(&o.builder); |
| 10308 | const struct_llvm_ty = try o.lowerPtrElemTy(struct_ty); |
| 10235 | 10309 | |
| 10236 | 10310 | if (llvmField(struct_ty, field_index, mod)) |llvm_field| { |
| 10237 | | return self.builder.buildStructGEP(struct_llvm_ty, struct_ptr, llvm_field.index, ""); |
| 10311 | return self.wip.gepStruct(struct_llvm_ty, struct_ptr, llvm_field.index, ""); |
| 10238 | 10312 | } else { |
| 10239 | 10313 | // If we found no index then this means this is a zero sized field at the |
| 10240 | 10314 | // end of the struct. Treat our struct pointer as an array of two and get |
| 10241 | 10315 | // the index to the element at index `1` to get a pointer to the end of |
| 10242 | 10316 | // the struct. |
| 10243 | | const llvm_index = try o.builder.intConst(.i32, @intFromBool(struct_ty.hasRuntimeBitsIgnoreComptime(mod))); |
| 10244 | | const indices: [1]*llvm.Value = .{llvm_index.toLlvm(&o.builder)}; |
| 10245 | | return self.builder.buildInBoundsGEP(struct_llvm_ty, struct_ptr, &indices, indices.len, ""); |
| 10317 | const llvm_index = try o.builder.intValue( |
| 10318 | try o.lowerType(Type.usize), |
| 10319 | @intFromBool(struct_ty.hasRuntimeBitsIgnoreComptime(mod)), |
| 10320 | ); |
| 10321 | return self.wip.gep(.inbounds, struct_llvm_ty, struct_ptr, &.{llvm_index}, ""); |
| 10246 | 10322 | } |
| 10247 | 10323 | }, |
| 10248 | 10324 | }, |
| ... | ... | @@ -10250,15 +10326,18 @@ pub const FuncGen = struct { |
| 10250 | 10326 | const layout = struct_ty.unionGetLayout(mod); |
| 10251 | 10327 | if (layout.payload_size == 0 or struct_ty.containerLayout(mod) == .Packed) return struct_ptr; |
| 10252 | 10328 | const payload_index = @intFromBool(layout.tag_align >= layout.payload_align); |
| 10253 | | const union_llvm_ty = (try o.lowerType(struct_ty)).toLlvm(&o.builder); |
| 10254 | | const union_field_ptr = self.builder.buildStructGEP(union_llvm_ty, struct_ptr, payload_index, ""); |
| 10255 | | return union_field_ptr; |
| 10329 | const union_llvm_ty = try o.lowerType(struct_ty); |
| 10330 | return self.wip.gepStruct(union_llvm_ty, struct_ptr, payload_index, ""); |
| 10256 | 10331 | }, |
| 10257 | 10332 | else => unreachable, |
| 10258 | 10333 | } |
| 10259 | 10334 | } |
| 10260 | 10335 | |
| 10261 | | fn getIntrinsic(fg: *FuncGen, name: []const u8, types: []const Builder.Type) Allocator.Error!*llvm.Value { |
| 10336 | fn getIntrinsic( |
| 10337 | fg: *FuncGen, |
| 10338 | name: []const u8, |
| 10339 | types: []const Builder.Type, |
| 10340 | ) Allocator.Error!*llvm.Value { |
| 10262 | 10341 | const o = fg.dg.object; |
| 10263 | 10342 | const id = llvm.lookupIntrinsicID(name.ptr, name.len); |
| 10264 | 10343 | assert(id != 0); |
| ... | ... | @@ -10271,109 +10350,105 @@ pub const FuncGen = struct { |
| 10271 | 10350 | /// Load a by-ref type by constructing a new alloca and performing a memcpy. |
| 10272 | 10351 | fn loadByRef( |
| 10273 | 10352 | fg: *FuncGen, |
| 10274 | | ptr: *llvm.Value, |
| 10353 | ptr: Builder.Value, |
| 10275 | 10354 | pointee_type: Type, |
| 10276 | | ptr_alignment: u32, |
| 10355 | ptr_alignment: Builder.Alignment, |
| 10277 | 10356 | is_volatile: bool, |
| 10278 | | ) !*llvm.Value { |
| 10357 | ) !Builder.Value { |
| 10279 | 10358 | const o = fg.dg.object; |
| 10280 | 10359 | const mod = o.module; |
| 10281 | | const pointee_llvm_ty = (try o.lowerType(pointee_type)).toLlvm(&o.builder); |
| 10282 | | const result_align = @max(ptr_alignment, pointee_type.abiAlignment(mod)); |
| 10360 | const pointee_llvm_ty = try o.lowerType(pointee_type); |
| 10361 | const result_align = Builder.Alignment.fromByteUnits( |
| 10362 | @max(ptr_alignment.toByteUnits() orelse 0, pointee_type.abiAlignment(mod)), |
| 10363 | ); |
| 10283 | 10364 | const result_ptr = try fg.buildAlloca(pointee_llvm_ty, result_align); |
| 10284 | 10365 | const usize_ty = try o.lowerType(Type.usize); |
| 10285 | 10366 | const size_bytes = pointee_type.abiSize(mod); |
| 10286 | | _ = fg.builder.buildMemCpy( |
| 10287 | | result_ptr, |
| 10288 | | result_align, |
| 10289 | | ptr, |
| 10290 | | ptr_alignment, |
| 10367 | _ = (try fg.wip.unimplemented(.void, "")).finish(fg.builder.buildMemCpy( |
| 10368 | result_ptr.toLlvm(&fg.wip), |
| 10369 | @intCast(result_align.toByteUnits() orelse 0), |
| 10370 | ptr.toLlvm(&fg.wip), |
| 10371 | @intCast(ptr_alignment.toByteUnits() orelse 0), |
| 10291 | 10372 | (try o.builder.intConst(usize_ty, size_bytes)).toLlvm(&o.builder), |
| 10292 | 10373 | is_volatile, |
| 10293 | | ); |
| 10374 | ), &fg.wip); |
| 10294 | 10375 | return result_ptr; |
| 10295 | 10376 | } |
| 10296 | 10377 | |
| 10297 | 10378 | /// This function always performs a copy. For isByRef=true types, it creates a new |
| 10298 | 10379 | /// alloca and copies the value into it, then returns the alloca instruction. |
| 10299 | 10380 | /// For isByRef=false types, it creates a load instruction and returns it. |
| 10300 | | fn load(self: *FuncGen, ptr: *llvm.Value, ptr_ty: Type) !?*llvm.Value { |
| 10381 | fn load(self: *FuncGen, ptr: Builder.Value, ptr_ty: Type) !Builder.Value { |
| 10301 | 10382 | const o = self.dg.object; |
| 10302 | 10383 | const mod = o.module; |
| 10303 | 10384 | const info = ptr_ty.ptrInfo(mod); |
| 10304 | 10385 | const elem_ty = info.child.toType(); |
| 10305 | | if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) return null; |
| 10386 | if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) return .none; |
| 10306 | 10387 | |
| 10307 | | const ptr_alignment: u32 = @intCast(info.flags.alignment.toByteUnitsOptional() orelse |
| 10308 | | elem_ty.abiAlignment(mod)); |
| 10309 | | const ptr_volatile = llvm.Bool.fromBool(info.flags.is_volatile); |
| 10388 | const ptr_alignment = Builder.Alignment.fromByteUnits( |
| 10389 | info.flags.alignment.toByteUnitsOptional() orelse elem_ty.abiAlignment(mod), |
| 10390 | ); |
| 10391 | const ptr_kind: Builder.MemoryAccessKind = switch (info.flags.is_volatile) { |
| 10392 | false => .normal, |
| 10393 | true => .@"volatile", |
| 10394 | }; |
| 10310 | 10395 | |
| 10311 | 10396 | assert(info.flags.vector_index != .runtime); |
| 10312 | 10397 | if (info.flags.vector_index != .none) { |
| 10313 | | const index_u32 = try o.builder.intConst(.i32, @intFromEnum(info.flags.vector_index)); |
| 10398 | const index_u32 = try o.builder.intValue(.i32, @intFromEnum(info.flags.vector_index)); |
| 10314 | 10399 | const vec_elem_ty = try o.lowerType(elem_ty); |
| 10315 | 10400 | const vec_ty = try o.builder.vectorType(.normal, info.packed_offset.host_size, vec_elem_ty); |
| 10316 | 10401 | |
| 10317 | | const loaded_vector = self.builder.buildLoad(vec_ty.toLlvm(&o.builder), ptr, ""); |
| 10318 | | loaded_vector.setAlignment(ptr_alignment); |
| 10319 | | loaded_vector.setVolatile(ptr_volatile); |
| 10320 | | |
| 10321 | | return self.builder.buildExtractElement(loaded_vector, index_u32.toLlvm(&o.builder), ""); |
| 10402 | const loaded_vector = try self.wip.load(ptr_kind, vec_ty, ptr, ptr_alignment, ""); |
| 10403 | return self.wip.extractElement(loaded_vector, index_u32, ""); |
| 10322 | 10404 | } |
| 10323 | 10405 | |
| 10324 | 10406 | if (info.packed_offset.host_size == 0) { |
| 10325 | 10407 | if (isByRef(elem_ty, mod)) { |
| 10326 | 10408 | return self.loadByRef(ptr, elem_ty, ptr_alignment, info.flags.is_volatile); |
| 10327 | 10409 | } |
| 10328 | | const elem_llvm_ty = (try o.lowerType(elem_ty)).toLlvm(&o.builder); |
| 10329 | | const llvm_inst = self.builder.buildLoad(elem_llvm_ty, ptr, ""); |
| 10330 | | llvm_inst.setAlignment(ptr_alignment); |
| 10331 | | llvm_inst.setVolatile(ptr_volatile); |
| 10332 | | return llvm_inst; |
| 10410 | return self.wip.load(ptr_kind, try o.lowerType(elem_ty), ptr, ptr_alignment, ""); |
| 10333 | 10411 | } |
| 10334 | 10412 | |
| 10335 | 10413 | const containing_int_ty = try o.builder.intType(@intCast(info.packed_offset.host_size * 8)); |
| 10336 | | const containing_int = self.builder.buildLoad(containing_int_ty.toLlvm(&o.builder), ptr, ""); |
| 10337 | | containing_int.setAlignment(ptr_alignment); |
| 10338 | | containing_int.setVolatile(ptr_volatile); |
| 10414 | const containing_int = try self.wip.load(ptr_kind, containing_int_ty, ptr, ptr_alignment, ""); |
| 10339 | 10415 | |
| 10340 | 10416 | const elem_bits = ptr_ty.childType(mod).bitSize(mod); |
| 10341 | | const shift_amt = try o.builder.intConst(containing_int_ty, info.packed_offset.bit_offset); |
| 10342 | | const shifted_value = self.builder.buildLShr(containing_int, shift_amt.toLlvm(&o.builder), ""); |
| 10343 | | const elem_llvm_ty = (try o.lowerType(elem_ty)).toLlvm(&o.builder); |
| 10417 | const shift_amt = try o.builder.intValue(containing_int_ty, info.packed_offset.bit_offset); |
| 10418 | const shifted_value = try self.wip.bin(.lshr, containing_int, shift_amt, ""); |
| 10419 | const elem_llvm_ty = try o.lowerType(elem_ty); |
| 10344 | 10420 | |
| 10345 | 10421 | if (isByRef(elem_ty, mod)) { |
| 10346 | | const result_align = elem_ty.abiAlignment(mod); |
| 10422 | const result_align = Builder.Alignment.fromByteUnits(elem_ty.abiAlignment(mod)); |
| 10347 | 10423 | const result_ptr = try self.buildAlloca(elem_llvm_ty, result_align); |
| 10348 | 10424 | |
| 10349 | | const same_size_int = (try o.builder.intType(@intCast(elem_bits))).toLlvm(&o.builder); |
| 10350 | | const truncated_int = self.builder.buildTrunc(shifted_value, same_size_int, ""); |
| 10351 | | const store_inst = self.builder.buildStore(truncated_int, result_ptr); |
| 10352 | | store_inst.setAlignment(result_align); |
| 10425 | const same_size_int = try o.builder.intType(@intCast(elem_bits)); |
| 10426 | const truncated_int = try self.wip.cast(.trunc, shifted_value, same_size_int, ""); |
| 10427 | _ = try self.wip.store(.normal, truncated_int, result_ptr, result_align); |
| 10353 | 10428 | return result_ptr; |
| 10354 | 10429 | } |
| 10355 | 10430 | |
| 10356 | 10431 | if (elem_ty.zigTypeTag(mod) == .Float or elem_ty.zigTypeTag(mod) == .Vector) { |
| 10357 | | const same_size_int = (try o.builder.intType(@intCast(elem_bits))).toLlvm(&o.builder); |
| 10358 | | const truncated_int = self.builder.buildTrunc(shifted_value, same_size_int, ""); |
| 10359 | | return self.builder.buildBitCast(truncated_int, elem_llvm_ty, ""); |
| 10432 | const same_size_int = try o.builder.intType(@intCast(elem_bits)); |
| 10433 | const truncated_int = try self.wip.cast(.trunc, shifted_value, same_size_int, ""); |
| 10434 | return self.wip.cast(.bitcast, truncated_int, elem_llvm_ty, ""); |
| 10360 | 10435 | } |
| 10361 | 10436 | |
| 10362 | 10437 | if (elem_ty.isPtrAtRuntime(mod)) { |
| 10363 | | const same_size_int = (try o.builder.intType(@intCast(elem_bits))).toLlvm(&o.builder); |
| 10364 | | const truncated_int = self.builder.buildTrunc(shifted_value, same_size_int, ""); |
| 10365 | | return self.builder.buildIntToPtr(truncated_int, elem_llvm_ty, ""); |
| 10438 | const same_size_int = try o.builder.intType(@intCast(elem_bits)); |
| 10439 | const truncated_int = try self.wip.cast(.trunc, shifted_value, same_size_int, ""); |
| 10440 | return self.wip.cast(.inttoptr, truncated_int, elem_llvm_ty, ""); |
| 10366 | 10441 | } |
| 10367 | 10442 | |
| 10368 | | return self.builder.buildTrunc(shifted_value, elem_llvm_ty, ""); |
| 10443 | return self.wip.cast(.trunc, shifted_value, elem_llvm_ty, ""); |
| 10369 | 10444 | } |
| 10370 | 10445 | |
| 10371 | 10446 | fn store( |
| 10372 | 10447 | self: *FuncGen, |
| 10373 | | ptr: *llvm.Value, |
| 10448 | ptr: Builder.Value, |
| 10374 | 10449 | ptr_ty: Type, |
| 10375 | | elem: *llvm.Value, |
| 10376 | | ordering: llvm.AtomicOrdering, |
| 10450 | elem: Builder.Value, |
| 10451 | ordering: Builder.AtomicOrdering, |
| 10377 | 10452 | ) !void { |
| 10378 | 10453 | const o = self.dg.object; |
| 10379 | 10454 | const mod = o.module; |
| ... | ... | @@ -10382,43 +10457,41 @@ pub const FuncGen = struct { |
| 10382 | 10457 | if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) { |
| 10383 | 10458 | return; |
| 10384 | 10459 | } |
| 10385 | | const ptr_alignment = ptr_ty.ptrAlignment(mod); |
| 10386 | | const ptr_volatile = llvm.Bool.fromBool(info.flags.is_volatile); |
| 10460 | const ptr_alignment = Builder.Alignment.fromByteUnits(ptr_ty.ptrAlignment(mod)); |
| 10461 | const ptr_kind: Builder.MemoryAccessKind = switch (info.flags.is_volatile) { |
| 10462 | false => .normal, |
| 10463 | true => .@"volatile", |
| 10464 | }; |
| 10387 | 10465 | |
| 10388 | 10466 | assert(info.flags.vector_index != .runtime); |
| 10389 | 10467 | if (info.flags.vector_index != .none) { |
| 10390 | | const index_u32 = try o.builder.intConst(.i32, @intFromEnum(info.flags.vector_index)); |
| 10468 | const index_u32 = try o.builder.intValue(.i32, @intFromEnum(info.flags.vector_index)); |
| 10391 | 10469 | const vec_elem_ty = try o.lowerType(elem_ty); |
| 10392 | 10470 | const vec_ty = try o.builder.vectorType(.normal, info.packed_offset.host_size, vec_elem_ty); |
| 10393 | 10471 | |
| 10394 | | const loaded_vector = self.builder.buildLoad(vec_ty.toLlvm(&o.builder), ptr, ""); |
| 10395 | | loaded_vector.setAlignment(ptr_alignment); |
| 10396 | | loaded_vector.setVolatile(ptr_volatile); |
| 10472 | const loaded_vector = try self.wip.load(ptr_kind, vec_ty, ptr, ptr_alignment, ""); |
| 10397 | 10473 | |
| 10398 | | const modified_vector = self.builder.buildInsertElement(loaded_vector, elem, index_u32.toLlvm(&o.builder), ""); |
| 10474 | const modified_vector = try self.wip.insertElement(loaded_vector, elem, index_u32, ""); |
| 10399 | 10475 | |
| 10400 | | const store_inst = self.builder.buildStore(modified_vector, ptr); |
| 10401 | | assert(ordering == .NotAtomic); |
| 10402 | | store_inst.setAlignment(ptr_alignment); |
| 10403 | | store_inst.setVolatile(ptr_volatile); |
| 10476 | assert(ordering == .none); |
| 10477 | _ = try self.wip.store(ptr_kind, modified_vector, ptr, ptr_alignment); |
| 10404 | 10478 | return; |
| 10405 | 10479 | } |
| 10406 | 10480 | |
| 10407 | 10481 | if (info.packed_offset.host_size != 0) { |
| 10408 | 10482 | const containing_int_ty = try o.builder.intType(@intCast(info.packed_offset.host_size * 8)); |
| 10409 | | const containing_int = self.builder.buildLoad(containing_int_ty.toLlvm(&o.builder), ptr, ""); |
| 10410 | | assert(ordering == .NotAtomic); |
| 10411 | | containing_int.setAlignment(ptr_alignment); |
| 10412 | | containing_int.setVolatile(ptr_volatile); |
| 10483 | assert(ordering == .none); |
| 10484 | const containing_int = |
| 10485 | try self.wip.load(ptr_kind, containing_int_ty, ptr, ptr_alignment, ""); |
| 10413 | 10486 | const elem_bits = ptr_ty.childType(mod).bitSize(mod); |
| 10414 | 10487 | const shift_amt = try o.builder.intConst(containing_int_ty, info.packed_offset.bit_offset); |
| 10415 | 10488 | // Convert to equally-sized integer type in order to perform the bit |
| 10416 | 10489 | // operations on the value to store |
| 10417 | 10490 | const value_bits_type = try o.builder.intType(@intCast(elem_bits)); |
| 10418 | 10491 | const value_bits = if (elem_ty.isPtrAtRuntime(mod)) |
| 10419 | | self.builder.buildPtrToInt(elem, value_bits_type.toLlvm(&o.builder), "") |
| 10492 | try self.wip.cast(.ptrtoint, elem, value_bits_type, "") |
| 10420 | 10493 | else |
| 10421 | | self.builder.buildBitCast(elem, value_bits_type.toLlvm(&o.builder), ""); |
| 10494 | try self.wip.cast(.bitcast, elem, value_bits_type, ""); |
| 10422 | 10495 | |
| 10423 | 10496 | var mask_val = try o.builder.intConst(value_bits_type, -1); |
| 10424 | 10497 | mask_val = try o.builder.castConst(.zext, mask_val, containing_int_ty); |
| ... | ... | @@ -10426,79 +10499,73 @@ pub const FuncGen = struct { |
| 10426 | 10499 | mask_val = |
| 10427 | 10500 | try o.builder.binConst(.xor, mask_val, try o.builder.intConst(containing_int_ty, -1)); |
| 10428 | 10501 | |
| 10429 | | const anded_containing_int = self.builder.buildAnd(containing_int, mask_val.toLlvm(&o.builder), ""); |
| 10430 | | const extended_value = self.builder.buildZExt(value_bits, containing_int_ty.toLlvm(&o.builder), ""); |
| 10431 | | const shifted_value = self.builder.buildShl(extended_value, shift_amt.toLlvm(&o.builder), ""); |
| 10432 | | const ored_value = self.builder.buildOr(shifted_value, anded_containing_int, ""); |
| 10502 | const anded_containing_int = |
| 10503 | try self.wip.bin(.@"and", containing_int, mask_val.toValue(), ""); |
| 10504 | const extended_value = try self.wip.cast(.zext, value_bits, containing_int_ty, ""); |
| 10505 | const shifted_value = try self.wip.bin(.shl, extended_value, shift_amt.toValue(), ""); |
| 10506 | const ored_value = try self.wip.bin(.@"or", shifted_value, anded_containing_int, ""); |
| 10433 | 10507 | |
| 10434 | | const store_inst = self.builder.buildStore(ored_value, ptr); |
| 10435 | | assert(ordering == .NotAtomic); |
| 10436 | | store_inst.setAlignment(ptr_alignment); |
| 10437 | | store_inst.setVolatile(ptr_volatile); |
| 10508 | assert(ordering == .none); |
| 10509 | _ = try self.wip.store(ptr_kind, ored_value, ptr, ptr_alignment); |
| 10438 | 10510 | return; |
| 10439 | 10511 | } |
| 10440 | 10512 | if (!isByRef(elem_ty, mod)) { |
| 10441 | | const store_inst = self.builder.buildStore(elem, ptr); |
| 10442 | | store_inst.setOrdering(ordering); |
| 10443 | | store_inst.setAlignment(ptr_alignment); |
| 10444 | | store_inst.setVolatile(ptr_volatile); |
| 10513 | _ = try self.wip.storeAtomic(ptr_kind, elem, ptr, self.sync_scope, ordering, ptr_alignment); |
| 10445 | 10514 | return; |
| 10446 | 10515 | } |
| 10447 | | assert(ordering == .NotAtomic); |
| 10516 | assert(ordering == .none); |
| 10448 | 10517 | const size_bytes = elem_ty.abiSize(mod); |
| 10449 | | _ = self.builder.buildMemCpy( |
| 10450 | | ptr, |
| 10451 | | ptr_alignment, |
| 10452 | | elem, |
| 10518 | _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildMemCpy( |
| 10519 | ptr.toLlvm(&self.wip), |
| 10520 | @intCast(ptr_alignment.toByteUnits() orelse 0), |
| 10521 | elem.toLlvm(&self.wip), |
| 10453 | 10522 | elem_ty.abiAlignment(mod), |
| 10454 | 10523 | (try o.builder.intConst(try o.lowerType(Type.usize), size_bytes)).toLlvm(&o.builder), |
| 10455 | 10524 | info.flags.is_volatile, |
| 10456 | | ); |
| 10525 | ), &self.wip); |
| 10457 | 10526 | } |
| 10458 | 10527 | |
| 10459 | | fn valgrindMarkUndef(fg: *FuncGen, ptr: *llvm.Value, len: *llvm.Value) Allocator.Error!void { |
| 10528 | fn valgrindMarkUndef(fg: *FuncGen, ptr: Builder.Value, len: Builder.Value) Allocator.Error!void { |
| 10460 | 10529 | const VG_USERREQ__MAKE_MEM_UNDEFINED = 1296236545; |
| 10461 | 10530 | const o = fg.dg.object; |
| 10462 | 10531 | const usize_ty = try o.lowerType(Type.usize); |
| 10463 | | const zero = (try o.builder.intConst(usize_ty, 0)).toLlvm(&o.builder); |
| 10464 | | const req = (try o.builder.intConst(usize_ty, VG_USERREQ__MAKE_MEM_UNDEFINED)).toLlvm(&o.builder); |
| 10465 | | const ptr_as_usize = fg.builder.buildPtrToInt(ptr, usize_ty.toLlvm(&o.builder), ""); |
| 10532 | const zero = try o.builder.intValue(usize_ty, 0); |
| 10533 | const req = try o.builder.intValue(usize_ty, VG_USERREQ__MAKE_MEM_UNDEFINED); |
| 10534 | const ptr_as_usize = try fg.wip.cast(.ptrtoint, ptr, usize_ty, ""); |
| 10466 | 10535 | _ = try valgrindClientRequest(fg, zero, req, ptr_as_usize, len, zero, zero, zero); |
| 10467 | 10536 | } |
| 10468 | 10537 | |
| 10469 | 10538 | fn valgrindClientRequest( |
| 10470 | 10539 | fg: *FuncGen, |
| 10471 | | default_value: *llvm.Value, |
| 10472 | | request: *llvm.Value, |
| 10473 | | a1: *llvm.Value, |
| 10474 | | a2: *llvm.Value, |
| 10475 | | a3: *llvm.Value, |
| 10476 | | a4: *llvm.Value, |
| 10477 | | a5: *llvm.Value, |
| 10478 | | ) Allocator.Error!*llvm.Value { |
| 10540 | default_value: Builder.Value, |
| 10541 | request: Builder.Value, |
| 10542 | a1: Builder.Value, |
| 10543 | a2: Builder.Value, |
| 10544 | a3: Builder.Value, |
| 10545 | a4: Builder.Value, |
| 10546 | a5: Builder.Value, |
| 10547 | ) Allocator.Error!Builder.Value { |
| 10479 | 10548 | const o = fg.dg.object; |
| 10480 | 10549 | const mod = o.module; |
| 10481 | 10550 | const target = mod.getTarget(); |
| 10482 | 10551 | if (!target_util.hasValgrindSupport(target)) return default_value; |
| 10483 | 10552 | |
| 10484 | 10553 | const llvm_usize = try o.lowerType(Type.usize); |
| 10485 | | const usize_alignment = Type.usize.abiSize(mod); |
| 10554 | const usize_alignment = Builder.Alignment.fromByteUnits(Type.usize.abiAlignment(mod)); |
| 10486 | 10555 | |
| 10487 | | const array_llvm_ty = (try o.builder.arrayType(6, llvm_usize)).toLlvm(&o.builder); |
| 10488 | | const array_ptr = fg.valgrind_client_request_array orelse a: { |
| 10489 | | const array_ptr = try fg.buildAlloca(array_llvm_ty, @intCast(usize_alignment)); |
| 10556 | const array_llvm_ty = try o.builder.arrayType(6, llvm_usize); |
| 10557 | const array_ptr = if (fg.valgrind_client_request_array == .none) a: { |
| 10558 | const array_ptr = try fg.buildAlloca(array_llvm_ty, usize_alignment); |
| 10490 | 10559 | fg.valgrind_client_request_array = array_ptr; |
| 10491 | 10560 | break :a array_ptr; |
| 10492 | | }; |
| 10493 | | const array_elements = [_]*llvm.Value{ request, a1, a2, a3, a4, a5 }; |
| 10494 | | const zero = (try o.builder.intConst(llvm_usize, 0)).toLlvm(&o.builder); |
| 10561 | } else fg.valgrind_client_request_array; |
| 10562 | const array_elements = [_]Builder.Value{ request, a1, a2, a3, a4, a5 }; |
| 10563 | const zero = try o.builder.intValue(llvm_usize, 0); |
| 10495 | 10564 | for (array_elements, 0..) |elem, i| { |
| 10496 | | const indexes = [_]*llvm.Value{ |
| 10497 | | zero, (try o.builder.intConst(llvm_usize, i)).toLlvm(&o.builder), |
| 10498 | | }; |
| 10499 | | const elem_ptr = fg.builder.buildInBoundsGEP(array_llvm_ty, array_ptr, &indexes, indexes.len, ""); |
| 10500 | | const store_inst = fg.builder.buildStore(elem, elem_ptr); |
| 10501 | | store_inst.setAlignment(@intCast(usize_alignment)); |
| 10565 | const elem_ptr = try fg.wip.gep(.inbounds, array_llvm_ty, array_ptr, &.{ |
| 10566 | zero, try o.builder.intValue(llvm_usize, i), |
| 10567 | }, ""); |
| 10568 | _ = try fg.wip.store(.normal, elem, elem_ptr, usize_alignment); |
| 10502 | 10569 | } |
| 10503 | 10570 | |
| 10504 | 10571 | const arch_specific: struct { |
| ... | ... | @@ -10533,8 +10600,8 @@ pub const FuncGen = struct { |
| 10533 | 10600 | }; |
| 10534 | 10601 | |
| 10535 | 10602 | const fn_llvm_ty = (try o.builder.fnType(llvm_usize, &(.{llvm_usize} ** 2), .normal)).toLlvm(&o.builder); |
| 10536 | | const array_ptr_as_usize = fg.builder.buildPtrToInt(array_ptr, llvm_usize.toLlvm(&o.builder), ""); |
| 10537 | | const args = [_]*llvm.Value{ array_ptr_as_usize, default_value }; |
| 10603 | const array_ptr_as_usize = try fg.wip.cast(.ptrtoint, array_ptr, llvm_usize, ""); |
| 10604 | const args = [_]*llvm.Value{ array_ptr_as_usize.toLlvm(&fg.wip), default_value.toLlvm(&fg.wip) }; |
| 10538 | 10605 | const asm_fn = llvm.getInlineAsm( |
| 10539 | 10606 | fn_llvm_ty, |
| 10540 | 10607 | arch_specific.template.ptr, |
| ... | ... | @@ -10547,14 +10614,9 @@ pub const FuncGen = struct { |
| 10547 | 10614 | .False, // can throw |
| 10548 | 10615 | ); |
| 10549 | 10616 | |
| 10550 | | const call = fg.builder.buildCall( |
| 10551 | | fn_llvm_ty, |
| 10552 | | asm_fn, |
| 10553 | | &args, |
| 10554 | | args.len, |
| 10555 | | .C, |
| 10556 | | .Auto, |
| 10557 | | "", |
| 10617 | const call = (try fg.wip.unimplemented(llvm_usize, "")).finish( |
| 10618 | fg.builder.buildCall(fn_llvm_ty, asm_fn, &args, args.len, .C, .Auto, ""), |
| 10619 | &fg.wip, |
| 10558 | 10620 | ); |
| 10559 | 10621 | return call; |
| 10560 | 10622 | } |
| ... | ... | @@ -10764,14 +10826,14 @@ fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void { |
| 10764 | 10826 | } |
| 10765 | 10827 | } |
| 10766 | 10828 | |
| 10767 | | fn toLlvmAtomicOrdering(atomic_order: std.builtin.AtomicOrder) llvm.AtomicOrdering { |
| 10829 | fn toLlvmAtomicOrdering(atomic_order: std.builtin.AtomicOrder) Builder.AtomicOrdering { |
| 10768 | 10830 | return switch (atomic_order) { |
| 10769 | | .Unordered => .Unordered, |
| 10770 | | .Monotonic => .Monotonic, |
| 10771 | | .Acquire => .Acquire, |
| 10772 | | .Release => .Release, |
| 10773 | | .AcqRel => .AcquireRelease, |
| 10774 | | .SeqCst => .SequentiallyConsistent, |
| 10831 | .Unordered => .unordered, |
| 10832 | .Monotonic => .monotonic, |
| 10833 | .Acquire => .acquire, |
| 10834 | .Release => .release, |
| 10835 | .AcqRel => .acq_rel, |
| 10836 | .SeqCst => .seq_cst, |
| 10775 | 10837 | }; |
| 10776 | 10838 | } |
| 10777 | 10839 | |
| ... | ... | @@ -11718,12 +11780,40 @@ fn compilerRtIntBits(bits: u16) u16 { |
| 11718 | 11780 | return bits; |
| 11719 | 11781 | } |
| 11720 | 11782 | |
| 11783 | fn buildAllocaInner( |
| 11784 | wip: *Builder.WipFunction, |
| 11785 | di_scope_non_null: bool, |
| 11786 | llvm_ty: Builder.Type, |
| 11787 | alignment: Builder.Alignment, |
| 11788 | target: std.Target, |
| 11789 | ) Allocator.Error!Builder.Value { |
| 11790 | const address_space = llvmAllocaAddressSpace(target); |
| 11791 | |
| 11792 | const alloca = blk: { |
| 11793 | const prev_cursor = wip.cursor; |
| 11794 | const prev_debug_location = wip.llvm.builder.getCurrentDebugLocation2(); |
| 11795 | defer { |
| 11796 | wip.cursor = prev_cursor; |
| 11797 | if (wip.cursor.block == .entry) wip.cursor.instruction += 1; |
| 11798 | if (di_scope_non_null) wip.llvm.builder.setCurrentDebugLocation2(prev_debug_location); |
| 11799 | } |
| 11800 | |
| 11801 | wip.cursor = .{ .block = .entry }; |
| 11802 | wip.llvm.builder.clearCurrentDebugLocation(); |
| 11803 | break :blk try wip.alloca(.normal, llvm_ty, .none, alignment, address_space, ""); |
| 11804 | }; |
| 11805 | |
| 11806 | // The pointer returned from this function should have the generic address space, |
| 11807 | // if this isn't the case then cast it to the generic address space. |
| 11808 | return wip.conv(.unneeded, alloca, .ptr, ""); |
| 11809 | } |
| 11810 | |
| 11721 | 11811 | fn errUnionPayloadOffset(payload_ty: Type, mod: *Module) u1 { |
| 11722 | | return @intFromBool(Type.anyerror.abiAlignment(mod) > payload_ty.abiAlignment(mod)); |
| 11812 | return @intFromBool(Type.err_int.abiAlignment(mod) > payload_ty.abiAlignment(mod)); |
| 11723 | 11813 | } |
| 11724 | 11814 | |
| 11725 | 11815 | fn errUnionErrorOffset(payload_ty: Type, mod: *Module) u1 { |
| 11726 | | return @intFromBool(Type.anyerror.abiAlignment(mod) <= payload_ty.abiAlignment(mod)); |
| 11816 | return @intFromBool(Type.err_int.abiAlignment(mod) <= payload_ty.abiAlignment(mod)); |
| 11727 | 11817 | } |
| 11728 | 11818 | |
| 11729 | 11819 | /// Returns true for asm constraint (e.g. "=*m", "=r") if it accepts a memory location |