| ... | @@ -1,16 +1,24 @@ | ... | @@ -1,16 +1,24 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | | 2 | |
| | 3 | const FuncGen = @import("llvm/FuncGen.zig"); |
| | 4 | const buildAllocaInner = FuncGen.buildAllocaInner; |
| | 5 | const isByRef = FuncGen.isByRef; |
| | 6 | const firstParamSRet = FuncGen.firstParamSRet; |
| | 7 | const lowerFnRetTy = FuncGen.lowerFnRetTy; |
| | 8 | const iterateParamTypes = FuncGen.iterateParamTypes; |
| | 9 | const ccAbiPromoteInt = FuncGen.ccAbiPromoteInt; |
| | 10 | const aarch64_c_abi = @import("aarch64/abi.zig"); |
| | 11 | |
| 3 | const std = @import("std"); | 12 | const std = @import("std"); |
| 4 | const Io = std.Io; | 13 | const Io = std.Io; |
| 5 | const assert = std.debug.assert; | 14 | const assert = std.debug.assert; |
| 6 | const Allocator = std.mem.Allocator; | 15 | const Allocator = std.mem.Allocator; |
| 7 | const log = std.log.scoped(.codegen); | 16 | const log = std.log.scoped(.codegen); |
| 8 | const math = std.math; | | |
| 9 | const DW = std.dwarf; | 17 | const DW = std.dwarf; |
| 10 | const Builder = std.zig.llvm.Builder; | 18 | const Builder = std.zig.llvm.Builder; |
| 11 | | 19 | |
| 12 | const build_options = @import("build_options"); | 20 | const build_options = @import("build_options"); |
| 13 | const llvm = if (build_options.have_llvm) | 21 | const bindings = if (build_options.have_llvm) |
| 14 | @import("llvm/bindings.zig") | 22 | @import("llvm/bindings.zig") |
| 15 | else | 23 | else |
| 16 | @compileError("LLVM unavailable"); | 24 | @compileError("LLVM unavailable"); |
| ... | @@ -24,21 +32,9 @@ const Air = @import("../Air.zig"); | ... | @@ -24,21 +32,9 @@ const Air = @import("../Air.zig"); |
| 24 | const Value = @import("../Value.zig"); | 32 | const Value = @import("../Value.zig"); |
| 25 | const Type = @import("../Type.zig"); | 33 | const Type = @import("../Type.zig"); |
| 26 | const codegen = @import("../codegen.zig"); | 34 | const codegen = @import("../codegen.zig"); |
| 27 | const x86_64_abi = @import("x86_64/abi.zig"); | | |
| 28 | const wasm_c_abi = @import("wasm/abi.zig"); | | |
| 29 | const aarch64_c_abi = @import("aarch64/abi.zig"); | | |
| 30 | const arm_c_abi = @import("arm/abi.zig"); | | |
| 31 | const riscv_c_abi = @import("riscv64/abi.zig"); | | |
| 32 | const mips_c_abi = @import("mips/abi.zig"); | | |
| 33 | const dev = @import("../dev.zig"); | 35 | const dev = @import("../dev.zig"); |
| 34 | | 36 | |
| 35 | const target_util = @import("../target.zig"); | 37 | const target_util = @import("../target.zig"); |
| 36 | const libcFloatPrefix = target_util.libcFloatPrefix; | | |
| 37 | const libcFloatSuffix = target_util.libcFloatSuffix; | | |
| 38 | const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev; | | |
| 39 | const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev; | | |
| 40 | | | |
| 41 | const Error = error{ OutOfMemory, CodegenFail }; | | |
| 42 | | 38 | |
| 43 | pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { | 39 | pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { |
| 44 | return comptime &.initMany(&.{ | 40 | return comptime &.initMany(&.{ |
| ... | @@ -493,7 +489,7 @@ pub fn dataLayout(target: *const std.Target) []const u8 { | ... | @@ -493,7 +489,7 @@ pub fn dataLayout(target: *const std.Target) []const u8 { |
| 493 | }; | 489 | }; |
| 494 | } | 490 | } |
| 495 | | 491 | |
| 496 | // Avoid depending on `llvm.CodeModel` in the bitcode-only case. | 492 | // Avoid depending on `bindings.CodeModel` in the bitcode-only case. |
| 497 | const CodeModel = enum { | 493 | const CodeModel = enum { |
| 498 | default, | 494 | default, |
| 499 | tiny, | 495 | tiny, |
| ... | @@ -602,7 +598,7 @@ pub const Object = struct { | ... | @@ -602,7 +598,7 @@ pub const Object = struct { |
| 602 | | 598 | |
| 603 | pub const Ptr = if (dev.env.supports(.llvm_backend)) *Object else noreturn; | 599 | pub const Ptr = if (dev.env.supports(.llvm_backend)) *Object else noreturn; |
| 604 | | 600 | |
| 605 | pub const TypeMap = std.AutoHashMapUnmanaged(InternPool.Index, Builder.Type); | 601 | const TypeMap = std.AutoHashMapUnmanaged(InternPool.Index, Builder.Type); |
| 606 | | 602 | |
| 607 | pub fn create(arena: Allocator, comp: *Compilation) !Ptr { | 603 | pub fn create(arena: Allocator, comp: *Compilation) !Ptr { |
| 608 | dev.check(.llvm_backend); | 604 | dev.check(.llvm_backend); |
| ... | @@ -1026,20 +1022,20 @@ pub const Object = struct { | ... | @@ -1026,20 +1022,20 @@ pub const Object = struct { |
| 1026 | | 1022 | |
| 1027 | initializeLLVMTarget(comp.root_mod.resolved_target.result.cpu.arch); | 1023 | initializeLLVMTarget(comp.root_mod.resolved_target.result.cpu.arch); |
| 1028 | | 1024 | |
| 1029 | const context: *llvm.Context = llvm.Context.create(); | 1025 | const context: *bindings.Context = .create(); |
| 1030 | errdefer context.dispose(); | 1026 | errdefer context.dispose(); |
| 1031 | | 1027 | |
| 1032 | const bitcode_memory_buffer = llvm.MemoryBuffer.createMemoryBufferWithMemoryRange( | 1028 | const bitcode_memory_buffer = bindings.MemoryBuffer.createMemoryBufferWithMemoryRange( |
| 1033 | @ptrCast(bitcode.ptr), | 1029 | @ptrCast(bitcode.ptr), |
| 1034 | bitcode.len * 4, | 1030 | bitcode.len * 4, |
| 1035 | "BitcodeBuffer", | 1031 | "BitcodeBuffer", |
| 1036 | llvm.Bool.False, | 1032 | bindings.Bool.False, |
| 1037 | ); | 1033 | ); |
| 1038 | defer bitcode_memory_buffer.dispose(); | 1034 | defer bitcode_memory_buffer.dispose(); |
| 1039 | | 1035 | |
| 1040 | context.enableBrokenDebugInfoCheck(); | 1036 | context.enableBrokenDebugInfoCheck(); |
| 1041 | | 1037 | |
| 1042 | var module: *llvm.Module = undefined; | 1038 | var module: *bindings.Module = undefined; |
| 1043 | if (context.parseBitcodeInContext2(bitcode_memory_buffer, &module).toBool() or context.getBrokenDebugInfo()) { | 1039 | if (context.parseBitcodeInContext2(bitcode_memory_buffer, &module).toBool() or context.getBrokenDebugInfo()) { |
| 1044 | return diags.fail("Failed to parse bitcode", .{}); | 1040 | return diags.fail("Failed to parse bitcode", .{}); |
| 1045 | } | 1041 | } |
| ... | @@ -1047,28 +1043,28 @@ pub const Object = struct { | ... | @@ -1047,28 +1043,28 @@ pub const Object = struct { |
| 1047 | }; | 1043 | }; |
| 1048 | defer context.dispose(); | 1044 | defer context.dispose(); |
| 1049 | | 1045 | |
| 1050 | var target: *llvm.Target = undefined; | 1046 | var target: *bindings.Target = undefined; |
| 1051 | var error_message: [*:0]const u8 = undefined; | 1047 | var error_message: [*:0]const u8 = undefined; |
| 1052 | if (llvm.Target.getFromTriple(target_triple_sentinel, &target, &error_message).toBool()) { | 1048 | if (bindings.Target.getFromTriple(target_triple_sentinel, &target, &error_message).toBool()) { |
| 1053 | defer llvm.disposeMessage(error_message); | 1049 | defer bindings.disposeMessage(error_message); |
| 1054 | return diags.fail("LLVM failed to parse '{s}': {s}", .{ target_triple_sentinel, error_message }); | 1050 | return diags.fail("LLVM failed to parse '{s}': {s}", .{ target_triple_sentinel, error_message }); |
| 1055 | } | 1051 | } |
| 1056 | | 1052 | |
| 1057 | const optimize_mode = comp.root_mod.optimize_mode; | 1053 | const optimize_mode = comp.root_mod.optimize_mode; |
| 1058 | | 1054 | |
| 1059 | const opt_level: llvm.CodeGenOptLevel = if (optimize_mode == .Debug) | 1055 | const opt_level: bindings.CodeGenOptLevel = if (optimize_mode == .Debug) |
| 1060 | .None | 1056 | .None |
| 1061 | else | 1057 | else |
| 1062 | .Aggressive; | 1058 | .Aggressive; |
| 1063 | | 1059 | |
| 1064 | const reloc_mode: llvm.RelocMode = if (comp.root_mod.pic) | 1060 | const reloc_mode: bindings.RelocMode = if (comp.root_mod.pic) |
| 1065 | .PIC | 1061 | .PIC |
| 1066 | else if (comp.config.link_mode == .dynamic) | 1062 | else if (comp.config.link_mode == .dynamic) |
| 1067 | llvm.RelocMode.DynamicNoPIC | 1063 | bindings.RelocMode.DynamicNoPIC |
| 1068 | else | 1064 | else |
| 1069 | .Static; | 1065 | .Static; |
| 1070 | | 1066 | |
| 1071 | const code_model: llvm.CodeModel = switch (codeModel(comp.root_mod.code_model, &comp.root_mod.resolved_target.result)) { | 1067 | const code_model: bindings.CodeModel = switch (codeModel(comp.root_mod.code_model, &comp.root_mod.resolved_target.result)) { |
| 1072 | .default => .Default, | 1068 | .default => .Default, |
| 1073 | .tiny => .Tiny, | 1069 | .tiny => .Tiny, |
| 1074 | .small => .Small, | 1070 | .small => .Small, |
| ... | @@ -1077,12 +1073,12 @@ pub const Object = struct { | ... | @@ -1077,12 +1073,12 @@ pub const Object = struct { |
| 1077 | .large => .Large, | 1073 | .large => .Large, |
| 1078 | }; | 1074 | }; |
| 1079 | | 1075 | |
| 1080 | const float_abi: llvm.TargetMachine.FloatABI = if (comp.root_mod.resolved_target.result.abi.float() == .hard) | 1076 | const float_abi: bindings.TargetMachine.FloatABI = if (comp.root_mod.resolved_target.result.abi.float() == .hard) |
| 1081 | .Hard | 1077 | .Hard |
| 1082 | else | 1078 | else |
| 1083 | .Soft; | 1079 | .Soft; |
| 1084 | | 1080 | |
| 1085 | var target_machine = llvm.TargetMachine.create( | 1081 | var target_machine = bindings.TargetMachine.create( |
| 1086 | target, | 1082 | target, |
| 1087 | target_triple_sentinel, | 1083 | target_triple_sentinel, |
| 1088 | if (comp.root_mod.resolved_target.result.cpu.model.llvm_name) |s| s.ptr else null, | 1084 | if (comp.root_mod.resolved_target.result.cpu.model.llvm_name) |s| s.ptr else null, |
| ... | @@ -1105,7 +1101,7 @@ pub const Object = struct { | ... | @@ -1105,7 +1101,7 @@ pub const Object = struct { |
| 1105 | // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. | 1101 | // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. |
| 1106 | // So we call the entire pipeline multiple times if this is requested. | 1102 | // So we call the entire pipeline multiple times if this is requested. |
| 1107 | // var error_message: [*:0]const u8 = undefined; | 1103 | // var error_message: [*:0]const u8 = undefined; |
| 1108 | var lowered_options: llvm.TargetMachine.EmitOptions = .{ | 1104 | var lowered_options: bindings.TargetMachine.EmitOptions = .{ |
| 1109 | .is_debug = options.is_debug, | 1105 | .is_debug = options.is_debug, |
| 1110 | .is_small = options.is_small, | 1106 | .is_small = options.is_small, |
| 1111 | .time_report_out = null, // set below to make sure it's only set for a single `emitToFile` | 1107 | .time_report_out = null, // set below to make sure it's only set for a single `emitToFile` |
| ... | @@ -1154,7 +1150,7 @@ pub const Object = struct { | ... | @@ -1154,7 +1150,7 @@ pub const Object = struct { |
| 1154 | }; | 1150 | }; |
| 1155 | if (options.asm_path != null and options.bin_path != null) { | 1151 | if (options.asm_path != null and options.bin_path != null) { |
| 1156 | if (target_machine.emitToFile(module, &error_message, &lowered_options)) { | 1152 | if (target_machine.emitToFile(module, &error_message, &lowered_options)) { |
| 1157 | defer llvm.disposeMessage(error_message); | 1153 | defer bindings.disposeMessage(error_message); |
| 1158 | return diags.fail("LLVM failed to emit bin={s} ir={s}: {s}", .{ | 1154 | return diags.fail("LLVM failed to emit bin={s} ir={s}: {s}", .{ |
| 1159 | emit_bin_msg, post_llvm_ir_msg, error_message, | 1155 | emit_bin_msg, post_llvm_ir_msg, error_message, |
| 1160 | }); | 1156 | }); |
| ... | @@ -1170,7 +1166,7 @@ pub const Object = struct { | ... | @@ -1170,7 +1166,7 @@ pub const Object = struct { |
| 1170 | | 1166 | |
| 1171 | lowered_options.asm_filename = if (options.asm_path) |x| x.ptr else null; | 1167 | lowered_options.asm_filename = if (options.asm_path) |x| x.ptr else null; |
| 1172 | if (target_machine.emitToFile(module, &error_message, &lowered_options)) { | 1168 | if (target_machine.emitToFile(module, &error_message, &lowered_options)) { |
| 1173 | defer llvm.disposeMessage(error_message); | 1169 | defer bindings.disposeMessage(error_message); |
| 1174 | return diags.fail("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{ | 1170 | return diags.fail("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{ |
| 1175 | emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg, error_message, | 1171 | emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg, error_message, |
| 1176 | }); | 1172 | }); |
| ... | @@ -1201,13 +1197,6 @@ pub const Object = struct { | ... | @@ -1201,13 +1197,6 @@ pub const Object = struct { |
| 1201 | const fn_info = zcu.typeToFunc(fn_ty).?; | 1197 | const fn_info = zcu.typeToFunc(fn_ty).?; |
| 1202 | const target = &owner_mod.resolved_target.result; | 1198 | const target = &owner_mod.resolved_target.result; |
| 1203 | | 1199 | |
| 1204 | var ng: NavGen = .{ | | |
| 1205 | .object = o, | | |
| 1206 | .nav_index = func.owner_nav, | | |
| 1207 | .pt = pt, | | |
| 1208 | .err_msg = null, | | |
| 1209 | }; | | |
| 1210 | | | |
| 1211 | const function_index = try o.resolveLlvmFunction(pt, func.owner_nav); | 1200 | const function_index = try o.resolveLlvmFunction(pt, func.owner_nav); |
| 1212 | | 1201 | |
| 1213 | var attributes = try function_index.ptrConst(&o.builder).attributes.toWip(&o.builder); | 1202 | var attributes = try function_index.ptrConst(&o.builder).attributes.toWip(&o.builder); |
| ... | @@ -1293,9 +1282,7 @@ pub const Object = struct { | ... | @@ -1293,9 +1282,7 @@ pub const Object = struct { |
| 1293 | | 1282 | |
| 1294 | var llvm_arg_i: u32 = 0; | 1283 | var llvm_arg_i: u32 = 0; |
| 1295 | | 1284 | |
| 1296 | // This gets the LLVM values from the function and stores them in `ng.args`. | 1285 | const ret_ptr: Builder.Value = if (firstParamSRet(fn_info, zcu, target)) param: { |
| 1297 | const sret = firstParamSRet(fn_info, zcu, target); | | |
| 1298 | const ret_ptr: Builder.Value = if (sret) param: { | | |
| 1299 | const param = wip.arg(llvm_arg_i); | 1286 | const param = wip.arg(llvm_arg_i); |
| 1300 | llvm_arg_i += 1; | 1287 | llvm_arg_i += 1; |
| 1301 | break :param param; | 1288 | break :param param; |
| ... | @@ -1398,7 +1385,7 @@ pub const Object = struct { | ... | @@ -1398,7 +1385,7 @@ pub const Object = struct { |
| 1398 | const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); | 1385 | const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); |
| 1399 | const ptr_info = param_ty.ptrInfo(zcu); | 1386 | const ptr_info = param_ty.ptrInfo(zcu); |
| 1400 | | 1387 | |
| 1401 | if (math.cast(u5, it.zig_index - 1)) |i| { | 1388 | if (std.math.cast(u5, it.zig_index - 1)) |i| { |
| 1402 | if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) { | 1389 | if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) { |
| 1403 | try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder); | 1390 | try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder); |
| 1404 | } | 1391 | } |
| ... | @@ -1543,10 +1530,12 @@ pub const Object = struct { | ... | @@ -1543,10 +1530,12 @@ pub const Object = struct { |
| 1543 | }; | 1530 | }; |
| 1544 | | 1531 | |
| 1545 | var fg: FuncGen = .{ | 1532 | var fg: FuncGen = .{ |
| | 1533 | .object = o, |
| | 1534 | .nav_index = func.owner_nav, |
| | 1535 | .pt = pt, |
| 1546 | .gpa = gpa, | 1536 | .gpa = gpa, |
| 1547 | .air = air.*, | 1537 | .air = air.*, |
| 1548 | .liveness = liveness.*.?, | 1538 | .liveness = liveness.*.?, |
| 1549 | .ng = &ng, | | |
| 1550 | .wip = wip, | 1539 | .wip = wip, |
| 1551 | .is_naked = fn_info.cc == .naked, | 1540 | .is_naked = fn_info.cc == .naked, |
| 1552 | .fuzz = fuzz, | 1541 | .fuzz = fuzz, |
| ... | @@ -1561,20 +1550,19 @@ pub const Object = struct { | ... | @@ -1561,20 +1550,19 @@ pub const Object = struct { |
| 1561 | .sync_scope = if (owner_mod.single_threaded) .singlethread else .system, | 1550 | .sync_scope = if (owner_mod.single_threaded) .singlethread else .system, |
| 1562 | .file = file, | 1551 | .file = file, |
| 1563 | .scope = subprogram, | 1552 | .scope = subprogram, |
| | 1553 | .inlined_at = .none, |
| 1564 | .base_line = zcu.navSrcLine(func.owner_nav), | 1554 | .base_line = zcu.navSrcLine(func.owner_nav), |
| 1565 | .prev_dbg_line = 0, | 1555 | .prev_dbg_line = 0, |
| 1566 | .prev_dbg_column = 0, | 1556 | .prev_dbg_column = 0, |
| 1567 | .err_ret_trace = err_ret_trace, | 1557 | .err_ret_trace = err_ret_trace, |
| 1568 | .disable_intrinsics = disable_intrinsics, | 1558 | .disable_intrinsics = disable_intrinsics, |
| | 1559 | .allowzero_access = false, |
| 1569 | }; | 1560 | }; |
| 1570 | defer fg.deinit(); | 1561 | defer fg.deinit(); |
| 1571 | deinit_wip = false; | 1562 | deinit_wip = false; |
| 1572 | | 1563 | |
| 1573 | fg.genBody(air.getMainBody(), .poi) catch |err| switch (err) { | 1564 | fg.genBody(air.getMainBody(), .poi) catch |err| switch (err) { |
| 1574 | error.CodegenFail => switch (zcu.codegenFailMsg(func.owner_nav, ng.err_msg.?)) { | 1565 | error.CodegenFail => return, // MLUGG TODO |
| 1575 | error.CodegenFail => return, | | |
| 1576 | error.OutOfMemory => |e| return e, | | |
| 1577 | }, | | |
| 1578 | else => |e| return e, | 1566 | else => |e| return e, |
| 1579 | }; | 1567 | }; |
| 1580 | | 1568 | |
| ... | @@ -1618,15 +1606,142 @@ pub const Object = struct { | ... | @@ -1618,15 +1606,142 @@ pub const Object = struct { |
| 1618 | try o.flushTypePool(pt); | 1606 | try o.flushTypePool(pt); |
| 1619 | } | 1607 | } |
| 1620 | | 1608 | |
| 1621 | pub fn updateNav(self: *Object, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { | 1609 | pub fn updateNav(o: *Object, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { |
| 1622 | var ng: NavGen = .{ | 1610 | const zcu = pt.zcu; |
| 1623 | .object = self, | 1611 | const ip = &zcu.intern_pool; |
| 1624 | .nav_index = nav_index, | 1612 | |
| 1625 | .pt = pt, | 1613 | const nav = ip.getNav(nav_index); |
| 1626 | .err_msg = null, | 1614 | const resolved = nav.resolved.?; |
| | 1615 | |
| | 1616 | const lib_name, const linkage, const visibility: Builder.Visibility, const is_dll_import, const init_val, const owner_nav = switch (ip.indexToKey(resolved.value)) { |
| | 1617 | else => .{ .none, .internal, .default, false, resolved.value, nav_index }, |
| | 1618 | .@"extern" => |e| .{ e.lib_name, e.linkage, .fromSymbolVisibility(e.visibility), e.is_dll_import, .none, e.owner_nav }, |
| 1627 | }; | 1619 | }; |
| 1628 | try ng.genDecl(); | 1620 | const ty: Type = .fromInterned(nav.resolved.?.type); |
| 1629 | try self.flushTypePool(pt); | 1621 | |
| | 1622 | if (linkage != .internal and ip.isFunctionType(ty.toIntern())) { |
| | 1623 | const function_index = try o.resolveLlvmFunction(pt, owner_nav); |
| | 1624 | // Add parameter attributes which weren't set by `resolveLlvmFunction` |
| | 1625 | const fn_info = zcu.typeToFunc(ty).?; |
| | 1626 | var attributes = try function_index.ptrConst(&o.builder).attributes.toWip(&o.builder); |
| | 1627 | defer attributes.deinit(&o.builder); |
| | 1628 | var it = iterateParamTypes(o, pt, fn_info); |
| | 1629 | if (firstParamSRet(fn_info, zcu, zcu.getTarget())) it.llvm_index += 1; |
| | 1630 | if (fn_info.cc == .auto and zcu.comp.config.any_error_tracing) it.llvm_index += 1; |
| | 1631 | while (try it.next()) |lowering| switch (lowering) { |
| | 1632 | .byval => { |
| | 1633 | const param_index = it.zig_index - 1; |
| | 1634 | const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[param_index]); |
| | 1635 | if (!isByRef(param_ty, zcu)) { |
| | 1636 | try o.addByValParamAttrs(pt, &attributes, param_ty, param_index, fn_info, it.llvm_index - 1); |
| | 1637 | } |
| | 1638 | }, |
| | 1639 | .byref => { |
| | 1640 | const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); |
| | 1641 | const param_llvm_ty = try o.lowerType(pt, param_ty); |
| | 1642 | const alignment = param_ty.abiAlignment(zcu); |
| | 1643 | try o.addByRefParamAttrs(&attributes, it.llvm_index - 1, alignment.toLlvm(), it.byval_attr, param_llvm_ty); |
| | 1644 | }, |
| | 1645 | .byref_mut => try attributes.addParamAttr(it.llvm_index - 1, .noundef, &o.builder), |
| | 1646 | // No attributes needed for these. |
| | 1647 | .no_bits, |
| | 1648 | .abi_sized_int, |
| | 1649 | .multiple_llvm_types, |
| | 1650 | .float_array, |
| | 1651 | .i32_array, |
| | 1652 | .i64_array, |
| | 1653 | => continue, |
| | 1654 | |
| | 1655 | .slice => unreachable, // extern functions do not support slice types. |
| | 1656 | }; |
| | 1657 | function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); |
| | 1658 | } else { |
| | 1659 | const variable_index = try o.resolveGlobalNav(pt, nav_index); |
| | 1660 | variable_index.setAlignment(zcu.navAlignment(nav_index).toLlvm(), &o.builder); |
| | 1661 | if (resolved.@"linksection".toSlice(ip)) |section| |
| | 1662 | variable_index.setSection(try o.builder.string(section), &o.builder); |
| | 1663 | if (resolved.@"const") variable_index.setMutability(.constant, &o.builder); |
| | 1664 | try variable_index.setInitializer(switch (init_val) { |
| | 1665 | .none => .no_init, |
| | 1666 | else => try o.lowerValue(pt, init_val), |
| | 1667 | }, &o.builder); |
| | 1668 | variable_index.setVisibility(visibility, &o.builder); |
| | 1669 | |
| | 1670 | const file_scope = zcu.navFileScopeIndex(nav_index); |
| | 1671 | const mod = zcu.fileByIndex(file_scope).mod.?; |
| | 1672 | if (resolved.@"threadlocal" and !mod.single_threaded) |
| | 1673 | variable_index.setThreadLocal(.generaldynamic, &o.builder); |
| | 1674 | |
| | 1675 | const line_number = zcu.navSrcLine(nav_index) + 1; |
| | 1676 | |
| | 1677 | if (!mod.strip) { |
| | 1678 | const debug_file = try o.getDebugFile(pt, file_scope); |
| | 1679 | |
| | 1680 | const debug_global_var = try o.builder.debugGlobalVar( |
| | 1681 | try o.builder.metadataString(nav.name.toSlice(ip)), // Name |
| | 1682 | try o.builder.metadataStringFromStrtabString(variable_index.name(&o.builder)), // Linkage name |
| | 1683 | debug_file, // File |
| | 1684 | debug_file, // Scope |
| | 1685 | line_number, |
| | 1686 | try o.getDebugType(pt, ty), |
| | 1687 | variable_index, |
| | 1688 | .{ .local = linkage == .internal }, |
| | 1689 | ); |
| | 1690 | |
| | 1691 | const debug_expression = try o.builder.debugExpression(&.{}); |
| | 1692 | |
| | 1693 | const debug_global_var_expression = try o.builder.debugGlobalVarExpression( |
| | 1694 | debug_global_var, |
| | 1695 | debug_expression, |
| | 1696 | ); |
| | 1697 | |
| | 1698 | variable_index.setGlobalVariableExpression(debug_global_var_expression, &o.builder); |
| | 1699 | try o.debug_globals.append(o.gpa, debug_global_var_expression); |
| | 1700 | } |
| | 1701 | } |
| | 1702 | |
| | 1703 | switch (linkage) { |
| | 1704 | .internal => {}, |
| | 1705 | .strong, .weak => { |
| | 1706 | const global_index = o.nav_map.get(nav_index).?; |
| | 1707 | |
| | 1708 | const decl_name = decl_name: { |
| | 1709 | if (zcu.getTarget().cpu.arch.isWasm() and ty.zigTypeTag(zcu) == .@"fn") { |
| | 1710 | if (lib_name.toSlice(ip)) |lib_name_slice| { |
| | 1711 | if (!std.mem.eql(u8, lib_name_slice, "c")) { |
| | 1712 | break :decl_name try o.builder.strtabStringFmt("{f}|{s}", .{ nav.name.fmt(ip), lib_name_slice }); |
| | 1713 | } |
| | 1714 | } |
| | 1715 | } |
| | 1716 | break :decl_name try o.builder.strtabString(nav.name.toSlice(ip)); |
| | 1717 | }; |
| | 1718 | |
| | 1719 | if (o.builder.getGlobal(decl_name)) |other_global| { |
| | 1720 | if (other_global != global_index) { |
| | 1721 | // Another global already has this name; just use it in place of this global. |
| | 1722 | try global_index.replace(other_global, &o.builder); |
| | 1723 | return; |
| | 1724 | } |
| | 1725 | } |
| | 1726 | |
| | 1727 | try global_index.rename(decl_name, &o.builder); |
| | 1728 | global_index.setUnnamedAddr(.default, &o.builder); |
| | 1729 | if (is_dll_import) { |
| | 1730 | global_index.setDllStorageClass(.dllimport, &o.builder); |
| | 1731 | } else if (zcu.comp.config.dll_export_fns) { |
| | 1732 | global_index.setDllStorageClass(.default, &o.builder); |
| | 1733 | } |
| | 1734 | |
| | 1735 | global_index.setLinkage(switch (linkage) { |
| | 1736 | .internal => unreachable, |
| | 1737 | .strong => .external, |
| | 1738 | .weak => .extern_weak, |
| | 1739 | .link_once => unreachable, |
| | 1740 | }, &o.builder); |
| | 1741 | global_index.setVisibility(visibility, &o.builder); |
| | 1742 | }, |
| | 1743 | .link_once => unreachable, |
| | 1744 | } |
| 1630 | } | 1745 | } |
| 1631 | | 1746 | |
| 1632 | fn flushTypePool(o: *Object, pt: Zcu.PerThread) Allocator.Error!void { | 1747 | fn flushTypePool(o: *Object, pt: Zcu.PerThread) Allocator.Error!void { |
| ... | @@ -1904,7 +2019,7 @@ pub const Object = struct { | ... | @@ -1904,7 +2019,7 @@ pub const Object = struct { |
| 1904 | } | 2019 | } |
| 1905 | } | 2020 | } |
| 1906 | | 2021 | |
| 1907 | fn getDebugFile(o: *Object, pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.Error!Builder.Metadata { | 2022 | pub fn getDebugFile(o: *Object, pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.Error!Builder.Metadata { |
| 1908 | const gpa = o.gpa; | 2023 | const gpa = o.gpa; |
| 1909 | const gop = try o.debug_file_map.getOrPut(gpa, file_index); | 2024 | const gop = try o.debug_file_map.getOrPut(gpa, file_index); |
| 1910 | errdefer assert(o.debug_file_map.remove(file_index)); | 2025 | errdefer assert(o.debug_file_map.remove(file_index)); |
| ... | @@ -1920,7 +2035,7 @@ pub const Object = struct { | ... | @@ -1920,7 +2035,7 @@ pub const Object = struct { |
| 1920 | return gop.value_ptr.*; | 2035 | return gop.value_ptr.*; |
| 1921 | } | 2036 | } |
| 1922 | | 2037 | |
| 1923 | fn getDebugType(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error!Builder.Metadata { | 2038 | pub fn getDebugType(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error!Builder.Metadata { |
| 1924 | assert(!o.builder.strip); | 2039 | assert(!o.builder.strip); |
| 1925 | const index = try o.type_pool.get(pt, .{ .llvm = o }, ty.toIntern()); | 2040 | const index = try o.type_pool.get(pt, .{ .llvm = o }, ty.toIntern()); |
| 1926 | return o.debug_types.items[@intFromEnum(index)]; | 2041 | return o.debug_types.items[@intFromEnum(index)]; |
| ... | @@ -2212,13 +2327,14 @@ pub const Object = struct { | ... | @@ -2212,13 +2327,14 @@ pub const Object = struct { |
| 2212 | defer debug_param_types.deinit(gpa); | 2327 | defer debug_param_types.deinit(gpa); |
| 2213 | | 2328 | |
| 2214 | // Return type goes first. | 2329 | // Return type goes first. |
| 2215 | const sret = firstParamSRet(fn_info, zcu, target); | 2330 | if (firstParamSRet(fn_info, zcu, target)) { |
| 2216 | const ret_ty: Type = if (sret) .void else .fromInterned(fn_info.return_type); | 2331 | // Actual return type is void, then first arg is the sret pointer. |
| 2217 | debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, ret_ty)); | 2332 | const ptr_ty = try pt.singleMutPtrType(.fromInterned(fn_info.return_type)); |
| 2218 | | 2333 | debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, .void)); |
| 2219 | if (sret) { | | |
| 2220 | const ptr_ty = try pt.singleMutPtrType(Type.fromInterned(fn_info.return_type)); | | |
| 2221 | debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, ptr_ty)); | 2334 | debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, ptr_ty)); |
| | 2335 | } else { |
| | 2336 | const ret_ty: Type = .fromInterned(fn_info.return_type); |
| | 2337 | debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, ret_ty)); |
| 2222 | } | 2338 | } |
| 2223 | | 2339 | |
| 2224 | if (fn_info.cc == .auto and zcu.comp.config.any_error_tracing) { | 2340 | if (fn_info.cc == .auto and zcu.comp.config.any_error_tracing) { |
| ... | @@ -2661,7 +2777,7 @@ pub const Object = struct { | ... | @@ -2661,7 +2777,7 @@ pub const Object = struct { |
| 2661 | /// If the llvm function does not exist, create it. | 2777 | /// If the llvm function does not exist, create it. |
| 2662 | /// Note that this can be called before the function's semantic analysis has | 2778 | /// Note that this can be called before the function's semantic analysis has |
| 2663 | /// completed, so if any attributes rely on that, they must be done in updateFunc, not here. | 2779 | /// completed, so if any attributes rely on that, they must be done in updateFunc, not here. |
| 2664 | fn resolveLlvmFunction( | 2780 | pub fn resolveLlvmFunction( |
| 2665 | o: *Object, | 2781 | o: *Object, |
| 2666 | pt: Zcu.PerThread, | 2782 | pt: Zcu.PerThread, |
| 2667 | nav_index: InternPool.Nav.Index, | 2783 | nav_index: InternPool.Nav.Index, |
| ... | @@ -2677,7 +2793,6 @@ pub const Object = struct { | ... | @@ -2677,7 +2793,6 @@ pub const Object = struct { |
| 2677 | | 2793 | |
| 2678 | const fn_info = zcu.typeToFunc(ty).?; | 2794 | const fn_info = zcu.typeToFunc(ty).?; |
| 2679 | const target = &owner_mod.resolved_target.result; | 2795 | const target = &owner_mod.resolved_target.result; |
| 2680 | const sret = firstParamSRet(fn_info, zcu, target); | | |
| 2681 | | 2796 | |
| 2682 | const is_extern, const lib_name = if (nav.getExtern(ip)) |@"extern"| | 2797 | const is_extern, const lib_name = if (nav.getExtern(ip)) |@"extern"| |
| 2683 | .{ true, @"extern".lib_name } | 2798 | .{ true, @"extern".lib_name } |
| ... | @@ -2712,7 +2827,7 @@ pub const Object = struct { | ... | @@ -2712,7 +2827,7 @@ pub const Object = struct { |
| 2712 | } | 2827 | } |
| 2713 | | 2828 | |
| 2714 | var llvm_arg_i: u32 = 0; | 2829 | var llvm_arg_i: u32 = 0; |
| 2715 | if (sret) { | 2830 | if (firstParamSRet(fn_info, zcu, target)) { |
| 2716 | // Sret pointers must not be address 0 | 2831 | // Sret pointers must not be address 0 |
| 2717 | try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); | 2832 | try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); |
| 2718 | try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder); | 2833 | try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder); |
| ... | @@ -2988,11 +3103,11 @@ pub const Object = struct { | ... | @@ -2988,11 +3103,11 @@ pub const Object = struct { |
| 2988 | return variable_index; | 3103 | return variable_index; |
| 2989 | } | 3104 | } |
| 2990 | | 3105 | |
| 2991 | fn errorIntType(o: *Object, pt: Zcu.PerThread) Allocator.Error!Builder.Type { | 3106 | pub fn errorIntType(o: *Object, pt: Zcu.PerThread) Allocator.Error!Builder.Type { |
| 2992 | return o.builder.intType(pt.zcu.errorSetBits()); | 3107 | return o.builder.intType(pt.zcu.errorSetBits()); |
| 2993 | } | 3108 | } |
| 2994 | | 3109 | |
| 2995 | fn lowerType(o: *Object, pt: Zcu.PerThread, t: Type) Allocator.Error!Builder.Type { | 3110 | pub fn lowerType(o: *Object, pt: Zcu.PerThread, t: Type) Allocator.Error!Builder.Type { |
| 2996 | const zcu = pt.zcu; | 3111 | const zcu = pt.zcu; |
| 2997 | const target = zcu.getTarget(); | 3112 | const target = zcu.getTarget(); |
| 2998 | const ip = &zcu.intern_pool; | 3113 | const ip = &zcu.intern_pool; |
| ... | @@ -3408,7 +3523,7 @@ pub const Object = struct { | ... | @@ -3408,7 +3523,7 @@ pub const Object = struct { |
| 3408 | return gop.value_ptr.*; | 3523 | return gop.value_ptr.*; |
| 3409 | }, | 3524 | }, |
| 3410 | .enum_type => try o.lowerType(pt, t.intTagType(zcu)), | 3525 | .enum_type => try o.lowerType(pt, t.intTagType(zcu)), |
| 3411 | .func_type => |func_type| try o.lowerTypeFn(pt, func_type), | 3526 | .func_type => |func_type| try o.lowerFnType(pt, func_type), |
| 3412 | .error_set_type, .inferred_error_set_type => try o.errorIntType(pt), | 3527 | .error_set_type, .inferred_error_set_type => try o.errorIntType(pt), |
| 3413 | // values, not types | 3528 | // values, not types |
| 3414 | .undef, | 3529 | .undef, |
| ... | @@ -3434,7 +3549,7 @@ pub const Object = struct { | ... | @@ -3434,7 +3549,7 @@ pub const Object = struct { |
| 3434 | }; | 3549 | }; |
| 3435 | } | 3550 | } |
| 3436 | | 3551 | |
| 3437 | fn lowerTypeFn(o: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.FuncType) Allocator.Error!Builder.Type { | 3552 | fn lowerFnType(o: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.FuncType) Allocator.Error!Builder.Type { |
| 3438 | const zcu = pt.zcu; | 3553 | const zcu = pt.zcu; |
| 3439 | const ip = &zcu.intern_pool; | 3554 | const ip = &zcu.intern_pool; |
| 3440 | const target = zcu.getTarget(); | 3555 | const target = zcu.getTarget(); |
| ... | @@ -3500,7 +3615,7 @@ pub const Object = struct { | ... | @@ -3500,7 +3615,7 @@ pub const Object = struct { |
| 3500 | ); | 3615 | ); |
| 3501 | } | 3616 | } |
| 3502 | | 3617 | |
| 3503 | fn lowerValue(o: *Object, pt: Zcu.PerThread, arg_val: InternPool.Index) Allocator.Error!Builder.Constant { | 3618 | pub fn lowerValue(o: *Object, pt: Zcu.PerThread, arg_val: InternPool.Index) Allocator.Error!Builder.Constant { |
| 3504 | const zcu = pt.zcu; | 3619 | const zcu = pt.zcu; |
| 3505 | const ip = &zcu.intern_pool; | 3620 | const ip = &zcu.intern_pool; |
| 3506 | const target = zcu.getTarget(); | 3621 | const target = zcu.getTarget(); |
| ... | @@ -4076,7 +4191,7 @@ pub const Object = struct { | ... | @@ -4076,7 +4191,7 @@ pub const Object = struct { |
| 4076 | | 4191 | |
| 4077 | /// This logic is very similar to `lowerNavRefValue` but for anonymous declarations. | 4192 | /// This logic is very similar to `lowerNavRefValue` but for anonymous declarations. |
| 4078 | /// Maybe the logic could be unified. | 4193 | /// Maybe the logic could be unified. |
| 4079 | fn lowerUavRef( | 4194 | pub fn lowerUavRef( |
| 4080 | o: *Object, | 4195 | o: *Object, |
| 4081 | pt: Zcu.PerThread, | 4196 | pt: Zcu.PerThread, |
| 4082 | uav: InternPool.Key.Ptr.BaseAddr.Uav, | 4197 | uav: InternPool.Key.Ptr.BaseAddr.Uav, |
| ... | @@ -4113,7 +4228,7 @@ pub const Object = struct { | ... | @@ -4113,7 +4228,7 @@ pub const Object = struct { |
| 4113 | return o.builder.convConst(llvm_val, try o.lowerType(pt, ptr_ty)); | 4228 | return o.builder.convConst(llvm_val, try o.lowerType(pt, ptr_ty)); |
| 4114 | } | 4229 | } |
| 4115 | | 4230 | |
| 4116 | fn lowerNavRefValue(o: *Object, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Allocator.Error!Builder.Constant { | 4231 | pub fn lowerNavRefValue(o: *Object, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Allocator.Error!Builder.Constant { |
| 4117 | const zcu = pt.zcu; | 4232 | const zcu = pt.zcu; |
| 4118 | const ip = &zcu.intern_pool; | 4233 | const ip = &zcu.intern_pool; |
| 4119 | | 4234 | |
| ... | @@ -4139,7 +4254,7 @@ pub const Object = struct { | ... | @@ -4139,7 +4254,7 @@ pub const Object = struct { |
| 4139 | return o.builder.convConst(llvm_val, try o.lowerType(pt, ptr_ty)); | 4254 | return o.builder.convConst(llvm_val, try o.lowerType(pt, ptr_ty)); |
| 4140 | } | 4255 | } |
| 4141 | | 4256 | |
| 4142 | fn lowerPtrToVoid(o: *Object, pt: Zcu.PerThread, ptr_ty: Type) Allocator.Error!Builder.Constant { | 4257 | pub fn lowerPtrToVoid(o: *Object, pt: Zcu.PerThread, ptr_ty: Type) Allocator.Error!Builder.Constant { |
| 4143 | const zcu = pt.zcu; | 4258 | const zcu = pt.zcu; |
| 4144 | // Even though we are pointing at something which has zero bits (e.g. `void`), | 4259 | // Even though we are pointing at something which has zero bits (e.g. `void`), |
| 4145 | // Pointers are defined to have bits. So we must return something here. | 4260 | // Pointers are defined to have bits. So we must return something here. |
| ... | @@ -4163,30 +4278,7 @@ pub const Object = struct { | ... | @@ -4163,30 +4278,7 @@ pub const Object = struct { |
| 4163 | return o.builder.castConst(.inttoptr, try o.builder.intConst(llvm_usize, int), llvm_ptr_ty); | 4278 | return o.builder.castConst(.inttoptr, try o.builder.intConst(llvm_usize, int), llvm_ptr_ty); |
| 4164 | } | 4279 | } |
| 4165 | | 4280 | |
| 4166 | /// If the operand type of an atomic operation is not byte sized we need to | 4281 | pub fn addByValParamAttrs( |
| 4167 | /// widen it before using it and then truncate the result. | | |
| 4168 | /// RMW exchange of floating-point values is bitcasted to same-sized integer | | |
| 4169 | /// types to work around a LLVM deficiency when targeting ARM/AArch64. | | |
| 4170 | fn getAtomicAbiType(o: *Object, pt: Zcu.PerThread, ty: Type, is_rmw_xchg: bool) Allocator.Error!Builder.Type { | | |
| 4171 | const zcu = pt.zcu; | | |
| 4172 | switch (ty.zigTypeTag(zcu)) { | | |
| 4173 | .int, .@"enum", .@"struct", .@"union" => {}, | | |
| 4174 | .float => { | | |
| 4175 | if (!is_rmw_xchg) return .none; | | |
| 4176 | return o.builder.intType(@intCast(ty.abiSize(zcu) * 8)); | | |
| 4177 | }, | | |
| 4178 | .bool => return .i8, | | |
| 4179 | else => return .none, | | |
| 4180 | } | | |
| 4181 | const bit_count = ty.bitSize(zcu); | | |
| 4182 | if (!std.math.isPowerOfTwo(bit_count) or (bit_count % 8) != 0) { | | |
| 4183 | return o.builder.intType(@intCast(ty.abiSize(zcu) * 8)); | | |
| 4184 | } else { | | |
| 4185 | return .none; | | |
| 4186 | } | | |
| 4187 | } | | |
| 4188 | | | |
| 4189 | fn addByValParamAttrs( | | |
| 4190 | o: *Object, | 4282 | o: *Object, |
| 4191 | pt: Zcu.PerThread, | 4283 | pt: Zcu.PerThread, |
| 4192 | attributes: *Builder.FunctionAttributes.Wip, | 4284 | attributes: *Builder.FunctionAttributes.Wip, |
| ... | @@ -4198,7 +4290,7 @@ pub const Object = struct { | ... | @@ -4198,7 +4290,7 @@ pub const Object = struct { |
| 4198 | const zcu = pt.zcu; | 4290 | const zcu = pt.zcu; |
| 4199 | if (param_ty.isPtrAtRuntime(zcu)) { | 4291 | if (param_ty.isPtrAtRuntime(zcu)) { |
| 4200 | const ptr_info = param_ty.ptrInfo(zcu); | 4292 | const ptr_info = param_ty.ptrInfo(zcu); |
| 4201 | if (math.cast(u5, param_index)) |i| { | 4293 | if (std.math.cast(u5, param_index)) |i| { |
| 4202 | if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) { | 4294 | if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) { |
| 4203 | try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder); | 4295 | try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder); |
| 4204 | } | 4296 | } |
| ... | @@ -4232,7 +4324,7 @@ pub const Object = struct { | ... | @@ -4232,7 +4324,7 @@ pub const Object = struct { |
| 4232 | }; | 4324 | }; |
| 4233 | } | 4325 | } |
| 4234 | | 4326 | |
| 4235 | fn addByRefParamAttrs( | 4327 | pub fn addByRefParamAttrs( |
| 4236 | o: *Object, | 4328 | o: *Object, |
| 4237 | attributes: *Builder.FunctionAttributes.Wip, | 4329 | attributes: *Builder.FunctionAttributes.Wip, |
| 4238 | llvm_arg_i: u32, | 4330 | llvm_arg_i: u32, |
| ... | @@ -4246,14 +4338,15 @@ pub const Object = struct { | ... | @@ -4246,14 +4338,15 @@ pub const Object = struct { |
| 4246 | if (byval) try attributes.addParamAttr(llvm_arg_i, .{ .byval = param_llvm_ty }, &o.builder); | 4338 | if (byval) try attributes.addParamAttr(llvm_arg_i, .{ .byval = param_llvm_ty }, &o.builder); |
| 4247 | } | 4339 | } |
| 4248 | | 4340 | |
| 4249 | fn llvmFieldIndex(o: *Object, struct_ty: Type, field_index: usize) ?c_uint { | 4341 | pub fn llvmFieldIndex(o: *Object, struct_ty: Type, field_index: usize) ?c_uint { |
| 4250 | return o.struct_field_map.get(.{ | 4342 | return o.struct_field_map.get(.{ |
| 4251 | .struct_ty = struct_ty.toIntern(), | 4343 | .struct_ty = struct_ty.toIntern(), |
| 4252 | .field_index = @intCast(field_index), | 4344 | .field_index = @intCast(field_index), |
| 4253 | }); | 4345 | }); |
| 4254 | } | 4346 | } |
| 4255 | | 4347 | |
| 4256 | fn getCmpLtErrorsLenFunction(o: *Object, pt: Zcu.PerThread) !Builder.Function.Index { | 4348 | /// MLUGG TODO: this is super dumb |
| | 4349 | pub fn getCmpLtErrorsLenFunction(o: *Object, pt: Zcu.PerThread) !Builder.Function.Index { |
| 4257 | const name = try o.builder.strtabString(lt_errors_fn_name); | 4350 | const name = try o.builder.strtabString(lt_errors_fn_name); |
| 4258 | if (o.builder.getGlobal(name)) |llvm_fn| return llvm_fn.ptrConst(&o.builder).kind.function; | 4351 | if (o.builder.getGlobal(name)) |llvm_fn| return llvm_fn.ptrConst(&o.builder).kind.function; |
| 4259 | | 4352 | |
| ... | @@ -4275,7 +4368,7 @@ pub const Object = struct { | ... | @@ -4275,7 +4368,7 @@ pub const Object = struct { |
| 4275 | return function_index; | 4368 | return function_index; |
| 4276 | } | 4369 | } |
| 4277 | | 4370 | |
| 4278 | fn getEnumTagNameFunction(o: *Object, pt: Zcu.PerThread, enum_ty: Type) !Builder.Function.Index { | 4371 | pub fn getEnumTagNameFunction(o: *Object, pt: Zcu.PerThread, enum_ty: Type) !Builder.Function.Index { |
| 4279 | const zcu = pt.zcu; | 4372 | const zcu = pt.zcu; |
| 4280 | const ip = &zcu.intern_pool; | 4373 | const ip = &zcu.intern_pool; |
| 4281 | const enum_type = ip.loadEnumType(enum_ty.toIntern()); | 4374 | const enum_type = ip.loadEnumType(enum_ty.toIntern()); |
| ... | @@ -4353,12 +4446,12 @@ pub const Object = struct { | ... | @@ -4353,12 +4446,12 @@ pub const Object = struct { |
| 4353 | return function_index; | 4446 | return function_index; |
| 4354 | } | 4447 | } |
| 4355 | | 4448 | |
| 4356 | fn lazyAbiAlignment(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error!Builder.Alignment.Lazy { | 4449 | pub fn lazyAbiAlignment(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error!Builder.Alignment.Lazy { |
| 4357 | const index = try o.type_pool.get(pt, .{ .llvm = o }, ty.toIntern()); | 4450 | const index = try o.type_pool.get(pt, .{ .llvm = o }, ty.toIntern()); |
| 4358 | return o.lazy_abi_aligns.items[@intFromEnum(index)]; | 4451 | return o.lazy_abi_aligns.items[@intFromEnum(index)]; |
| 4359 | } | 4452 | } |
| 4360 | | 4453 | |
| 4361 | fn updateIsNamedEnumValueFunction( | 4454 | pub fn updateIsNamedEnumValueFunction( |
| 4362 | o: *Object, | 4455 | o: *Object, |
| 4363 | pt: Zcu.PerThread, | 4456 | pt: Zcu.PerThread, |
| 4364 | enum_ty: Type, | 4457 | enum_ty: Type, |
| ... | @@ -4411,7248 +4504,104 @@ pub const Object = struct { | ... | @@ -4411,7248 +4504,104 @@ pub const Object = struct { |
| 4411 | } | 4504 | } |
| 4412 | }; | 4505 | }; |
| 4413 | | 4506 | |
| 4414 | pub const NavGen = struct { | 4507 | const CallingConventionInfo = struct { |
| 4415 | object: *Object, | 4508 | /// The LLVM calling convention to use. |
| 4416 | nav_index: InternPool.Nav.Index, | 4509 | llvm_cc: Builder.CallConv, |
| 4417 | pt: Zcu.PerThread, | 4510 | /// Whether to use an `alignstack` attribute to forcibly re-align the stack pointer in the function's prologue. |
| 4418 | err_msg: ?*Zcu.ErrorMsg, | 4511 | align_stack: bool, |
| | 4512 | /// Whether the function needs a `naked` attribute. |
| | 4513 | naked: bool, |
| | 4514 | /// How many leading parameters to apply the `inreg` attribute to. |
| | 4515 | inreg_param_count: u2 = 0, |
| | 4516 | }; |
| 4419 | | 4517 | |
| 4420 | fn ownerModule(ng: NavGen) *Package.Module { | 4518 | pub fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: *const std.Target) ?CallingConventionInfo { |
| 4421 | return ng.pt.zcu.navFileScope(ng.nav_index).mod.?; | 4519 | const llvm_cc = toLlvmCallConvTag(cc, target) orelse return null; |
| | 4520 | const incoming_stack_alignment: ?u64, const register_params: u2 = switch (cc) { |
| | 4521 | inline else => |pl| switch (@TypeOf(pl)) { |
| | 4522 | void => .{ null, 0 }, |
| | 4523 | std.builtin.CallingConvention.ArcInterruptOptions, |
| | 4524 | std.builtin.CallingConvention.ArmInterruptOptions, |
| | 4525 | std.builtin.CallingConvention.RiscvInterruptOptions, |
| | 4526 | std.builtin.CallingConvention.ShInterruptOptions, |
| | 4527 | std.builtin.CallingConvention.MicroblazeInterruptOptions, |
| | 4528 | std.builtin.CallingConvention.MipsInterruptOptions, |
| | 4529 | std.builtin.CallingConvention.CommonOptions, |
| | 4530 | => .{ pl.incoming_stack_alignment, 0 }, |
| | 4531 | std.builtin.CallingConvention.X86RegparmOptions => .{ pl.incoming_stack_alignment, pl.register_params }, |
| | 4532 | else => @compileError("TODO: toLlvmCallConv" ++ @tagName(pl)), |
| | 4533 | }, |
| | 4534 | }; |
| | 4535 | return .{ |
| | 4536 | .llvm_cc = llvm_cc, |
| | 4537 | .align_stack = if (incoming_stack_alignment) |a| need_align: { |
| | 4538 | const normal_stack_align = target.stackAlignment(); |
| | 4539 | break :need_align a < normal_stack_align; |
| | 4540 | } else false, |
| | 4541 | .naked = cc == .naked, |
| | 4542 | .inreg_param_count = register_params, |
| | 4543 | }; |
| | 4544 | } |
| | 4545 | pub fn toLlvmCallConvTag(cc_tag: std.builtin.CallingConvention.Tag, target: *const std.Target) ?Builder.CallConv { |
| | 4546 | if (target.cCallingConvention()) |default_c| { |
| | 4547 | if (cc_tag == default_c) { |
| | 4548 | return .ccc; |
| | 4549 | } |
| 4422 | } | 4550 | } |
| | 4551 | return switch (cc_tag) { |
| | 4552 | .@"inline" => unreachable, |
| | 4553 | .auto, .async => .fastcc, |
| | 4554 | .naked => .ccc, |
| | 4555 | .x86_64_sysv => .x86_64_sysvcc, |
| | 4556 | .x86_64_win => .win64cc, |
| | 4557 | .x86_64_regcall_v3_sysv => if (target.cpu.arch == .x86_64 and target.os.tag != .windows) |
| | 4558 | .x86_regcallcc |
| | 4559 | else |
| | 4560 | null, |
| | 4561 | .x86_64_regcall_v4_win => if (target.cpu.arch == .x86_64 and target.os.tag == .windows) |
| | 4562 | .x86_regcallcc // we use the "RegCallv4" module flag to make this correct |
| | 4563 | else |
| | 4564 | null, |
| | 4565 | .x86_64_vectorcall => .x86_vectorcallcc, |
| | 4566 | .x86_64_interrupt => .x86_intrcc, |
| | 4567 | .x86_stdcall => .x86_stdcallcc, |
| | 4568 | .x86_fastcall => .x86_fastcallcc, |
| | 4569 | .x86_thiscall => .x86_thiscallcc, |
| | 4570 | .x86_regcall_v3 => if (target.cpu.arch == .x86 and target.os.tag != .windows) |
| | 4571 | .x86_regcallcc |
| | 4572 | else |
| | 4573 | null, |
| | 4574 | .x86_regcall_v4_win => if (target.cpu.arch == .x86 and target.os.tag == .windows) |
| | 4575 | .x86_regcallcc // we use the "RegCallv4" module flag to make this correct |
| | 4576 | else |
| | 4577 | null, |
| | 4578 | .x86_vectorcall => .x86_vectorcallcc, |
| | 4579 | .x86_interrupt => .x86_intrcc, |
| | 4580 | .aarch64_vfabi => .aarch64_vector_pcs, |
| | 4581 | .aarch64_vfabi_sve => .aarch64_sve_vector_pcs, |
| | 4582 | .arm_aapcs => .arm_aapcscc, |
| | 4583 | .arm_aapcs_vfp => .arm_aapcs_vfpcc, |
| | 4584 | .riscv64_lp64_v => .riscv_vectorcallcc, |
| | 4585 | .riscv32_ilp32_v => .riscv_vectorcallcc, |
| | 4586 | .avr_builtin => .avr_builtincc, |
| | 4587 | .avr_signal => .avr_signalcc, |
| | 4588 | .avr_interrupt => .avr_intrcc, |
| | 4589 | .m68k_rtd => .m68k_rtdcc, |
| | 4590 | .m68k_interrupt => .m68k_intrcc, |
| | 4591 | .msp430_interrupt => .msp430_intrcc, |
| | 4592 | .amdgcn_kernel => .amdgpu_kernel, |
| | 4593 | .amdgcn_cs => .amdgpu_cs, |
| | 4594 | .nvptx_device => .ptx_device, |
| | 4595 | .nvptx_kernel => .ptx_kernel, |
| 4423 | | 4596 | |
| 4424 | fn todo(ng: *NavGen, comptime format: []const u8, args: anytype) Error { | 4597 | // Calling conventions which LLVM uses function attributes for. |
| 4425 | @branchHint(.cold); | 4598 | .riscv64_interrupt, |
| 4426 | assert(ng.err_msg == null); | 4599 | .riscv32_interrupt, |
| 4427 | const o = ng.object; | 4600 | .arm_interrupt, |
| 4428 | const gpa = o.gpa; | 4601 | .mips64_interrupt, |
| 4429 | const src_loc = ng.pt.zcu.navSrcLoc(ng.nav_index); | 4602 | .mips_interrupt, |
| 4430 | ng.err_msg = try Zcu.ErrorMsg.create(gpa, src_loc, "TODO (LLVM): " ++ format, args); | 4603 | .csky_interrupt, |
| 4431 | return error.CodegenFail; | 4604 | => .ccc, |
| 4432 | } | | |
| 4433 | | | |
| 4434 | fn genDecl(ng: *NavGen) !void { | | |
| 4435 | const o = ng.object; | | |
| 4436 | const pt = ng.pt; | | |
| 4437 | const zcu = pt.zcu; | | |
| 4438 | const ip = &zcu.intern_pool; | | |
| 4439 | const nav_index = ng.nav_index; | | |
| 4440 | const nav = ip.getNav(nav_index); | | |
| 4441 | const resolved = nav.resolved.?; | | |
| 4442 | | | |
| 4443 | const lib_name, const linkage, const visibility: Builder.Visibility, const is_dll_import, const init_val, const owner_nav = switch (ip.indexToKey(resolved.value)) { | | |
| 4444 | else => .{ .none, .internal, .default, false, resolved.value, nav_index }, | | |
| 4445 | .@"extern" => |e| .{ e.lib_name, e.linkage, .fromSymbolVisibility(e.visibility), e.is_dll_import, .none, e.owner_nav }, | | |
| 4446 | }; | | |
| 4447 | const ty: Type = .fromInterned(nav.resolved.?.type); | | |
| 4448 | | | |
| 4449 | if (linkage != .internal and ip.isFunctionType(ty.toIntern())) { | | |
| 4450 | const function_index = try o.resolveLlvmFunction(pt, owner_nav); | | |
| 4451 | // Add parameter attributes which weren't set by `resolveLlvmFunction` | | |
| 4452 | const fn_info = zcu.typeToFunc(ty).?; | | |
| 4453 | var attributes = try function_index.ptrConst(&o.builder).attributes.toWip(&o.builder); | | |
| 4454 | defer attributes.deinit(&o.builder); | | |
| 4455 | var it = iterateParamTypes(o, pt, fn_info); | | |
| 4456 | if (firstParamSRet(fn_info, zcu, zcu.getTarget())) it.llvm_index += 1; | | |
| 4457 | if (fn_info.cc == .auto and zcu.comp.config.any_error_tracing) it.llvm_index += 1; | | |
| 4458 | while (try it.next()) |lowering| switch (lowering) { | | |
| 4459 | .byval => { | | |
| 4460 | const param_index = it.zig_index - 1; | | |
| 4461 | const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[param_index]); | | |
| 4462 | if (!isByRef(param_ty, zcu)) { | | |
| 4463 | try o.addByValParamAttrs(pt, &attributes, param_ty, param_index, fn_info, it.llvm_index - 1); | | |
| 4464 | } | | |
| 4465 | }, | | |
| 4466 | .byref => { | | |
| 4467 | const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); | | |
| 4468 | const param_llvm_ty = try o.lowerType(pt, param_ty); | | |
| 4469 | const alignment = param_ty.abiAlignment(zcu); | | |
| 4470 | try o.addByRefParamAttrs(&attributes, it.llvm_index - 1, alignment.toLlvm(), it.byval_attr, param_llvm_ty); | | |
| 4471 | }, | | |
| 4472 | .byref_mut => try attributes.addParamAttr(it.llvm_index - 1, .noundef, &o.builder), | | |
| 4473 | // No attributes needed for these. | | |
| 4474 | .no_bits, | | |
| 4475 | .abi_sized_int, | | |
| 4476 | .multiple_llvm_types, | | |
| 4477 | .float_array, | | |
| 4478 | .i32_array, | | |
| 4479 | .i64_array, | | |
| 4480 | => continue, | | |
| 4481 | | | |
| 4482 | .slice => unreachable, // extern functions do not support slice types. | | |
| 4483 | }; | | |
| 4484 | function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); | | |
| 4485 | } else { | | |
| 4486 | const variable_index = try o.resolveGlobalNav(pt, nav_index); | | |
| 4487 | variable_index.setAlignment(zcu.navAlignment(nav_index).toLlvm(), &o.builder); | | |
| 4488 | if (resolved.@"linksection".toSlice(ip)) |section| | | |
| 4489 | variable_index.setSection(try o.builder.string(section), &o.builder); | | |
| 4490 | if (resolved.@"const") variable_index.setMutability(.constant, &o.builder); | | |
| 4491 | try variable_index.setInitializer(switch (init_val) { | | |
| 4492 | .none => .no_init, | | |
| 4493 | else => try o.lowerValue(pt, init_val), | | |
| 4494 | }, &o.builder); | | |
| 4495 | variable_index.setVisibility(visibility, &o.builder); | | |
| 4496 | | | |
| 4497 | const file_scope = zcu.navFileScopeIndex(nav_index); | | |
| 4498 | const mod = zcu.fileByIndex(file_scope).mod.?; | | |
| 4499 | if (resolved.@"threadlocal" and !mod.single_threaded) | | |
| 4500 | variable_index.setThreadLocal(.generaldynamic, &o.builder); | | |
| 4501 | | | |
| 4502 | const line_number = zcu.navSrcLine(nav_index) + 1; | | |
| 4503 | | | |
| 4504 | if (!mod.strip) { | | |
| 4505 | const debug_file = try o.getDebugFile(pt, file_scope); | | |
| 4506 | | | |
| 4507 | const debug_global_var = try o.builder.debugGlobalVar( | | |
| 4508 | try o.builder.metadataString(nav.name.toSlice(ip)), // Name | | |
| 4509 | try o.builder.metadataStringFromStrtabString(variable_index.name(&o.builder)), // Linkage name | | |
| 4510 | debug_file, // File | | |
| 4511 | debug_file, // Scope | | |
| 4512 | line_number, | | |
| 4513 | try o.getDebugType(pt, ty), | | |
| 4514 | variable_index, | | |
| 4515 | .{ .local = linkage == .internal }, | | |
| 4516 | ); | | |
| 4517 | | | |
| 4518 | const debug_expression = try o.builder.debugExpression(&.{}); | | |
| 4519 | | | |
| 4520 | const debug_global_var_expression = try o.builder.debugGlobalVarExpression( | | |
| 4521 | debug_global_var, | | |
| 4522 | debug_expression, | | |
| 4523 | ); | | |
| 4524 | | | |
| 4525 | variable_index.setGlobalVariableExpression(debug_global_var_expression, &o.builder); | | |
| 4526 | try o.debug_globals.append(o.gpa, debug_global_var_expression); | | |
| 4527 | } | | |
| 4528 | } | | |
| 4529 | | | |
| 4530 | switch (linkage) { | | |
| 4531 | .internal => {}, | | |
| 4532 | .strong, .weak => { | | |
| 4533 | const global_index = o.nav_map.get(nav_index).?; | | |
| 4534 | | | |
| 4535 | const decl_name = decl_name: { | | |
| 4536 | if (zcu.getTarget().cpu.arch.isWasm() and ty.zigTypeTag(zcu) == .@"fn") { | | |
| 4537 | if (lib_name.toSlice(ip)) |lib_name_slice| { | | |
| 4538 | if (!std.mem.eql(u8, lib_name_slice, "c")) { | | |
| 4539 | break :decl_name try o.builder.strtabStringFmt("{f}|{s}", .{ nav.name.fmt(ip), lib_name_slice }); | | |
| 4540 | } | | |
| 4541 | } | | |
| 4542 | } | | |
| 4543 | break :decl_name try o.builder.strtabString(nav.name.toSlice(ip)); | | |
| 4544 | }; | | |
| 4545 | | | |
| 4546 | if (o.builder.getGlobal(decl_name)) |other_global| { | | |
| 4547 | if (other_global != global_index) { | | |
| 4548 | // Another global already has this name; just use it in place of this global. | | |
| 4549 | try global_index.replace(other_global, &o.builder); | | |
| 4550 | return; | | |
| 4551 | } | | |
| 4552 | } | | |
| 4553 | | | |
| 4554 | try global_index.rename(decl_name, &o.builder); | | |
| 4555 | global_index.setUnnamedAddr(.default, &o.builder); | | |
| 4556 | if (is_dll_import) { | | |
| 4557 | global_index.setDllStorageClass(.dllimport, &o.builder); | | |
| 4558 | } else if (zcu.comp.config.dll_export_fns) { | | |
| 4559 | global_index.setDllStorageClass(.default, &o.builder); | | |
| 4560 | } | | |
| 4561 | | | |
| 4562 | global_index.setLinkage(switch (linkage) { | | |
| 4563 | .internal => unreachable, | | |
| 4564 | .strong => .external, | | |
| 4565 | .weak => .extern_weak, | | |
| 4566 | .link_once => unreachable, | | |
| 4567 | }, &o.builder); | | |
| 4568 | global_index.setVisibility(visibility, &o.builder); | | |
| 4569 | }, | | |
| 4570 | .link_once => unreachable, | | |
| 4571 | } | | |
| 4572 | } | | |
| 4573 | }; | | |
| 4574 | | | |
| 4575 | pub const FuncGen = struct { | | |
| 4576 | gpa: Allocator, | | |
| 4577 | ng: *NavGen, | | |
| 4578 | air: Air, | | |
| 4579 | liveness: Air.Liveness, | | |
| 4580 | wip: Builder.WipFunction, | | |
| 4581 | is_naked: bool, | | |
| 4582 | fuzz: ?Fuzz, | | |
| 4583 | | | |
| 4584 | file: Builder.Metadata, | | |
| 4585 | scope: Builder.Metadata, | | |
| 4586 | | | |
| 4587 | inlined_at: Builder.Metadata.Optional = .none, | | |
| 4588 | | | |
| 4589 | base_line: u32, | | |
| 4590 | prev_dbg_line: c_uint, | | |
| 4591 | prev_dbg_column: c_uint, | | |
| 4592 | | | |
| 4593 | /// This stores the LLVM values used in a function, such that they can be referred to | | |
| 4594 | /// in other instructions. This table is cleared before every function is generated. | | |
| 4595 | func_inst_table: std.AutoHashMapUnmanaged(Air.Inst.Ref, Builder.Value), | | |
| 4596 | | | |
| 4597 | /// If the return type is sret, this is the result pointer. Otherwise null. | | |
| 4598 | /// Note that this can disagree with isByRef for the return type in the case | | |
| 4599 | /// of C ABI functions. | | |
| 4600 | ret_ptr: Builder.Value, | | |
| 4601 | /// Any function that needs to perform Valgrind client requests needs an array alloca | | |
| 4602 | /// instruction, however a maximum of one per function is needed. | | |
| 4603 | valgrind_client_request_array: Builder.Value = .none, | | |
| 4604 | /// These fields are used to refer to the LLVM value of the function parameters | | |
| 4605 | /// in an Arg instruction. | | |
| 4606 | /// This list may be shorter than the list according to the zig type system; | | |
| 4607 | /// it omits 0-bit types. If the function uses sret as the first parameter, | | |
| 4608 | /// this slice does not include it. | | |
| 4609 | args: []const Builder.Value, | | |
| 4610 | arg_index: u32, | | |
| 4611 | arg_inline_index: u32, | | |
| 4612 | | | |
| 4613 | err_ret_trace: Builder.Value = .none, | | |
| 4614 | | | |
| 4615 | /// This data structure is used to implement breaking to blocks. | | |
| 4616 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, struct { | | |
| 4617 | parent_bb: Builder.Function.Block.Index, | | |
| 4618 | breaks: *BreakList, | | |
| 4619 | }), | | |
| 4620 | | | |
| 4621 | /// Maps `loop` instructions to the bb to branch to to repeat the loop. | | |
| 4622 | loops: std.AutoHashMapUnmanaged(Air.Inst.Index, Builder.Function.Block.Index), | | |
| 4623 | | | |
| 4624 | /// Maps `loop_switch_br` instructions to the information required to lower | | |
| 4625 | /// dispatches (`switch_dispatch` instructions). | | |
| 4626 | switch_dispatch_info: std.AutoHashMapUnmanaged(Air.Inst.Index, SwitchDispatchInfo), | | |
| 4627 | | | |
| 4628 | sync_scope: Builder.SyncScope, | | |
| 4629 | | | |
| 4630 | disable_intrinsics: bool, | | |
| 4631 | | | |
| 4632 | /// Have we seen loads or stores involving `allowzero` pointers? | | |
| 4633 | allowzero_access: bool = false, | | |
| 4634 | | | |
| 4635 | fn maybeMarkAllowZeroAccess(self: *FuncGen, info: InternPool.Key.PtrType) void { | | |
| 4636 | // LLVM already considers null pointers to be valid in non-generic address spaces, so avoid | | |
| 4637 | // pessimizing optimization for functions with accesses to such pointers. | | |
| 4638 | if (info.flags.address_space == .generic and info.flags.is_allowzero) self.allowzero_access = true; | | |
| 4639 | } | | |
| 4640 | | | |
| 4641 | const Fuzz = struct { | | |
| 4642 | counters_variable: Builder.Variable.Index, | | |
| 4643 | pcs: std.ArrayList(Builder.Constant), | | |
| 4644 | | | |
| 4645 | fn deinit(f: *Fuzz, gpa: Allocator) void { | | |
| 4646 | f.pcs.deinit(gpa); | | |
| 4647 | f.* = undefined; | | |
| 4648 | } | | |
| 4649 | }; | | |
| 4650 | | | |
| 4651 | const SwitchDispatchInfo = struct { | | |
| 4652 | /// These are the blocks corresponding to each switch case. | | |
| 4653 | /// The final element corresponds to the `else` case. | | |
| 4654 | /// Slices allocated into `gpa`. | | |
| 4655 | case_blocks: []Builder.Function.Block.Index, | | |
| 4656 | /// This is `.none` if `jmp_table` is set, since we won't use a `switch` instruction to dispatch. | | |
| 4657 | switch_weights: Builder.Function.Instruction.BrCond.Weights, | | |
| 4658 | /// If not `null`, we have manually constructed a jump table to reach the desired block. | | |
| 4659 | /// `table` can be used if the value is between `min` and `max` inclusive. | | |
| 4660 | /// We perform this lowering manually to avoid some questionable behavior from LLVM. | | |
| 4661 | /// See `airSwitchBr` for details. | | |
| 4662 | jmp_table: ?JmpTable, | | |
| 4663 | | | |
| 4664 | const JmpTable = struct { | | |
| 4665 | min: Builder.Constant, | | |
| 4666 | max: Builder.Constant, | | |
| 4667 | in_bounds_hint: enum { none, unpredictable, likely, unlikely }, | | |
| 4668 | /// Pointer to the jump table itself, to be used with `indirectbr`. | | |
| 4669 | /// The index into the jump table is the dispatch condition minus `min`. | | |
| 4670 | /// The table values are `blockaddress` constants corresponding to blocks in `case_blocks`. | | |
| 4671 | table: Builder.Constant, | | |
| 4672 | /// `true` if `table` conatins a reference to the `else` block. | | |
| 4673 | /// In this case, the `indirectbr` must include the `else` block in its target list. | | |
| 4674 | table_includes_else: bool, | | |
| 4675 | }; | | |
| 4676 | }; | | |
| 4677 | | | |
| 4678 | const BreakList = union { | | |
| 4679 | list: std.MultiArrayList(struct { | | |
| 4680 | bb: Builder.Function.Block.Index, | | |
| 4681 | val: Builder.Value, | | |
| 4682 | }), | | |
| 4683 | len: usize, | | |
| 4684 | }; | | |
| 4685 | | | |
| 4686 | fn deinit(self: *FuncGen) void { | | |
| 4687 | const gpa = self.gpa; | | |
| 4688 | if (self.fuzz) |*f| f.deinit(self.gpa); | | |
| 4689 | self.wip.deinit(); | | |
| 4690 | self.func_inst_table.deinit(gpa); | | |
| 4691 | self.blocks.deinit(gpa); | | |
| 4692 | self.loops.deinit(gpa); | | |
| 4693 | var it = self.switch_dispatch_info.valueIterator(); | | |
| 4694 | while (it.next()) |info| { | | |
| 4695 | self.gpa.free(info.case_blocks); | | |
| 4696 | } | | |
| 4697 | self.switch_dispatch_info.deinit(gpa); | | |
| 4698 | } | | |
| 4699 | | | |
| 4700 | fn todo(self: *FuncGen, comptime format: []const u8, args: anytype) Error { | | |
| 4701 | @branchHint(.cold); | | |
| 4702 | return self.ng.todo(format, args); | | |
| 4703 | } | | |
| 4704 | | | |
| 4705 | fn resolveInst(self: *FuncGen, inst: Air.Inst.Ref) !Builder.Value { | | |
| 4706 | const gpa = self.gpa; | | |
| 4707 | const gop = try self.func_inst_table.getOrPut(gpa, inst); | | |
| 4708 | if (gop.found_existing) return gop.value_ptr.*; | | |
| 4709 | | | |
| 4710 | const llvm_val = try self.resolveValue((try self.air.value(inst, self.ng.pt)).?); | | |
| 4711 | gop.value_ptr.* = llvm_val.toValue(); | | |
| 4712 | return llvm_val.toValue(); | | |
| 4713 | } | | |
| 4714 | | | |
| 4715 | fn resolveValue(self: *FuncGen, val: Value) Error!Builder.Constant { | | |
| 4716 | const o = self.ng.object; | | |
| 4717 | const pt = self.ng.pt; | | |
| 4718 | const zcu = pt.zcu; | | |
| 4719 | const ty = val.typeOf(zcu); | | |
| 4720 | const llvm_val = try o.lowerValue(pt, val.toIntern()); | | |
| 4721 | if (!isByRef(ty, zcu)) return llvm_val; | | |
| 4722 | | | |
| 4723 | // We have an LLVM value but we need to create a global constant and | | |
| 4724 | // set the value as its initializer, and then return a pointer to the global. | | |
| 4725 | const target = zcu.getTarget(); | | |
| 4726 | const variable_index = try o.builder.addVariable( | | |
| 4727 | .empty, | | |
| 4728 | llvm_val.typeOf(&o.builder), | | |
| 4729 | toLlvmGlobalAddressSpace(.generic, target), | | |
| 4730 | ); | | |
| 4731 | try variable_index.setInitializer(llvm_val, &o.builder); | | |
| 4732 | variable_index.setLinkage(.private, &o.builder); | | |
| 4733 | variable_index.setMutability(.constant, &o.builder); | | |
| 4734 | variable_index.setUnnamedAddr(.unnamed_addr, &o.builder); | | |
| 4735 | variable_index.setAlignment(ty.abiAlignment(zcu).toLlvm(), &o.builder); | | |
| 4736 | return o.builder.convConst( | | |
| 4737 | variable_index.toConst(&o.builder), | | |
| 4738 | try o.builder.ptrType(toLlvmAddressSpace(.generic, target)), | | |
| 4739 | ); | | |
| 4740 | } | | |
| 4741 | | | |
| 4742 | fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.CoveragePoint) Error!void { | | |
| 4743 | const o = self.ng.object; | | |
| 4744 | const zcu = self.ng.pt.zcu; | | |
| 4745 | const ip = &zcu.intern_pool; | | |
| 4746 | const air_tags = self.air.instructions.items(.tag); | | |
| 4747 | switch (coverage_point) { | | |
| 4748 | .none => {}, | | |
| 4749 | .poi => if (self.fuzz) |*fuzz| { | | |
| 4750 | const poi_index = fuzz.pcs.items.len; | | |
| 4751 | const base_ptr = fuzz.counters_variable.toValue(&o.builder); | | |
| 4752 | const ptr = if (poi_index == 0) base_ptr else try self.wip.gep(.inbounds, .i8, base_ptr, &.{ | | |
| 4753 | try o.builder.intValue(.i32, poi_index), | | |
| 4754 | }, ""); | | |
| 4755 | const one = try o.builder.intValue(.i8, 1); | | |
| 4756 | _ = try self.wip.atomicrmw(.normal, .add, ptr, one, self.sync_scope, .monotonic, .default, ""); | | |
| 4757 | | | |
| 4758 | // LLVM does not allow blockaddress on the entry block. | | |
| 4759 | const pc = if (self.wip.cursor.block == .entry) | | |
| 4760 | self.wip.function.toConst(&o.builder) | | |
| 4761 | else | | |
| 4762 | try o.builder.blockAddrConst(self.wip.function, self.wip.cursor.block); | | |
| 4763 | const gpa = self.gpa; | | |
| 4764 | try fuzz.pcs.append(gpa, pc); | | |
| 4765 | }, | | |
| 4766 | } | | |
| 4767 | for (body, 0..) |inst, i| { | | |
| 4768 | if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) continue; | | |
| 4769 | | | |
| 4770 | const val: Builder.Value = switch (air_tags[@intFromEnum(inst)]) { | | |
| 4771 | // zig fmt: off | | |
| 4772 | | | |
| 4773 | // No "scalarize" legalizations are enabled, so these instructions never appear. | | |
| 4774 | .legalize_vec_elem_val => unreachable, | | |
| 4775 | .legalize_vec_store_elem => unreachable, | | |
| 4776 | // No soft float legalizations are enabled. | | |
| 4777 | .legalize_compiler_rt_call => unreachable, | | |
| 4778 | | | |
| 4779 | .add => try self.airAdd(inst, .normal), | | |
| 4780 | .add_optimized => try self.airAdd(inst, .fast), | | |
| 4781 | .add_wrap => try self.airAddWrap(inst), | | |
| 4782 | .add_sat => try self.airAddSat(inst), | | |
| 4783 | | | |
| 4784 | .sub => try self.airSub(inst, .normal), | | |
| 4785 | .sub_optimized => try self.airSub(inst, .fast), | | |
| 4786 | .sub_wrap => try self.airSubWrap(inst), | | |
| 4787 | .sub_sat => try self.airSubSat(inst), | | |
| 4788 | | | |
| 4789 | .mul => try self.airMul(inst, .normal), | | |
| 4790 | .mul_optimized => try self.airMul(inst, .fast), | | |
| 4791 | .mul_wrap => try self.airMulWrap(inst), | | |
| 4792 | .mul_sat => try self.airMulSat(inst), | | |
| 4793 | | | |
| 4794 | .add_safe => try self.airSafeArithmetic(inst, .@"sadd.with.overflow", .@"uadd.with.overflow"), | | |
| 4795 | .sub_safe => try self.airSafeArithmetic(inst, .@"ssub.with.overflow", .@"usub.with.overflow"), | | |
| 4796 | .mul_safe => try self.airSafeArithmetic(inst, .@"smul.with.overflow", .@"umul.with.overflow"), | | |
| 4797 | | | |
| 4798 | .div_float => try self.airDivFloat(inst, .normal), | | |
| 4799 | .div_trunc => try self.airDivTrunc(inst, .normal), | | |
| 4800 | .div_floor => try self.airDivFloor(inst, .normal), | | |
| 4801 | .div_exact => try self.airDivExact(inst, .normal), | | |
| 4802 | .rem => try self.airRem(inst, .normal), | | |
| 4803 | .mod => try self.airMod(inst, .normal), | | |
| 4804 | .abs => try self.airAbs(inst), | | |
| 4805 | .ptr_add => try self.airPtrAdd(inst), | | |
| 4806 | .ptr_sub => try self.airPtrSub(inst), | | |
| 4807 | .shl => try self.airShl(inst), | | |
| 4808 | .shl_sat => try self.airShlSat(inst), | | |
| 4809 | .shl_exact => try self.airShlExact(inst), | | |
| 4810 | .min => try self.airMin(inst), | | |
| 4811 | .max => try self.airMax(inst), | | |
| 4812 | .slice => try self.airSlice(inst), | | |
| 4813 | .mul_add => try self.airMulAdd(inst), | | |
| 4814 | | | |
| 4815 | .div_float_optimized => try self.airDivFloat(inst, .fast), | | |
| 4816 | .div_trunc_optimized => try self.airDivTrunc(inst, .fast), | | |
| 4817 | .div_floor_optimized => try self.airDivFloor(inst, .fast), | | |
| 4818 | .div_exact_optimized => try self.airDivExact(inst, .fast), | | |
| 4819 | .rem_optimized => try self.airRem(inst, .fast), | | |
| 4820 | .mod_optimized => try self.airMod(inst, .fast), | | |
| 4821 | | | |
| 4822 | .add_with_overflow => try self.airOverflow(inst, .@"sadd.with.overflow", .@"uadd.with.overflow"), | | |
| 4823 | .sub_with_overflow => try self.airOverflow(inst, .@"ssub.with.overflow", .@"usub.with.overflow"), | | |
| 4824 | .mul_with_overflow => try self.airOverflow(inst, .@"smul.with.overflow", .@"umul.with.overflow"), | | |
| 4825 | .shl_with_overflow => try self.airShlWithOverflow(inst), | | |
| 4826 | | | |
| 4827 | .bit_and, .bool_and => try self.airAnd(inst), | | |
| 4828 | .bit_or, .bool_or => try self.airOr(inst), | | |
| 4829 | .xor => try self.airXor(inst), | | |
| 4830 | .shr => try self.airShr(inst, false), | | |
| 4831 | .shr_exact => try self.airShr(inst, true), | | |
| 4832 | | | |
| 4833 | .sqrt => try self.airUnaryOp(inst, .sqrt), | | |
| 4834 | .sin => try self.airUnaryOp(inst, .sin), | | |
| 4835 | .cos => try self.airUnaryOp(inst, .cos), | | |
| 4836 | .tan => try self.airUnaryOp(inst, .tan), | | |
| 4837 | .exp => try self.airUnaryOp(inst, .exp), | | |
| 4838 | .exp2 => try self.airUnaryOp(inst, .exp2), | | |
| 4839 | .log => try self.airUnaryOp(inst, .log), | | |
| 4840 | .log2 => try self.airUnaryOp(inst, .log2), | | |
| 4841 | .log10 => try self.airUnaryOp(inst, .log10), | | |
| 4842 | .floor => try self.airUnaryOp(inst, .floor), | | |
| 4843 | .ceil => try self.airUnaryOp(inst, .ceil), | | |
| 4844 | .round => try self.airUnaryOp(inst, .round), | | |
| 4845 | .trunc_float => try self.airUnaryOp(inst, .trunc), | | |
| 4846 | | | |
| 4847 | .neg => try self.airNeg(inst, .normal), | | |
| 4848 | .neg_optimized => try self.airNeg(inst, .fast), | | |
| 4849 | | | |
| 4850 | .cmp_eq => try self.airCmp(inst, .eq, .normal), | | |
| 4851 | .cmp_gt => try self.airCmp(inst, .gt, .normal), | | |
| 4852 | .cmp_gte => try self.airCmp(inst, .gte, .normal), | | |
| 4853 | .cmp_lt => try self.airCmp(inst, .lt, .normal), | | |
| 4854 | .cmp_lte => try self.airCmp(inst, .lte, .normal), | | |
| 4855 | .cmp_neq => try self.airCmp(inst, .neq, .normal), | | |
| 4856 | | | |
| 4857 | .cmp_eq_optimized => try self.airCmp(inst, .eq, .fast), | | |
| 4858 | .cmp_gt_optimized => try self.airCmp(inst, .gt, .fast), | | |
| 4859 | .cmp_gte_optimized => try self.airCmp(inst, .gte, .fast), | | |
| 4860 | .cmp_lt_optimized => try self.airCmp(inst, .lt, .fast), | | |
| 4861 | .cmp_lte_optimized => try self.airCmp(inst, .lte, .fast), | | |
| 4862 | .cmp_neq_optimized => try self.airCmp(inst, .neq, .fast), | | |
| 4863 | | | |
| 4864 | .cmp_vector => try self.airCmpVector(inst, .normal), | | |
| 4865 | .cmp_vector_optimized => try self.airCmpVector(inst, .fast), | | |
| 4866 | .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), | | |
| 4867 | | | |
| 4868 | .is_non_null => try self.airIsNonNull(inst, false, .ne), | | |
| 4869 | .is_non_null_ptr => try self.airIsNonNull(inst, true , .ne), | | |
| 4870 | .is_null => try self.airIsNonNull(inst, false, .eq), | | |
| 4871 | .is_null_ptr => try self.airIsNonNull(inst, true , .eq), | | |
| 4872 | | | |
| 4873 | .is_non_err => try self.airIsErr(inst, .eq, false), | | |
| 4874 | .is_non_err_ptr => try self.airIsErr(inst, .eq, true), | | |
| 4875 | .is_err => try self.airIsErr(inst, .ne, false), | | |
| 4876 | .is_err_ptr => try self.airIsErr(inst, .ne, true), | | |
| 4877 | | | |
| 4878 | .alloc => try self.airAlloc(inst), | | |
| 4879 | .ret_ptr => try self.airRetPtr(inst), | | |
| 4880 | .arg => try self.airArg(inst), | | |
| 4881 | .bitcast => try self.airBitCast(inst), | | |
| 4882 | .breakpoint => try self.airBreakpoint(inst), | | |
| 4883 | .ret_addr => try self.airRetAddr(inst), | | |
| 4884 | .frame_addr => try self.airFrameAddress(inst), | | |
| 4885 | .@"try" => try self.airTry(inst, false), | | |
| 4886 | .try_cold => try self.airTry(inst, true), | | |
| 4887 | .try_ptr => try self.airTryPtr(inst, false), | | |
| 4888 | .try_ptr_cold => try self.airTryPtr(inst, true), | | |
| 4889 | .intcast => try self.airIntCast(inst, false), | | |
| 4890 | .intcast_safe => try self.airIntCast(inst, true), | | |
| 4891 | .trunc => try self.airTrunc(inst), | | |
| 4892 | .fptrunc => try self.airFptrunc(inst), | | |
| 4893 | .fpext => try self.airFpext(inst), | | |
| 4894 | .load => try self.airLoad(inst), | | |
| 4895 | .not => try self.airNot(inst), | | |
| 4896 | .store => try self.airStore(inst, false), | | |
| 4897 | .store_safe => try self.airStore(inst, true), | | |
| 4898 | .assembly => try self.airAssembly(inst), | | |
| 4899 | .slice_ptr => try self.airSliceField(inst, 0), | | |
| 4900 | .slice_len => try self.airSliceField(inst, 1), | | |
| 4901 | | | |
| 4902 | .ptr_slice_ptr_ptr => try self.airPtrSliceFieldPtr(inst, 0), | | |
| 4903 | .ptr_slice_len_ptr => try self.airPtrSliceFieldPtr(inst, 1), | | |
| 4904 | | | |
| 4905 | .int_from_float => try self.airIntFromFloat(inst, .normal), | | |
| 4906 | .int_from_float_optimized => try self.airIntFromFloat(inst, .fast), | | |
| 4907 | .int_from_float_safe => unreachable, // handled by `legalizeFeatures` | | |
| 4908 | .int_from_float_optimized_safe => unreachable, // handled by `legalizeFeatures` | | |
| 4909 | | | |
| 4910 | .array_to_slice => try self.airArrayToSlice(inst), | | |
| 4911 | .float_from_int => try self.airFloatFromInt(inst), | | |
| 4912 | .cmpxchg_weak => try self.airCmpxchg(inst, .weak), | | |
| 4913 | .cmpxchg_strong => try self.airCmpxchg(inst, .strong), | | |
| 4914 | .atomic_rmw => try self.airAtomicRmw(inst), | | |
| 4915 | .atomic_load => try self.airAtomicLoad(inst), | | |
| 4916 | .memset => try self.airMemset(inst, false), | | |
| 4917 | .memset_safe => try self.airMemset(inst, true), | | |
| 4918 | .memcpy => try self.airMemcpy(inst), | | |
| 4919 | .memmove => try self.airMemmove(inst), | | |
| 4920 | .set_union_tag => try self.airSetUnionTag(inst), | | |
| 4921 | .get_union_tag => try self.airGetUnionTag(inst), | | |
| 4922 | .clz => try self.airClzCtz(inst, .ctlz), | | |
| 4923 | .ctz => try self.airClzCtz(inst, .cttz), | | |
| 4924 | .popcount => try self.airBitOp(inst, .ctpop), | | |
| 4925 | .byte_swap => try self.airByteSwap(inst), | | |
| 4926 | .bit_reverse => try self.airBitOp(inst, .bitreverse), | | |
| 4927 | .tag_name => try self.airTagName(inst), | | |
| 4928 | .error_name => try self.airErrorName(inst), | | |
| 4929 | .splat => try self.airSplat(inst), | | |
| 4930 | .select => try self.airSelect(inst), | | |
| 4931 | .shuffle_one => try self.airShuffleOne(inst), | | |
| 4932 | .shuffle_two => try self.airShuffleTwo(inst), | | |
| 4933 | .aggregate_init => try self.airAggregateInit(inst), | | |
| 4934 | .union_init => try self.airUnionInit(inst), | | |
| 4935 | .prefetch => try self.airPrefetch(inst), | | |
| 4936 | .addrspace_cast => try self.airAddrSpaceCast(inst), | | |
| 4937 | | | |
| 4938 | .is_named_enum_value => try self.airIsNamedEnumValue(inst), | | |
| 4939 | .error_set_has_value => try self.airErrorSetHasValue(inst), | | |
| 4940 | | | |
| 4941 | .reduce => try self.airReduce(inst, .normal), | | |
| 4942 | .reduce_optimized => try self.airReduce(inst, .fast), | | |
| 4943 | | | |
| 4944 | .atomic_store_unordered => try self.airAtomicStore(inst, .unordered), | | |
| 4945 | .atomic_store_monotonic => try self.airAtomicStore(inst, .monotonic), | | |
| 4946 | .atomic_store_release => try self.airAtomicStore(inst, .release), | | |
| 4947 | .atomic_store_seq_cst => try self.airAtomicStore(inst, .seq_cst), | | |
| 4948 | | | |
| 4949 | .struct_field_ptr => try self.airStructFieldPtr(inst), | | |
| 4950 | .struct_field_val => try self.airStructFieldVal(inst), | | |
| 4951 | | | |
| 4952 | .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), | | |
| 4953 | .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), | | |
| 4954 | .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), | | |
| 4955 | .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), | | |
| 4956 | | | |
| 4957 | .field_parent_ptr => try self.airFieldParentPtr(inst), | | |
| 4958 | | | |
| 4959 | .array_elem_val => try self.airArrayElemVal(inst), | | |
| 4960 | .slice_elem_val => try self.airSliceElemVal(inst), | | |
| 4961 | .slice_elem_ptr => try self.airSliceElemPtr(inst), | | |
| 4962 | .ptr_elem_val => try self.airPtrElemVal(inst), | | |
| 4963 | .ptr_elem_ptr => try self.airPtrElemPtr(inst), | | |
| 4964 | | | |
| 4965 | .optional_payload => try self.airOptionalPayload(inst), | | |
| 4966 | .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), | | |
| 4967 | .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), | | |
| 4968 | | | |
| 4969 | .unwrap_errunion_payload => try self.airErrUnionPayload(inst, false), | | |
| 4970 | .unwrap_errunion_payload_ptr => try self.airErrUnionPayload(inst, true), | | |
| 4971 | .unwrap_errunion_err => try self.airErrUnionErr(inst, false), | | |
| 4972 | .unwrap_errunion_err_ptr => try self.airErrUnionErr(inst, true), | | |
| 4973 | .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst), | | |
| 4974 | .err_return_trace => try self.airErrReturnTrace(inst), | | |
| 4975 | .set_err_return_trace => try self.airSetErrReturnTrace(inst), | | |
| 4976 | .save_err_return_trace_index => try self.airSaveErrReturnTraceIndex(inst), | | |
| 4977 | | | |
| 4978 | .wrap_optional => try self.airWrapOptional(body[i..]), | | |
| 4979 | .wrap_errunion_payload => try self.airWrapErrUnionPayload(body[i..]), | | |
| 4980 | .wrap_errunion_err => try self.airWrapErrUnionErr(body[i..]), | | |
| 4981 | | | |
| 4982 | .wasm_memory_size => try self.airWasmMemorySize(inst), | | |
| 4983 | .wasm_memory_grow => try self.airWasmMemoryGrow(inst), | | |
| 4984 | | | |
| 4985 | .runtime_nav_ptr => try self.airRuntimeNavPtr(inst), | | |
| 4986 | | | |
| 4987 | .inferred_alloc, .inferred_alloc_comptime => unreachable, | | |
| 4988 | | | |
| 4989 | .dbg_stmt => try self.airDbgStmt(inst), | | |
| 4990 | .dbg_empty_stmt => try self.airDbgEmptyStmt(inst), | | |
| 4991 | .dbg_var_ptr => try self.airDbgVarPtr(inst), | | |
| 4992 | .dbg_var_val => try self.airDbgVarVal(inst, false), | | |
| 4993 | .dbg_arg_inline => try self.airDbgVarVal(inst, true), | | |
| 4994 | | | |
| 4995 | .c_va_arg => try self.airCVaArg(inst), | | |
| 4996 | .c_va_copy => try self.airCVaCopy(inst), | | |
| 4997 | .c_va_end => try self.airCVaEnd(inst), | | |
| 4998 | .c_va_start => try self.airCVaStart(inst), | | |
| 4999 | | | |
| 5000 | .work_item_id => try self.airWorkItemId(inst), | | |
| 5001 | .work_group_size => try self.airWorkGroupSize(inst), | | |
| 5002 | .work_group_id => try self.airWorkGroupId(inst), | | |
| 5003 | | | |
| 5004 | // Instructions that are known to always be `noreturn` based on their tag. | | |
| 5005 | .br => return self.airBr(inst), | | |
| 5006 | .repeat => return self.airRepeat(inst), | | |
| 5007 | .switch_dispatch => return self.airSwitchDispatch(inst), | | |
| 5008 | .cond_br => return self.airCondBr(inst), | | |
| 5009 | .switch_br => return self.airSwitchBr(inst, false), | | |
| 5010 | .loop_switch_br => return self.airSwitchBr(inst, true), | | |
| 5011 | .loop => return self.airLoop(inst), | | |
| 5012 | .ret => return self.airRet(inst, false), | | |
| 5013 | .ret_safe => return self.airRet(inst, true), | | |
| 5014 | .ret_load => return self.airRetLoad(inst), | | |
| 5015 | .trap => return self.airTrap(inst), | | |
| 5016 | .unreach => return self.airUnreach(inst), | | |
| 5017 | | | |
| 5018 | // Instructions which may be `noreturn`. | | |
| 5019 | .block => res: { | | |
| 5020 | const res = try self.airBlock(inst); | | |
| 5021 | if (self.typeOfIndex(inst).isNoReturn(zcu)) return; | | |
| 5022 | break :res res; | | |
| 5023 | }, | | |
| 5024 | .dbg_inline_block => res: { | | |
| 5025 | const res = try self.airDbgInlineBlock(inst); | | |
| 5026 | if (self.typeOfIndex(inst).isNoReturn(zcu)) return; | | |
| 5027 | break :res res; | | |
| 5028 | }, | | |
| 5029 | .call, .call_always_tail, .call_never_tail, .call_never_inline => |tag| res: { | | |
| 5030 | const res = try self.airCall(inst, switch (tag) { | | |
| 5031 | .call => .auto, | | |
| 5032 | .call_always_tail => .always_tail, | | |
| 5033 | .call_never_tail => .never_tail, | | |
| 5034 | .call_never_inline => .never_inline, | | |
| 5035 | else => unreachable, | | |
| 5036 | }); | | |
| 5037 | // TODO: the AIR we emit for calls is a bit weird - the instruction has | | |
| 5038 | // type `noreturn`, but there are instructions (and maybe a safety check) following | | |
| 5039 | // nonetheless. The `unreachable` or safety check should be emitted by backends instead. | | |
| 5040 | //if (self.typeOfIndex(inst).isNoReturn(mod)) return; | | |
| 5041 | break :res res; | | |
| 5042 | }, | | |
| 5043 | | | |
| 5044 | // zig fmt: on | | |
| 5045 | }; | | |
| 5046 | if (val != .none) try self.func_inst_table.putNoClobber(self.gpa, inst.toRef(), val); | | |
| 5047 | } | | |
| 5048 | unreachable; | | |
| 5049 | } | | |
| 5050 | | | |
| 5051 | fn genBodyDebugScope( | | |
| 5052 | self: *FuncGen, | | |
| 5053 | maybe_inline_func: ?InternPool.Index, | | |
| 5054 | body: []const Air.Inst.Index, | | |
| 5055 | coverage_point: Air.CoveragePoint, | | |
| 5056 | ) Error!void { | | |
| 5057 | if (self.wip.strip) return self.genBody(body, coverage_point); | | |
| 5058 | | | |
| 5059 | const old_debug_location = self.wip.debug_location; | | |
| 5060 | const old_file = self.file; | | |
| 5061 | const old_inlined_at = self.inlined_at; | | |
| 5062 | const old_base_line = self.base_line; | | |
| 5063 | defer if (maybe_inline_func) |_| { | | |
| 5064 | self.wip.debug_location = old_debug_location; | | |
| 5065 | self.file = old_file; | | |
| 5066 | self.inlined_at = old_inlined_at; | | |
| 5067 | self.base_line = old_base_line; | | |
| 5068 | }; | | |
| 5069 | | | |
| 5070 | const old_scope = self.scope; | | |
| 5071 | defer self.scope = old_scope; | | |
| 5072 | | | |
| 5073 | if (maybe_inline_func) |inline_func| { | | |
| 5074 | const o = self.ng.object; | | |
| 5075 | const pt = self.ng.pt; | | |
| 5076 | const zcu = pt.zcu; | | |
| 5077 | const ip = &zcu.intern_pool; | | |
| 5078 | | | |
| 5079 | const func = zcu.funcInfo(inline_func); | | |
| 5080 | const nav = ip.getNav(func.owner_nav); | | |
| 5081 | const file_scope = zcu.navFileScopeIndex(func.owner_nav); | | |
| 5082 | const mod = zcu.fileByIndex(file_scope).mod.?; | | |
| 5083 | | | |
| 5084 | self.file = try o.getDebugFile(pt, file_scope); | | |
| 5085 | | | |
| 5086 | self.base_line = zcu.navSrcLine(func.owner_nav); | | |
| 5087 | const line_number = self.base_line + 1; | | |
| 5088 | self.inlined_at = try self.wip.debug_location.toMetadata(&o.builder); | | |
| 5089 | | | |
| 5090 | const fn_ty = try pt.funcType(.{ | | |
| 5091 | .param_types = &.{}, | | |
| 5092 | .return_type = .void_type, | | |
| 5093 | }); | | |
| 5094 | | | |
| 5095 | self.scope = try o.builder.debugSubprogram( | | |
| 5096 | self.file, | | |
| 5097 | try o.builder.metadataString(nav.name.toSlice(&zcu.intern_pool)), | | |
| 5098 | try o.builder.metadataString(nav.fqn.toSlice(&zcu.intern_pool)), | | |
| 5099 | line_number, | | |
| 5100 | line_number + func.lbrace_line, | | |
| 5101 | try o.getDebugType(pt, fn_ty), | | |
| 5102 | .{ | | |
| 5103 | .di_flags = .{ .StaticMember = true }, | | |
| 5104 | .sp_flags = .{ | | |
| 5105 | .Optimized = mod.optimize_mode != .Debug, | | |
| 5106 | .Definition = true, | | |
| 5107 | .LocalToUnit = true, // inline functions cannot be exported | | |
| 5108 | }, | | |
| 5109 | }, | | |
| 5110 | o.debug_compile_unit.unwrap().?, | | |
| 5111 | ); | | |
| 5112 | } | | |
| 5113 | | | |
| 5114 | self.scope = try self.ng.object.builder.debugLexicalBlock( | | |
| 5115 | self.scope, | | |
| 5116 | self.file, | | |
| 5117 | self.prev_dbg_line, | | |
| 5118 | self.prev_dbg_column, | | |
| 5119 | ); | | |
| 5120 | self.wip.debug_location = .{ .location = .{ | | |
| 5121 | .line = self.prev_dbg_line, | | |
| 5122 | .column = self.prev_dbg_column, | | |
| 5123 | .scope = self.scope.toOptional(), | | |
| 5124 | .inlined_at = self.inlined_at, | | |
| 5125 | } }; | | |
| 5126 | | | |
| 5127 | try self.genBody(body, coverage_point); | | |
| 5128 | } | | |
| 5129 | | | |
| 5130 | pub const CallAttr = enum { | | |
| 5131 | Auto, | | |
| 5132 | NeverTail, | | |
| 5133 | NeverInline, | | |
| 5134 | AlwaysTail, | | |
| 5135 | AlwaysInline, | | |
| 5136 | }; | | |
| 5137 | | | |
| 5138 | fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !Builder.Value { | | |
| 5139 | const air_call = self.air.unwrapCall(inst); | | |
| 5140 | const args = air_call.args; | | |
| 5141 | const o = self.ng.object; | | |
| 5142 | const pt = self.ng.pt; | | |
| 5143 | const zcu = pt.zcu; | | |
| 5144 | const ip = &zcu.intern_pool; | | |
| 5145 | const callee_ty = self.typeOf(air_call.callee); | | |
| 5146 | const zig_fn_ty = switch (callee_ty.zigTypeTag(zcu)) { | | |
| 5147 | .@"fn" => callee_ty, | | |
| 5148 | .pointer => callee_ty.childType(zcu), | | |
| 5149 | else => unreachable, | | |
| 5150 | }; | | |
| 5151 | const fn_info = zcu.typeToFunc(zig_fn_ty).?; | | |
| 5152 | const return_type = Type.fromInterned(fn_info.return_type); | | |
| 5153 | const llvm_fn = try self.resolveInst(air_call.callee); | | |
| 5154 | const target = zcu.getTarget(); | | |
| 5155 | const sret = firstParamSRet(fn_info, zcu, target); | | |
| 5156 | | | |
| 5157 | var llvm_args = std.array_list.Managed(Builder.Value).init(self.gpa); | | |
| 5158 | defer llvm_args.deinit(); | | |
| 5159 | | | |
| 5160 | var attributes: Builder.FunctionAttributes.Wip = .{}; | | |
| 5161 | defer attributes.deinit(&o.builder); | | |
| 5162 | | | |
| 5163 | if (self.disable_intrinsics) { | | |
| 5164 | try attributes.addFnAttr(.nobuiltin, &o.builder); | | |
| 5165 | } | | |
| 5166 | | | |
| 5167 | switch (modifier) { | | |
| 5168 | .auto, .always_tail => {}, | | |
| 5169 | .never_tail, .never_inline => try attributes.addFnAttr(.@"noinline", &o.builder), | | |
| 5170 | .no_suspend, .always_inline, .compile_time => unreachable, | | |
| 5171 | } | | |
| 5172 | | | |
| 5173 | const ret_ptr = if (!sret) null else blk: { | | |
| 5174 | const llvm_ret_ty = try o.lowerType(pt, return_type); | | |
| 5175 | try attributes.addParamAttr(0, .{ .sret = llvm_ret_ty }, &o.builder); | | |
| 5176 | | | |
| 5177 | const alignment = return_type.abiAlignment(zcu).toLlvm(); | | |
| 5178 | const ret_ptr = try self.buildAlloca(llvm_ret_ty, alignment); | | |
| 5179 | try llvm_args.append(ret_ptr); | | |
| 5180 | break :blk ret_ptr; | | |
| 5181 | }; | | |
| 5182 | | | |
| 5183 | const err_return_tracing = fn_info.cc == .auto and zcu.comp.config.any_error_tracing; | | |
| 5184 | if (err_return_tracing) { | | |
| 5185 | assert(self.err_ret_trace != .none); | | |
| 5186 | try llvm_args.append(self.err_ret_trace); | | |
| 5187 | } | | |
| 5188 | | | |
| 5189 | var it = iterateParamTypes(o, pt, fn_info); | | |
| 5190 | while (try it.nextCall(self, args)) |lowering| switch (lowering) { | | |
| 5191 | .no_bits => continue, | | |
| 5192 | .byval => { | | |
| 5193 | const arg = args[it.zig_index - 1]; | | |
| 5194 | const param_ty = self.typeOf(arg); | | |
| 5195 | const llvm_arg = try self.resolveInst(arg); | | |
| 5196 | const llvm_param_ty = try o.lowerType(pt, param_ty); | | |
| 5197 | if (isByRef(param_ty, zcu)) { | | |
| 5198 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); | | |
| 5199 | const loaded = try self.wip.load(.normal, llvm_param_ty, llvm_arg, alignment, ""); | | |
| 5200 | try llvm_args.append(loaded); | | |
| 5201 | } else { | | |
| 5202 | try llvm_args.append(llvm_arg); | | |
| 5203 | } | | |
| 5204 | }, | | |
| 5205 | .byref => { | | |
| 5206 | const arg = args[it.zig_index - 1]; | | |
| 5207 | const param_ty = self.typeOf(arg); | | |
| 5208 | const llvm_arg = try self.resolveInst(arg); | | |
| 5209 | if (isByRef(param_ty, zcu)) { | | |
| 5210 | try llvm_args.append(llvm_arg); | | |
| 5211 | } else { | | |
| 5212 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); | | |
| 5213 | const param_llvm_ty = llvm_arg.typeOfWip(&self.wip); | | |
| 5214 | const arg_ptr = try self.buildAlloca(param_llvm_ty, alignment); | | |
| 5215 | _ = try self.wip.store(.normal, llvm_arg, arg_ptr, alignment); | | |
| 5216 | try llvm_args.append(arg_ptr); | | |
| 5217 | } | | |
| 5218 | }, | | |
| 5219 | .byref_mut => { | | |
| 5220 | const arg = args[it.zig_index - 1]; | | |
| 5221 | const param_ty = self.typeOf(arg); | | |
| 5222 | const llvm_arg = try self.resolveInst(arg); | | |
| 5223 | | | |
| 5224 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); | | |
| 5225 | const param_llvm_ty = try o.lowerType(pt, param_ty); | | |
| 5226 | const arg_ptr = try self.buildAlloca(param_llvm_ty, alignment); | | |
| 5227 | if (isByRef(param_ty, zcu)) { | | |
| 5228 | const loaded = try self.wip.load(.normal, param_llvm_ty, llvm_arg, alignment, ""); | | |
| 5229 | _ = try self.wip.store(.normal, loaded, arg_ptr, alignment); | | |
| 5230 | } else { | | |
| 5231 | _ = try self.wip.store(.normal, llvm_arg, arg_ptr, alignment); | | |
| 5232 | } | | |
| 5233 | try llvm_args.append(arg_ptr); | | |
| 5234 | }, | | |
| 5235 | .abi_sized_int => { | | |
| 5236 | const arg = args[it.zig_index - 1]; | | |
| 5237 | const param_ty = self.typeOf(arg); | | |
| 5238 | const llvm_arg = try self.resolveInst(arg); | | |
| 5239 | const int_llvm_ty = try o.builder.intType(@intCast(param_ty.abiSize(zcu) * 8)); | | |
| 5240 | | | |
| 5241 | if (isByRef(param_ty, zcu)) { | | |
| 5242 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); | | |
| 5243 | const loaded = try self.wip.load(.normal, int_llvm_ty, llvm_arg, alignment, ""); | | |
| 5244 | try llvm_args.append(loaded); | | |
| 5245 | } else { | | |
| 5246 | // LLVM does not allow bitcasting structs so we must allocate | | |
| 5247 | // a local, store as one type, and then load as another type. | | |
| 5248 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); | | |
| 5249 | const int_ptr = try self.buildAlloca(int_llvm_ty, alignment); | | |
| 5250 | _ = try self.wip.store(.normal, llvm_arg, int_ptr, alignment); | | |
| 5251 | const loaded = try self.wip.load(.normal, int_llvm_ty, int_ptr, alignment, ""); | | |
| 5252 | try llvm_args.append(loaded); | | |
| 5253 | } | | |
| 5254 | }, | | |
| 5255 | .slice => { | | |
| 5256 | const arg = args[it.zig_index - 1]; | | |
| 5257 | const llvm_arg = try self.resolveInst(arg); | | |
| 5258 | const ptr = try self.wip.extractValue(llvm_arg, &.{0}, ""); | | |
| 5259 | const len = try self.wip.extractValue(llvm_arg, &.{1}, ""); | | |
| 5260 | try llvm_args.appendSlice(&.{ ptr, len }); | | |
| 5261 | }, | | |
| 5262 | .multiple_llvm_types => { | | |
| 5263 | const arg = args[it.zig_index - 1]; | | |
| 5264 | const param_ty = self.typeOf(arg); | | |
| 5265 | const llvm_types = it.types_buffer[0..it.types_len]; | | |
| 5266 | const llvm_arg = try self.resolveInst(arg); | | |
| 5267 | const is_by_ref = isByRef(param_ty, zcu); | | |
| 5268 | const arg_ptr = if (is_by_ref) llvm_arg else ptr: { | | |
| 5269 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); | | |
| 5270 | const ptr = try self.buildAlloca(llvm_arg.typeOfWip(&self.wip), alignment); | | |
| 5271 | _ = try self.wip.store(.normal, llvm_arg, ptr, alignment); | | |
| 5272 | break :ptr ptr; | | |
| 5273 | }; | | |
| 5274 | | | |
| 5275 | const llvm_ty = try o.builder.structType(.normal, llvm_types); | | |
| 5276 | try llvm_args.ensureUnusedCapacity(it.types_len); | | |
| 5277 | for (llvm_types, 0..) |field_ty, i| { | | |
| 5278 | const alignment = | | |
| 5279 | Builder.Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)); | | |
| 5280 | const field_ptr = try self.wip.gepStruct(llvm_ty, arg_ptr, i, ""); | | |
| 5281 | const loaded = try self.wip.load(.normal, field_ty, field_ptr, alignment, ""); | | |
| 5282 | llvm_args.appendAssumeCapacity(loaded); | | |
| 5283 | } | | |
| 5284 | }, | | |
| 5285 | .float_array => |count| { | | |
| 5286 | const arg = args[it.zig_index - 1]; | | |
| 5287 | const arg_ty = self.typeOf(arg); | | |
| 5288 | var llvm_arg = try self.resolveInst(arg); | | |
| 5289 | const alignment = arg_ty.abiAlignment(zcu).toLlvm(); | | |
| 5290 | if (!isByRef(arg_ty, zcu)) { | | |
| 5291 | const ptr = try self.buildAlloca(llvm_arg.typeOfWip(&self.wip), alignment); | | |
| 5292 | _ = try self.wip.store(.normal, llvm_arg, ptr, alignment); | | |
| 5293 | llvm_arg = ptr; | | |
| 5294 | } | | |
| 5295 | | | |
| 5296 | const float_ty = try o.lowerType(pt, aarch64_c_abi.getFloatArrayType(arg_ty, zcu).?); | | |
| 5297 | const array_ty = try o.builder.arrayType(count, float_ty); | | |
| 5298 | | | |
| 5299 | const loaded = try self.wip.load(.normal, array_ty, llvm_arg, alignment, ""); | | |
| 5300 | try llvm_args.append(loaded); | | |
| 5301 | }, | | |
| 5302 | .i32_array, .i64_array => |arr_len| { | | |
| 5303 | const elem_size: u8 = if (lowering == .i32_array) 32 else 64; | | |
| 5304 | const arg = args[it.zig_index - 1]; | | |
| 5305 | const arg_ty = self.typeOf(arg); | | |
| 5306 | var llvm_arg = try self.resolveInst(arg); | | |
| 5307 | const alignment = arg_ty.abiAlignment(zcu).toLlvm(); | | |
| 5308 | if (!isByRef(arg_ty, zcu)) { | | |
| 5309 | const ptr = try self.buildAlloca(llvm_arg.typeOfWip(&self.wip), alignment); | | |
| 5310 | _ = try self.wip.store(.normal, llvm_arg, ptr, alignment); | | |
| 5311 | llvm_arg = ptr; | | |
| 5312 | } | | |
| 5313 | | | |
| 5314 | const array_ty = | | |
| 5315 | try o.builder.arrayType(arr_len, try o.builder.intType(@intCast(elem_size))); | | |
| 5316 | const loaded = try self.wip.load(.normal, array_ty, llvm_arg, alignment, ""); | | |
| 5317 | try llvm_args.append(loaded); | | |
| 5318 | }, | | |
| 5319 | }; | | |
| 5320 | | | |
| 5321 | { | | |
| 5322 | // Add argument attributes. | | |
| 5323 | it = iterateParamTypes(o, pt, fn_info); | | |
| 5324 | it.llvm_index += @intFromBool(sret); | | |
| 5325 | it.llvm_index += @intFromBool(err_return_tracing); | | |
| 5326 | while (try it.next()) |lowering| switch (lowering) { | | |
| 5327 | .byval => { | | |
| 5328 | const param_index = it.zig_index - 1; | | |
| 5329 | const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[param_index]); | | |
| 5330 | if (!isByRef(param_ty, zcu)) { | | |
| 5331 | try o.addByValParamAttrs(pt, &attributes, param_ty, param_index, fn_info, it.llvm_index - 1); | | |
| 5332 | } | | |
| 5333 | }, | | |
| 5334 | .byref => { | | |
| 5335 | const param_index = it.zig_index - 1; | | |
| 5336 | const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[param_index]); | | |
| 5337 | const param_llvm_ty = try o.lowerType(pt, param_ty); | | |
| 5338 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); | | |
| 5339 | try o.addByRefParamAttrs(&attributes, it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty); | | |
| 5340 | }, | | |
| 5341 | .byref_mut => try attributes.addParamAttr(it.llvm_index - 1, .noundef, &o.builder), | | |
| 5342 | // No attributes needed for these. | | |
| 5343 | .no_bits, | | |
| 5344 | .abi_sized_int, | | |
| 5345 | .multiple_llvm_types, | | |
| 5346 | .float_array, | | |
| 5347 | .i32_array, | | |
| 5348 | .i64_array, | | |
| 5349 | => continue, | | |
| 5350 | | | |
| 5351 | .slice => { | | |
| 5352 | assert(!it.byval_attr); | | |
| 5353 | const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); | | |
| 5354 | const ptr_info = param_ty.ptrInfo(zcu); | | |
| 5355 | const llvm_arg_i = it.llvm_index - 2; | | |
| 5356 | | | |
| 5357 | if (math.cast(u5, it.zig_index - 1)) |i| { | | |
| 5358 | if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) { | | |
| 5359 | try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder); | | |
| 5360 | } | | |
| 5361 | } | | |
| 5362 | if (param_ty.zigTypeTag(zcu) != .optional and | | |
| 5363 | !ptr_info.flags.is_allowzero and | | |
| 5364 | ptr_info.flags.address_space == .generic) | | |
| 5365 | { | | |
| 5366 | try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); | | |
| 5367 | } | | |
| 5368 | if (ptr_info.flags.is_const) { | | |
| 5369 | try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder); | | |
| 5370 | } | | |
| 5371 | const elem_align: Builder.Alignment.Lazy = switch (ptr_info.flags.alignment) { | | |
| 5372 | else => |a| .wrap(a.toLlvm()), | | |
| 5373 | .none => try o.lazyAbiAlignment(pt, .fromInterned(ptr_info.child)), | | |
| 5374 | }; | | |
| 5375 | try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align }, &o.builder); | | |
| 5376 | }, | | |
| 5377 | }; | | |
| 5378 | } | | |
| 5379 | | | |
| 5380 | const call = try self.wip.call( | | |
| 5381 | switch (modifier) { | | |
| 5382 | .auto, .never_inline => .normal, | | |
| 5383 | .never_tail => .notail, | | |
| 5384 | .always_tail => .musttail, | | |
| 5385 | .no_suspend, .always_inline, .compile_time => unreachable, | | |
| 5386 | }, | | |
| 5387 | toLlvmCallConvTag(fn_info.cc, target).?, | | |
| 5388 | try attributes.finish(&o.builder), | | |
| 5389 | try o.lowerType(pt, zig_fn_ty), | | |
| 5390 | llvm_fn, | | |
| 5391 | llvm_args.items, | | |
| 5392 | "", | | |
| 5393 | ); | | |
| 5394 | | | |
| 5395 | if (fn_info.return_type == .noreturn_type and modifier != .always_tail) { | | |
| 5396 | return .none; | | |
| 5397 | } | | |
| 5398 | | | |
| 5399 | if (self.liveness.isUnused(inst) or !return_type.hasRuntimeBits(zcu)) { | | |
| 5400 | return .none; | | |
| 5401 | } | | |
| 5402 | | | |
| 5403 | const llvm_ret_ty = try o.lowerType(pt, return_type); | | |
| 5404 | if (ret_ptr) |rp| { | | |
| 5405 | if (isByRef(return_type, zcu)) { | | |
| 5406 | return rp; | | |
| 5407 | } else { | | |
| 5408 | // our by-ref status disagrees with sret so we must load. | | |
| 5409 | const return_alignment = return_type.abiAlignment(zcu).toLlvm(); | | |
| 5410 | return self.wip.load(.normal, llvm_ret_ty, rp, return_alignment, ""); | | |
| 5411 | } | | |
| 5412 | } | | |
| 5413 | | | |
| 5414 | const abi_ret_ty = try lowerFnRetTy(o, pt, fn_info); | | |
| 5415 | | | |
| 5416 | if (abi_ret_ty != llvm_ret_ty) { | | |
| 5417 | // In this case the function return type is honoring the calling convention by having | | |
| 5418 | // a different LLVM type than the usual one. We solve this here at the callsite | | |
| 5419 | // by using our canonical type, then loading it if necessary. | | |
| 5420 | const alignment = return_type.abiAlignment(zcu).toLlvm(); | | |
| 5421 | const rp = try self.buildAlloca(abi_ret_ty, alignment); | | |
| 5422 | _ = try self.wip.store(.normal, call, rp, alignment); | | |
| 5423 | return if (isByRef(return_type, zcu)) | | |
| 5424 | rp | | |
| 5425 | else | | |
| 5426 | try self.wip.load(.normal, llvm_ret_ty, rp, alignment, ""); | | |
| 5427 | } | | |
| 5428 | | | |
| 5429 | if (isByRef(return_type, zcu)) { | | |
| 5430 | // our by-ref status disagrees with sret so we must allocate, store, | | |
| 5431 | // and return the allocation pointer. | | |
| 5432 | const alignment = return_type.abiAlignment(zcu).toLlvm(); | | |
| 5433 | const rp = try self.buildAlloca(llvm_ret_ty, alignment); | | |
| 5434 | _ = try self.wip.store(.normal, call, rp, alignment); | | |
| 5435 | return rp; | | |
| 5436 | } else { | | |
| 5437 | return call; | | |
| 5438 | } | | |
| 5439 | } | | |
| 5440 | | | |
| 5441 | fn buildSimplePanic(fg: *FuncGen, panic_id: Zcu.SimplePanicId) !void { | | |
| 5442 | const o = fg.ng.object; | | |
| 5443 | const pt = fg.ng.pt; | | |
| 5444 | const zcu = pt.zcu; | | |
| 5445 | const target = zcu.getTarget(); | | |
| 5446 | const panic_func = zcu.funcInfo(zcu.builtin_decl_values.get(panic_id.toBuiltin())); | | |
| 5447 | const fn_info = zcu.typeToFunc(.fromInterned(panic_func.ty)).?; | | |
| 5448 | const panic_global = try o.resolveLlvmFunction(pt, panic_func.owner_nav); | | |
| 5449 | | | |
| 5450 | const has_err_trace = zcu.comp.config.any_error_tracing and fn_info.cc == .auto; | | |
| 5451 | if (has_err_trace) assert(fg.err_ret_trace != .none); | | |
| 5452 | _ = try fg.wip.callIntrinsicAssumeCold(); | | |
| 5453 | _ = try fg.wip.call( | | |
| 5454 | .normal, | | |
| 5455 | toLlvmCallConvTag(fn_info.cc, target).?, | | |
| 5456 | .none, | | |
| 5457 | panic_global.typeOf(&o.builder), | | |
| 5458 | panic_global.toValue(&o.builder), | | |
| 5459 | if (has_err_trace) &.{fg.err_ret_trace} else &.{}, | | |
| 5460 | "", | | |
| 5461 | ); | | |
| 5462 | _ = try fg.wip.@"unreachable"(); | | |
| 5463 | } | | |
| 5464 | | | |
| 5465 | fn airRet(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !void { | | |
| 5466 | const o = self.ng.object; | | |
| 5467 | const pt = self.ng.pt; | | |
| 5468 | const zcu = pt.zcu; | | |
| 5469 | const ip = &zcu.intern_pool; | | |
| 5470 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | | |
| 5471 | const ret_ty = self.typeOf(un_op); | | |
| 5472 | | | |
| 5473 | if (self.ret_ptr != .none) { | | |
| 5474 | const ptr_ty = try pt.singleMutPtrType(ret_ty); | | |
| 5475 | | | |
| 5476 | const operand = try self.resolveInst(un_op); | | |
| 5477 | const val_is_undef = if (try self.air.value(un_op, pt)) |val| val.isUndef(zcu) else false; | | |
| 5478 | if (val_is_undef and safety) undef: { | | |
| 5479 | const ptr_info = ptr_ty.ptrInfo(zcu); | | |
| 5480 | const needs_bitmask = (ptr_info.packed_offset.host_size != 0); | | |
| 5481 | if (needs_bitmask) { | | |
| 5482 | // TODO: only some bits are to be undef, we cannot write with a simple memset. | | |
| 5483 | // meanwhile, ignore the write rather than stomping over valid bits. | | |
| 5484 | // https://github.com/ziglang/zig/issues/15337 | | |
| 5485 | break :undef; | | |
| 5486 | } | | |
| 5487 | const len = try o.builder.intValue(try o.lowerType(pt, Type.usize), ret_ty.abiSize(zcu)); | | |
| 5488 | _ = try self.wip.callMemSet( | | |
| 5489 | self.ret_ptr, | | |
| 5490 | ptr_ty.ptrAlignment(zcu).toLlvm(), | | |
| 5491 | try o.builder.intValue(.i8, 0xaa), | | |
| 5492 | len, | | |
| 5493 | .normal, | | |
| 5494 | self.disable_intrinsics, | | |
| 5495 | ); | | |
| 5496 | const owner_mod = self.ng.ownerModule(); | | |
| 5497 | if (owner_mod.valgrind) { | | |
| 5498 | try self.valgrindMarkUndef(self.ret_ptr, len); | | |
| 5499 | } | | |
| 5500 | _ = try self.wip.retVoid(); | | |
| 5501 | return; | | |
| 5502 | } | | |
| 5503 | | | |
| 5504 | const unwrapped_operand = operand.unwrap(); | | |
| 5505 | const unwrapped_ret = self.ret_ptr.unwrap(); | | |
| 5506 | | | |
| 5507 | // Return value was stored previously | | |
| 5508 | if (unwrapped_operand == .instruction and unwrapped_ret == .instruction and unwrapped_operand.instruction == unwrapped_ret.instruction) { | | |
| 5509 | _ = try self.wip.retVoid(); | | |
| 5510 | return; | | |
| 5511 | } | | |
| 5512 | | | |
| 5513 | try self.store(self.ret_ptr, ptr_ty, operand, .none); | | |
| 5514 | _ = try self.wip.retVoid(); | | |
| 5515 | return; | | |
| 5516 | } | | |
| 5517 | const fn_info = zcu.typeToFunc(Type.fromInterned(ip.getNav(self.ng.nav_index).resolved.?.type)).?; | | |
| 5518 | if (!ret_ty.hasRuntimeBits(zcu)) { | | |
| 5519 | if (Type.fromInterned(fn_info.return_type).isError(zcu)) { | | |
| 5520 | // Functions with an empty error set are emitted with an error code | | |
| 5521 | // return type and return zero so they can be function pointers coerced | | |
| 5522 | // to functions that return anyerror. | | |
| 5523 | _ = try self.wip.ret(try o.builder.intValue(try o.errorIntType(pt), 0)); | | |
| 5524 | } else { | | |
| 5525 | _ = try self.wip.retVoid(); | | |
| 5526 | } | | |
| 5527 | return; | | |
| 5528 | } | | |
| 5529 | | | |
| 5530 | const abi_ret_ty = try lowerFnRetTy(o, pt, fn_info); | | |
| 5531 | const operand = try self.resolveInst(un_op); | | |
| 5532 | const val_is_undef = if (try self.air.value(un_op, pt)) |val| val.isUndef(zcu) else false; | | |
| 5533 | const alignment = ret_ty.abiAlignment(zcu).toLlvm(); | | |
| 5534 | | | |
| 5535 | if (val_is_undef and safety) { | | |
| 5536 | const llvm_ret_ty = operand.typeOfWip(&self.wip); | | |
| 5537 | const rp = try self.buildAlloca(llvm_ret_ty, alignment); | | |
| 5538 | const len = try o.builder.intValue(try o.lowerType(pt, Type.usize), ret_ty.abiSize(zcu)); | | |
| 5539 | _ = try self.wip.callMemSet( | | |
| 5540 | rp, | | |
| 5541 | alignment, | | |
| 5542 | try o.builder.intValue(.i8, 0xaa), | | |
| 5543 | len, | | |
| 5544 | .normal, | | |
| 5545 | self.disable_intrinsics, | | |
| 5546 | ); | | |
| 5547 | const owner_mod = self.ng.ownerModule(); | | |
| 5548 | if (owner_mod.valgrind) { | | |
| 5549 | try self.valgrindMarkUndef(rp, len); | | |
| 5550 | } | | |
| 5551 | _ = try self.wip.ret(try self.wip.load(.normal, abi_ret_ty, rp, alignment, "")); | | |
| 5552 | return; | | |
| 5553 | } | | |
| 5554 | | | |
| 5555 | if (isByRef(ret_ty, zcu)) { | | |
| 5556 | // operand is a pointer however self.ret_ptr is null so that means | | |
| 5557 | // we need to return a value. | | |
| 5558 | _ = try self.wip.ret(try self.wip.load(.normal, abi_ret_ty, operand, alignment, "")); | | |
| 5559 | return; | | |
| 5560 | } | | |
| 5561 | | | |
| 5562 | const llvm_ret_ty = operand.typeOfWip(&self.wip); | | |
| 5563 | if (abi_ret_ty == llvm_ret_ty) { | | |
| 5564 | _ = try self.wip.ret(operand); | | |
| 5565 | return; | | |
| 5566 | } | | |
| 5567 | | | |
| 5568 | const rp = try self.buildAlloca(llvm_ret_ty, alignment); | | |
| 5569 | _ = try self.wip.store(.normal, operand, rp, alignment); | | |
| 5570 | _ = try self.wip.ret(try self.wip.load(.normal, abi_ret_ty, rp, alignment, "")); | | |
| 5571 | return; | | |
| 5572 | } | | |
| 5573 | | | |
| 5574 | fn airRetLoad(self: *FuncGen, inst: Air.Inst.Index) !void { | | |
| 5575 | const o = self.ng.object; | | |
| 5576 | const pt = self.ng.pt; | | |
| 5577 | const zcu = pt.zcu; | | |
| 5578 | const ip = &zcu.intern_pool; | | |
| 5579 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | | |
| 5580 | const ptr_ty = self.typeOf(un_op); | | |
| 5581 | const ret_ty = ptr_ty.childType(zcu); | | |
| 5582 | const fn_info = zcu.typeToFunc(.fromInterned(ip.getNav(self.ng.nav_index).resolved.?.type)).?; | | |
| 5583 | if (!ret_ty.hasRuntimeBits(zcu)) { | | |
| 5584 | if (Type.fromInterned(fn_info.return_type).isError(zcu)) { | | |
| 5585 | // Functions with an empty error set are emitted with an error code | | |
| 5586 | // return type and return zero so they can be function pointers coerced | | |
| 5587 | // to functions that return anyerror. | | |
| 5588 | _ = try self.wip.ret(try o.builder.intValue(try o.errorIntType(pt), 0)); | | |
| 5589 | } else { | | |
| 5590 | _ = try self.wip.retVoid(); | | |
| 5591 | } | | |
| 5592 | return; | | |
| 5593 | } | | |
| 5594 | if (self.ret_ptr != .none) { | | |
| 5595 | _ = try self.wip.retVoid(); | | |
| 5596 | return; | | |
| 5597 | } | | |
| 5598 | const ptr = try self.resolveInst(un_op); | | |
| 5599 | const abi_ret_ty = try lowerFnRetTy(o, pt, fn_info); | | |
| 5600 | const alignment = ret_ty.abiAlignment(zcu).toLlvm(); | | |
| 5601 | _ = try self.wip.ret(try self.wip.load(.normal, abi_ret_ty, ptr, alignment, "")); | | |
| 5602 | return; | | |
| 5603 | } | | |
| 5604 | | | |
| 5605 | fn airCVaArg(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 5606 | const o = self.ng.object; | | |
| 5607 | const pt = self.ng.pt; | | |
| 5608 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 5609 | const list = try self.resolveInst(ty_op.operand); | | |
| 5610 | const arg_ty = ty_op.ty.toType(); | | |
| 5611 | const llvm_arg_ty = try o.lowerType(pt, arg_ty); | | |
| 5612 | | | |
| 5613 | return self.wip.vaArg(list, llvm_arg_ty, ""); | | |
| 5614 | } | | |
| 5615 | | | |
| 5616 | fn airCVaCopy(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 5617 | const o = self.ng.object; | | |
| 5618 | const pt = self.ng.pt; | | |
| 5619 | const zcu = pt.zcu; | | |
| 5620 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 5621 | const src_list = try self.resolveInst(ty_op.operand); | | |
| 5622 | const va_list_ty = ty_op.ty.toType(); | | |
| 5623 | const llvm_va_list_ty = try o.lowerType(pt, va_list_ty); | | |
| 5624 | | | |
| 5625 | const result_alignment = va_list_ty.abiAlignment(pt.zcu).toLlvm(); | | |
| 5626 | const dest_list = try self.buildAlloca(llvm_va_list_ty, result_alignment); | | |
| 5627 | | | |
| 5628 | _ = try self.wip.callIntrinsic(.normal, .none, .va_copy, &.{dest_list.typeOfWip(&self.wip)}, &.{ dest_list, src_list }, ""); | | |
| 5629 | return if (isByRef(va_list_ty, zcu)) | | |
| 5630 | dest_list | | |
| 5631 | else | | |
| 5632 | try self.wip.load(.normal, llvm_va_list_ty, dest_list, result_alignment, ""); | | |
| 5633 | } | | |
| 5634 | | | |
| 5635 | fn airCVaEnd(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 5636 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | | |
| 5637 | const src_list = try self.resolveInst(un_op); | | |
| 5638 | | | |
| 5639 | _ = try self.wip.callIntrinsic(.normal, .none, .va_end, &.{src_list.typeOfWip(&self.wip)}, &.{src_list}, ""); | | |
| 5640 | return .none; | | |
| 5641 | } | | |
| 5642 | | | |
| 5643 | fn airCVaStart(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 5644 | const o = self.ng.object; | | |
| 5645 | const pt = self.ng.pt; | | |
| 5646 | const zcu = pt.zcu; | | |
| 5647 | const va_list_ty = self.typeOfIndex(inst); | | |
| 5648 | const llvm_va_list_ty = try o.lowerType(pt, va_list_ty); | | |
| 5649 | | | |
| 5650 | const result_alignment = va_list_ty.abiAlignment(pt.zcu).toLlvm(); | | |
| 5651 | const dest_list = try self.buildAlloca(llvm_va_list_ty, result_alignment); | | |
| 5652 | | | |
| 5653 | _ = try self.wip.callIntrinsic(.normal, .none, .va_start, &.{dest_list.typeOfWip(&self.wip)}, &.{dest_list}, ""); | | |
| 5654 | return if (isByRef(va_list_ty, zcu)) | | |
| 5655 | dest_list | | |
| 5656 | else | | |
| 5657 | try self.wip.load(.normal, llvm_va_list_ty, dest_list, result_alignment, ""); | | |
| 5658 | } | | |
| 5659 | | | |
| 5660 | fn airCmp( | | |
| 5661 | self: *FuncGen, | | |
| 5662 | inst: Air.Inst.Index, | | |
| 5663 | op: math.CompareOperator, | | |
| 5664 | fast: Builder.FastMathKind, | | |
| 5665 | ) !Builder.Value { | | |
| 5666 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 5667 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 5668 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 5669 | const operand_ty = self.typeOf(bin_op.lhs); | | |
| 5670 | | | |
| 5671 | return self.cmp(fast, op, operand_ty, lhs, rhs); | | |
| 5672 | } | | |
| 5673 | | | |
| 5674 | fn airCmpVector(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) !Builder.Value { | | |
| 5675 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | | |
| 5676 | const extra = self.air.extraData(Air.VectorCmp, ty_pl.payload).data; | | |
| 5677 | | | |
| 5678 | const lhs = try self.resolveInst(extra.lhs); | | |
| 5679 | const rhs = try self.resolveInst(extra.rhs); | | |
| 5680 | const vec_ty = self.typeOf(extra.lhs); | | |
| 5681 | const cmp_op = extra.compareOperator(); | | |
| 5682 | | | |
| 5683 | return self.cmp(fast, cmp_op, vec_ty, lhs, rhs); | | |
| 5684 | } | | |
| 5685 | | | |
| 5686 | fn airCmpLtErrorsLen(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 5687 | const o = self.ng.object; | | |
| 5688 | const pt = self.ng.pt; | | |
| 5689 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | | |
| 5690 | const operand = try self.resolveInst(un_op); | | |
| 5691 | const llvm_fn = try o.getCmpLtErrorsLenFunction(pt); | | |
| 5692 | return self.wip.call( | | |
| 5693 | .normal, | | |
| 5694 | .fastcc, | | |
| 5695 | .none, | | |
| 5696 | llvm_fn.typeOf(&o.builder), | | |
| 5697 | llvm_fn.toValue(&o.builder), | | |
| 5698 | &.{operand}, | | |
| 5699 | "", | | |
| 5700 | ); | | |
| 5701 | } | | |
| 5702 | | | |
| 5703 | fn cmp( | | |
| 5704 | self: *FuncGen, | | |
| 5705 | fast: Builder.FastMathKind, | | |
| 5706 | op: math.CompareOperator, | | |
| 5707 | operand_ty: Type, | | |
| 5708 | lhs: Builder.Value, | | |
| 5709 | rhs: Builder.Value, | | |
| 5710 | ) Allocator.Error!Builder.Value { | | |
| 5711 | const o = self.ng.object; | | |
| 5712 | const pt = self.ng.pt; | | |
| 5713 | const zcu = pt.zcu; | | |
| 5714 | const scalar_ty = operand_ty.scalarType(zcu); | | |
| 5715 | const int_ty = switch (scalar_ty.zigTypeTag(zcu)) { | | |
| 5716 | .@"enum" => scalar_ty.intTagType(zcu), | | |
| 5717 | .int, .bool, .pointer, .error_set => scalar_ty, | | |
| 5718 | .optional => blk: { | | |
| 5719 | const payload_ty = operand_ty.optionalChild(zcu); | | |
| 5720 | if (!payload_ty.hasRuntimeBits(zcu) or | | |
| 5721 | operand_ty.optionalReprIsPayload(zcu)) | | |
| 5722 | { | | |
| 5723 | break :blk operand_ty; | | |
| 5724 | } | | |
| 5725 | // We need to emit instructions to check for equality/inequality | | |
| 5726 | // of optionals that are not pointers. | | |
| 5727 | const is_by_ref = isByRef(scalar_ty, zcu); | | |
| 5728 | const opt_llvm_ty = try o.lowerType(pt, scalar_ty); | | |
| 5729 | const lhs_non_null = try self.optCmpNull(.ne, opt_llvm_ty, lhs, is_by_ref, .normal); | | |
| 5730 | const rhs_non_null = try self.optCmpNull(.ne, opt_llvm_ty, rhs, is_by_ref, .normal); | | |
| 5731 | const llvm_i2 = try o.builder.intType(2); | | |
| 5732 | const lhs_non_null_i2 = try self.wip.cast(.zext, lhs_non_null, llvm_i2, ""); | | |
| 5733 | const rhs_non_null_i2 = try self.wip.cast(.zext, rhs_non_null, llvm_i2, ""); | | |
| 5734 | const lhs_shifted = try self.wip.bin(.shl, lhs_non_null_i2, try o.builder.intValue(llvm_i2, 1), ""); | | |
| 5735 | const lhs_rhs_ored = try self.wip.bin(.@"or", lhs_shifted, rhs_non_null_i2, ""); | | |
| 5736 | const both_null_block = try self.wip.block(1, "BothNull"); | | |
| 5737 | const mixed_block = try self.wip.block(1, "Mixed"); | | |
| 5738 | const both_pl_block = try self.wip.block(1, "BothNonNull"); | | |
| 5739 | const end_block = try self.wip.block(3, "End"); | | |
| 5740 | var wip_switch = try self.wip.@"switch"(lhs_rhs_ored, mixed_block, 2, .none); | | |
| 5741 | defer wip_switch.finish(&self.wip); | | |
| 5742 | try wip_switch.addCase( | | |
| 5743 | try o.builder.intConst(llvm_i2, 0b00), | | |
| 5744 | both_null_block, | | |
| 5745 | &self.wip, | | |
| 5746 | ); | | |
| 5747 | try wip_switch.addCase( | | |
| 5748 | try o.builder.intConst(llvm_i2, 0b11), | | |
| 5749 | both_pl_block, | | |
| 5750 | &self.wip, | | |
| 5751 | ); | | |
| 5752 | | | |
| 5753 | self.wip.cursor = .{ .block = both_null_block }; | | |
| 5754 | _ = try self.wip.br(end_block); | | |
| 5755 | | | |
| 5756 | self.wip.cursor = .{ .block = mixed_block }; | | |
| 5757 | _ = try self.wip.br(end_block); | | |
| 5758 | | | |
| 5759 | self.wip.cursor = .{ .block = both_pl_block }; | | |
| 5760 | const lhs_payload = try self.optPayloadHandle(opt_llvm_ty, lhs, scalar_ty, true); | | |
| 5761 | const rhs_payload = try self.optPayloadHandle(opt_llvm_ty, rhs, scalar_ty, true); | | |
| 5762 | const payload_cmp = try self.cmp(fast, op, payload_ty, lhs_payload, rhs_payload); | | |
| 5763 | _ = try self.wip.br(end_block); | | |
| 5764 | const both_pl_block_end = self.wip.cursor.block; | | |
| 5765 | | | |
| 5766 | self.wip.cursor = .{ .block = end_block }; | | |
| 5767 | const llvm_i1_0 = Builder.Value.false; | | |
| 5768 | const llvm_i1_1 = Builder.Value.true; | | |
| 5769 | const incoming_values: [3]Builder.Value = .{ | | |
| 5770 | switch (op) { | | |
| 5771 | .eq => llvm_i1_1, | | |
| 5772 | .neq => llvm_i1_0, | | |
| 5773 | else => unreachable, | | |
| 5774 | }, | | |
| 5775 | switch (op) { | | |
| 5776 | .eq => llvm_i1_0, | | |
| 5777 | .neq => llvm_i1_1, | | |
| 5778 | else => unreachable, | | |
| 5779 | }, | | |
| 5780 | payload_cmp, | | |
| 5781 | }; | | |
| 5782 | | | |
| 5783 | const phi = try self.wip.phi(.i1, ""); | | |
| 5784 | phi.finish( | | |
| 5785 | &incoming_values, | | |
| 5786 | &.{ both_null_block, mixed_block, both_pl_block_end }, | | |
| 5787 | &self.wip, | | |
| 5788 | ); | | |
| 5789 | return phi.toValue(); | | |
| 5790 | }, | | |
| 5791 | .float => return self.buildFloatCmp(fast, op, operand_ty, .{ lhs, rhs }), | | |
| 5792 | .@"struct", .@"union" => scalar_ty.bitpackBackingInt(zcu), | | |
| 5793 | else => unreachable, | | |
| 5794 | }; | | |
| 5795 | const is_signed = int_ty.isSignedInt(zcu); | | |
| 5796 | const cond: Builder.IntegerCondition = switch (op) { | | |
| 5797 | .eq => .eq, | | |
| 5798 | .neq => .ne, | | |
| 5799 | .lt => if (is_signed) .slt else .ult, | | |
| 5800 | .lte => if (is_signed) .sle else .ule, | | |
| 5801 | .gt => if (is_signed) .sgt else .ugt, | | |
| 5802 | .gte => if (is_signed) .sge else .uge, | | |
| 5803 | }; | | |
| 5804 | return self.wip.icmp(cond, lhs, rhs, ""); | | |
| 5805 | } | | |
| 5806 | | | |
| 5807 | fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 5808 | const block = self.air.unwrapBlock(inst); | | |
| 5809 | return self.lowerBlock(inst, null, block.body); | | |
| 5810 | } | | |
| 5811 | | | |
| 5812 | fn lowerBlock( | | |
| 5813 | self: *FuncGen, | | |
| 5814 | inst: Air.Inst.Index, | | |
| 5815 | maybe_inline_func: ?InternPool.Index, | | |
| 5816 | body: []const Air.Inst.Index, | | |
| 5817 | ) !Builder.Value { | | |
| 5818 | const o = self.ng.object; | | |
| 5819 | const pt = self.ng.pt; | | |
| 5820 | const zcu = pt.zcu; | | |
| 5821 | const inst_ty = self.typeOfIndex(inst); | | |
| 5822 | | | |
| 5823 | if (inst_ty.isNoReturn(zcu)) { | | |
| 5824 | try self.genBodyDebugScope(maybe_inline_func, body, .none); | | |
| 5825 | return .none; | | |
| 5826 | } | | |
| 5827 | | | |
| 5828 | const have_block_result = inst_ty.hasRuntimeBits(zcu); | | |
| 5829 | | | |
| 5830 | var breaks: BreakList = if (have_block_result) .{ .list = .{} } else .{ .len = 0 }; | | |
| 5831 | defer if (have_block_result) breaks.list.deinit(self.gpa); | | |
| 5832 | | | |
| 5833 | const parent_bb = try self.wip.block(0, "Block"); | | |
| 5834 | try self.blocks.putNoClobber(self.gpa, inst, .{ | | |
| 5835 | .parent_bb = parent_bb, | | |
| 5836 | .breaks = &breaks, | | |
| 5837 | }); | | |
| 5838 | defer assert(self.blocks.remove(inst)); | | |
| 5839 | | | |
| 5840 | try self.genBodyDebugScope(maybe_inline_func, body, .none); | | |
| 5841 | | | |
| 5842 | self.wip.cursor = .{ .block = parent_bb }; | | |
| 5843 | | | |
| 5844 | // Create a phi node only if the block returns a value. | | |
| 5845 | if (have_block_result) { | | |
| 5846 | const raw_llvm_ty = try o.lowerType(pt, inst_ty); | | |
| 5847 | const llvm_ty: Builder.Type = ty: { | | |
| 5848 | // If the zig tag type is a function, this represents an actual function body; not | | |
| 5849 | // a pointer to it. LLVM IR allows the call instruction to use function bodies instead | | |
| 5850 | // of function pointers, however the phi makes it a runtime value and therefore | | |
| 5851 | // the LLVM type has to be wrapped in a pointer. | | |
| 5852 | if (inst_ty.zigTypeTag(zcu) == .@"fn" or isByRef(inst_ty, zcu)) { | | |
| 5853 | break :ty .ptr; | | |
| 5854 | } | | |
| 5855 | break :ty raw_llvm_ty; | | |
| 5856 | }; | | |
| 5857 | | | |
| 5858 | parent_bb.ptr(&self.wip).incoming = @intCast(breaks.list.len); | | |
| 5859 | const phi = try self.wip.phi(llvm_ty, ""); | | |
| 5860 | phi.finish(breaks.list.items(.val), breaks.list.items(.bb), &self.wip); | | |
| 5861 | return phi.toValue(); | | |
| 5862 | } else { | | |
| 5863 | parent_bb.ptr(&self.wip).incoming = @intCast(breaks.len); | | |
| 5864 | return .none; | | |
| 5865 | } | | |
| 5866 | } | | |
| 5867 | | | |
| 5868 | fn airBr(self: *FuncGen, inst: Air.Inst.Index) !void { | | |
| 5869 | const zcu = self.ng.pt.zcu; | | |
| 5870 | const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br; | | |
| 5871 | const block = self.blocks.get(branch.block_inst).?; | | |
| 5872 | | | |
| 5873 | // Add the values to the lists only if the break provides a value. | | |
| 5874 | const operand_ty = self.typeOf(branch.operand); | | |
| 5875 | if (operand_ty.hasRuntimeBits(zcu)) { | | |
| 5876 | const val = try self.resolveInst(branch.operand); | | |
| 5877 | | | |
| 5878 | // For the phi node, we need the basic blocks and the values of the | | |
| 5879 | // break instructions. | | |
| 5880 | try block.breaks.list.append(self.gpa, .{ .bb = self.wip.cursor.block, .val = val }); | | |
| 5881 | } else block.breaks.len += 1; | | |
| 5882 | _ = try self.wip.br(block.parent_bb); | | |
| 5883 | } | | |
| 5884 | | | |
| 5885 | fn airRepeat(self: *FuncGen, inst: Air.Inst.Index) !void { | | |
| 5886 | const repeat = self.air.instructions.items(.data)[@intFromEnum(inst)].repeat; | | |
| 5887 | const loop_bb = self.loops.get(repeat.loop_inst).?; | | |
| 5888 | loop_bb.ptr(&self.wip).incoming += 1; | | |
| 5889 | _ = try self.wip.br(loop_bb); | | |
| 5890 | } | | |
| 5891 | | | |
| 5892 | fn lowerSwitchDispatch( | | |
| 5893 | self: *FuncGen, | | |
| 5894 | switch_inst: Air.Inst.Index, | | |
| 5895 | cond_ref: Air.Inst.Ref, | | |
| 5896 | dispatch_info: SwitchDispatchInfo, | | |
| 5897 | ) !void { | | |
| 5898 | const o = self.ng.object; | | |
| 5899 | const pt = self.ng.pt; | | |
| 5900 | const zcu = pt.zcu; | | |
| 5901 | const cond_ty = self.typeOf(cond_ref); | | |
| 5902 | const switch_br = self.air.unwrapSwitch(switch_inst); | | |
| 5903 | | | |
| 5904 | if (try self.air.value(cond_ref, pt)) |cond_val| { | | |
| 5905 | // Comptime-known dispatch. Iterate the cases to find the correct | | |
| 5906 | // one, and branch to the corresponding element of `case_blocks`. | | |
| 5907 | var it = switch_br.iterateCases(); | | |
| 5908 | const target_case_idx = target: while (it.next()) |case| { | | |
| 5909 | for (case.items) |item| { | | |
| 5910 | const val = Value.fromInterned(item.toInterned().?); | | |
| 5911 | if (cond_val.compareHetero(.eq, val, zcu)) break :target case.idx; | | |
| 5912 | } | | |
| 5913 | for (case.ranges) |range| { | | |
| 5914 | const low = Value.fromInterned(range[0].toInterned().?); | | |
| 5915 | const high = Value.fromInterned(range[1].toInterned().?); | | |
| 5916 | if (cond_val.compareHetero(.gte, low, zcu) and | | |
| 5917 | cond_val.compareHetero(.lte, high, zcu)) | | |
| 5918 | { | | |
| 5919 | break :target case.idx; | | |
| 5920 | } | | |
| 5921 | } | | |
| 5922 | } else dispatch_info.case_blocks.len - 1; | | |
| 5923 | const target_block = dispatch_info.case_blocks[target_case_idx]; | | |
| 5924 | target_block.ptr(&self.wip).incoming += 1; | | |
| 5925 | _ = try self.wip.br(target_block); | | |
| 5926 | return; | | |
| 5927 | } | | |
| 5928 | | | |
| 5929 | // Runtime-known dispatch. | | |
| 5930 | const cond = try self.resolveInst(cond_ref); | | |
| 5931 | | | |
| 5932 | if (dispatch_info.jmp_table) |jmp_table| { | | |
| 5933 | // We should use the constructed jump table. | | |
| 5934 | // First, check the bounds to branch to the `else` case if needed. | | |
| 5935 | const inbounds = try self.wip.bin( | | |
| 5936 | .@"and", | | |
| 5937 | try self.cmp(.normal, .gte, cond_ty, cond, jmp_table.min.toValue()), | | |
| 5938 | try self.cmp(.normal, .lte, cond_ty, cond, jmp_table.max.toValue()), | | |
| 5939 | "", | | |
| 5940 | ); | | |
| 5941 | const jmp_table_block = try self.wip.block(1, "Then"); | | |
| 5942 | const else_block = dispatch_info.case_blocks[dispatch_info.case_blocks.len - 1]; | | |
| 5943 | else_block.ptr(&self.wip).incoming += 1; | | |
| 5944 | _ = try self.wip.brCond(inbounds, jmp_table_block, else_block, switch (jmp_table.in_bounds_hint) { | | |
| 5945 | .none => .none, | | |
| 5946 | .unpredictable => .unpredictable, | | |
| 5947 | .likely => .then_likely, | | |
| 5948 | .unlikely => .else_likely, | | |
| 5949 | }); | | |
| 5950 | | | |
| 5951 | self.wip.cursor = .{ .block = jmp_table_block }; | | |
| 5952 | | | |
| 5953 | // Figure out the list of blocks we might branch to. | | |
| 5954 | // This includes all case blocks, but it might not include the `else` block if | | |
| 5955 | // the table is dense. | | |
| 5956 | const target_blocks_len = dispatch_info.case_blocks.len - @intFromBool(!jmp_table.table_includes_else); | | |
| 5957 | const target_blocks = dispatch_info.case_blocks[0..target_blocks_len]; | | |
| 5958 | | | |
| 5959 | // Make sure to cast the index to a usize so it's not treated as negative! | | |
| 5960 | const table_index = try self.wip.conv( | | |
| 5961 | .unsigned, | | |
| 5962 | try self.wip.bin(.@"sub nuw", cond, jmp_table.min.toValue(), ""), | | |
| 5963 | try o.lowerType(pt, .usize), | | |
| 5964 | "", | | |
| 5965 | ); | | |
| 5966 | const target_ptr_ptr = try self.wip.gep( | | |
| 5967 | .inbounds, | | |
| 5968 | .ptr, | | |
| 5969 | jmp_table.table.toValue(), | | |
| 5970 | &.{table_index}, | | |
| 5971 | "", | | |
| 5972 | ); | | |
| 5973 | const target_ptr = try self.wip.load(.normal, .ptr, target_ptr_ptr, .default, ""); | | |
| 5974 | | | |
| 5975 | // Do the branch! | | |
| 5976 | _ = try self.wip.indirectbr(target_ptr, target_blocks); | | |
| 5977 | | | |
| 5978 | // Mark all target blocks as having one more incoming branch. | | |
| 5979 | for (target_blocks) |case_block| { | | |
| 5980 | case_block.ptr(&self.wip).incoming += 1; | | |
| 5981 | } | | |
| 5982 | | | |
| 5983 | return; | | |
| 5984 | } | | |
| 5985 | | | |
| 5986 | // We must lower to an actual LLVM `switch` instruction. | | |
| 5987 | // The switch prongs will correspond to our scalar cases. Ranges will | | |
| 5988 | // be handled by conditional branches in the `else` prong. | | |
| 5989 | | | |
| 5990 | const llvm_usize = try o.lowerType(pt, Type.usize); | | |
| 5991 | const cond_int = if (cond.typeOfWip(&self.wip).isPointer(&o.builder)) | | |
| 5992 | try self.wip.cast(.ptrtoint, cond, llvm_usize, "") | | |
| 5993 | else | | |
| 5994 | cond; | | |
| 5995 | | | |
| 5996 | const llvm_cases_len, const last_range_case = info: { | | |
| 5997 | var llvm_cases_len: u32 = 0; | | |
| 5998 | var last_range_case: ?u32 = null; | | |
| 5999 | var it = switch_br.iterateCases(); | | |
| 6000 | while (it.next()) |case| { | | |
| 6001 | if (case.ranges.len > 0) last_range_case = case.idx; | | |
| 6002 | llvm_cases_len += @intCast(case.items.len); | | |
| 6003 | } | | |
| 6004 | break :info .{ llvm_cases_len, last_range_case }; | | |
| 6005 | }; | | |
| 6006 | | | |
| 6007 | // The `else` of the LLVM `switch` is the actual `else` prong only | | |
| 6008 | // if there are no ranges. Otherwise, the `else` will have a | | |
| 6009 | // conditional chain before the "true" `else` prong. | | |
| 6010 | const llvm_else_block = if (last_range_case == null) | | |
| 6011 | dispatch_info.case_blocks[dispatch_info.case_blocks.len - 1] | | |
| 6012 | else | | |
| 6013 | try self.wip.block(0, "RangeTest"); | | |
| 6014 | | | |
| 6015 | llvm_else_block.ptr(&self.wip).incoming += 1; | | |
| 6016 | | | |
| 6017 | var wip_switch = try self.wip.@"switch"(cond_int, llvm_else_block, llvm_cases_len, dispatch_info.switch_weights); | | |
| 6018 | defer wip_switch.finish(&self.wip); | | |
| 6019 | | | |
| 6020 | // Construct the actual cases. Set the cursor to the `else` block so | | |
| 6021 | // we can construct ranges at the same time as scalar cases. | | |
| 6022 | self.wip.cursor = .{ .block = llvm_else_block }; | | |
| 6023 | | | |
| 6024 | var it = switch_br.iterateCases(); | | |
| 6025 | while (it.next()) |case| { | | |
| 6026 | const case_block = dispatch_info.case_blocks[case.idx]; | | |
| 6027 | | | |
| 6028 | for (case.items) |item| { | | |
| 6029 | const llvm_item = (try self.resolveInst(item)).toConst().?; | | |
| 6030 | const llvm_int_item = if (llvm_item.typeOf(&o.builder).isPointer(&o.builder)) | | |
| 6031 | try o.builder.castConst(.ptrtoint, llvm_item, llvm_usize) | | |
| 6032 | else | | |
| 6033 | llvm_item; | | |
| 6034 | try wip_switch.addCase(llvm_int_item, case_block, &self.wip); | | |
| 6035 | } | | |
| 6036 | case_block.ptr(&self.wip).incoming += @intCast(case.items.len); | | |
| 6037 | | | |
| 6038 | if (case.ranges.len == 0) continue; | | |
| 6039 | | | |
| 6040 | // Add a conditional for the ranges, directing to the relevant bb. | | |
| 6041 | // We don't need to consider `cold` branch hints since that information is stored | | |
| 6042 | // in the target bb body, but we do care about likely/unlikely/unpredictable. | | |
| 6043 | | | |
| 6044 | const hint = switch_br.getHint(case.idx); | | |
| 6045 | | | |
| 6046 | var range_cond: ?Builder.Value = null; | | |
| 6047 | for (case.ranges) |range| { | | |
| 6048 | const llvm_min = try self.resolveInst(range[0]); | | |
| 6049 | const llvm_max = try self.resolveInst(range[1]); | | |
| 6050 | const cond_part = try self.wip.bin( | | |
| 6051 | .@"and", | | |
| 6052 | try self.cmp(.normal, .gte, cond_ty, cond, llvm_min), | | |
| 6053 | try self.cmp(.normal, .lte, cond_ty, cond, llvm_max), | | |
| 6054 | "", | | |
| 6055 | ); | | |
| 6056 | if (range_cond) |prev| { | | |
| 6057 | range_cond = try self.wip.bin(.@"or", prev, cond_part, ""); | | |
| 6058 | } else range_cond = cond_part; | | |
| 6059 | } | | |
| 6060 | | | |
| 6061 | // If the check fails, we either branch to the "true" `else` case, | | |
| 6062 | // or to the next range condition. | | |
| 6063 | const range_else_block = if (case.idx == last_range_case.?) | | |
| 6064 | dispatch_info.case_blocks[dispatch_info.case_blocks.len - 1] | | |
| 6065 | else | | |
| 6066 | try self.wip.block(0, "RangeTest"); | | |
| 6067 | | | |
| 6068 | _ = try self.wip.brCond(range_cond.?, case_block, range_else_block, switch (hint) { | | |
| 6069 | .none, .cold => .none, | | |
| 6070 | .unpredictable => .unpredictable, | | |
| 6071 | .likely => .then_likely, | | |
| 6072 | .unlikely => .else_likely, | | |
| 6073 | }); | | |
| 6074 | case_block.ptr(&self.wip).incoming += 1; | | |
| 6075 | range_else_block.ptr(&self.wip).incoming += 1; | | |
| 6076 | | | |
| 6077 | // Construct the next range conditional (if any) in the false branch. | | |
| 6078 | self.wip.cursor = .{ .block = range_else_block }; | | |
| 6079 | } | | |
| 6080 | } | | |
| 6081 | | | |
| 6082 | fn airSwitchDispatch(self: *FuncGen, inst: Air.Inst.Index) !void { | | |
| 6083 | const br = self.air.instructions.items(.data)[@intFromEnum(inst)].br; | | |
| 6084 | const dispatch_info = self.switch_dispatch_info.get(br.block_inst).?; | | |
| 6085 | return self.lowerSwitchDispatch(br.block_inst, br.operand, dispatch_info); | | |
| 6086 | } | | |
| 6087 | | | |
| 6088 | fn airCondBr(self: *FuncGen, inst: Air.Inst.Index) !void { | | |
| 6089 | const cond_br = self.air.unwrapCondBr(inst); | | |
| 6090 | const cond = try self.resolveInst(cond_br.condition); | | |
| 6091 | const then_body = cond_br.then_body; | | |
| 6092 | const else_body = cond_br.else_body; | | |
| 6093 | | | |
| 6094 | const Hint = enum { | | |
| 6095 | none, | | |
| 6096 | unpredictable, | | |
| 6097 | then_likely, | | |
| 6098 | else_likely, | | |
| 6099 | then_cold, | | |
| 6100 | else_cold, | | |
| 6101 | }; | | |
| 6102 | const hint: Hint = switch (cond_br.branch_hints.true) { | | |
| 6103 | .none => switch (cond_br.branch_hints.false) { | | |
| 6104 | .none => .none, | | |
| 6105 | .likely => .else_likely, | | |
| 6106 | .unlikely => .then_likely, | | |
| 6107 | .cold => .else_cold, | | |
| 6108 | .unpredictable => .unpredictable, | | |
| 6109 | }, | | |
| 6110 | .likely => switch (cond_br.branch_hints.false) { | | |
| 6111 | .none => .then_likely, | | |
| 6112 | .likely => .unpredictable, | | |
| 6113 | .unlikely => .then_likely, | | |
| 6114 | .cold => .else_cold, | | |
| 6115 | .unpredictable => .unpredictable, | | |
| 6116 | }, | | |
| 6117 | .unlikely => switch (cond_br.branch_hints.false) { | | |
| 6118 | .none => .else_likely, | | |
| 6119 | .likely => .else_likely, | | |
| 6120 | .unlikely => .unpredictable, | | |
| 6121 | .cold => .else_cold, | | |
| 6122 | .unpredictable => .unpredictable, | | |
| 6123 | }, | | |
| 6124 | .cold => .then_cold, | | |
| 6125 | .unpredictable => .unpredictable, | | |
| 6126 | }; | | |
| 6127 | | | |
| 6128 | const then_block = try self.wip.block(1, "Then"); | | |
| 6129 | const else_block = try self.wip.block(1, "Else"); | | |
| 6130 | _ = try self.wip.brCond(cond, then_block, else_block, switch (hint) { | | |
| 6131 | .none, .then_cold, .else_cold => .none, | | |
| 6132 | .unpredictable => .unpredictable, | | |
| 6133 | .then_likely => .then_likely, | | |
| 6134 | .else_likely => .else_likely, | | |
| 6135 | }); | | |
| 6136 | | | |
| 6137 | self.wip.cursor = .{ .block = then_block }; | | |
| 6138 | if (hint == .then_cold) _ = try self.wip.callIntrinsicAssumeCold(); | | |
| 6139 | try self.genBodyDebugScope(null, then_body, cond_br.branch_hints.then_cov); | | |
| 6140 | | | |
| 6141 | self.wip.cursor = .{ .block = else_block }; | | |
| 6142 | if (hint == .else_cold) _ = try self.wip.callIntrinsicAssumeCold(); | | |
| 6143 | try self.genBodyDebugScope(null, else_body, cond_br.branch_hints.else_cov); | | |
| 6144 | | | |
| 6145 | // No need to reset the insert cursor since this instruction is noreturn. | | |
| 6146 | } | | |
| 6147 | | | |
| 6148 | fn airTry(self: *FuncGen, inst: Air.Inst.Index, err_cold: bool) !Builder.Value { | | |
| 6149 | const unwrapped_try = self.air.unwrapTry(inst); | | |
| 6150 | const err_union = try self.resolveInst(unwrapped_try.error_union); | | |
| 6151 | const body = unwrapped_try.else_body; | | |
| 6152 | const err_union_ty = self.typeOf(unwrapped_try.error_union); | | |
| 6153 | const is_unused = self.liveness.isUnused(inst); | | |
| 6154 | return lowerTry(self, err_union, body, err_union_ty, false, .none, false, is_unused, err_cold); | | |
| 6155 | } | | |
| 6156 | | | |
| 6157 | fn airTryPtr(self: *FuncGen, inst: Air.Inst.Index, err_cold: bool) !Builder.Value { | | |
| 6158 | const zcu = self.ng.pt.zcu; | | |
| 6159 | const unwrapped_try = self.air.unwrapTryPtr(inst); | | |
| 6160 | const err_union_ptr = try self.resolveInst(unwrapped_try.error_union_ptr); | | |
| 6161 | const body = unwrapped_try.else_body; | | |
| 6162 | const err_union_ptr_ty = self.typeOf(unwrapped_try.error_union_ptr); | | |
| 6163 | const err_union_ty = err_union_ptr_ty.childType(zcu); | | |
| 6164 | const is_unused = self.liveness.isUnused(inst); | | |
| 6165 | | | |
| 6166 | self.maybeMarkAllowZeroAccess(self.typeOf(unwrapped_try.error_union_ptr).ptrInfo(zcu)); | | |
| 6167 | | | |
| 6168 | return lowerTry(self, err_union_ptr, body, err_union_ty, true, err_union_ptr_ty.ptrAlignment(zcu), true, is_unused, err_cold); | | |
| 6169 | } | | |
| 6170 | | | |
| 6171 | fn lowerTry( | | |
| 6172 | fg: *FuncGen, | | |
| 6173 | err_union: Builder.Value, | | |
| 6174 | body: []const Air.Inst.Index, | | |
| 6175 | err_union_ty: Type, | | |
| 6176 | operand_is_ptr: bool, | | |
| 6177 | operand_ptr_align: InternPool.Alignment, | | |
| 6178 | can_elide_load: bool, | | |
| 6179 | is_unused: bool, | | |
| 6180 | err_cold: bool, | | |
| 6181 | ) !Builder.Value { | | |
| 6182 | const o = fg.ng.object; | | |
| 6183 | const pt = fg.ng.pt; | | |
| 6184 | const zcu = pt.zcu; | | |
| 6185 | const payload_ty = err_union_ty.errorUnionPayload(zcu); | | |
| 6186 | const payload_has_bits = payload_ty.hasRuntimeBits(zcu); | | |
| 6187 | const err_union_llvm_ty = try o.lowerType(pt, err_union_ty); | | |
| 6188 | const error_type = try o.errorIntType(pt); | | |
| 6189 | | | |
| 6190 | const err_set_align: InternPool.Alignment, const payload_align: InternPool.Alignment = if (operand_is_ptr) .{ | | |
| 6191 | operand_ptr_align.minStrict(Type.anyerror.abiAlignment(zcu)), | | |
| 6192 | operand_ptr_align.minStrict(payload_ty.abiAlignment(zcu)), | | |
| 6193 | } else .{ .none, .none }; | | |
| 6194 | | | |
| 6195 | if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) { | | |
| 6196 | const loaded = loaded: { | | |
| 6197 | const access_kind: Builder.MemoryAccessKind = | | |
| 6198 | if (err_union_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; | | |
| 6199 | | | |
| 6200 | if (!payload_has_bits) { | | |
| 6201 | break :loaded if (operand_is_ptr) | | |
| 6202 | try fg.wip.load(access_kind, error_type, err_union, err_set_align.toLlvm(), "") | | |
| 6203 | else | | |
| 6204 | err_union; | | |
| 6205 | } | | |
| 6206 | const err_field_index = try errUnionErrorOffset(payload_ty, pt); | | |
| 6207 | if (operand_is_ptr or isByRef(err_union_ty, zcu)) { | | |
| 6208 | const err_field_ptr = | | |
| 6209 | try fg.wip.gepStruct(err_union_llvm_ty, err_union, err_field_index, ""); | | |
| 6210 | break :loaded try fg.wip.load( | | |
| 6211 | if (operand_is_ptr) access_kind else .normal, | | |
| 6212 | error_type, | | |
| 6213 | err_field_ptr, | | |
| 6214 | err_set_align.toLlvm(), | | |
| 6215 | "", | | |
| 6216 | ); | | |
| 6217 | } | | |
| 6218 | break :loaded try fg.wip.extractValue(err_union, &.{err_field_index}, ""); | | |
| 6219 | }; | | |
| 6220 | const zero = try o.builder.intValue(error_type, 0); | | |
| 6221 | const is_err = try fg.wip.icmp(.ne, loaded, zero, ""); | | |
| 6222 | | | |
| 6223 | const return_block = try fg.wip.block(1, "TryRet"); | | |
| 6224 | const continue_block = try fg.wip.block(1, "TryCont"); | | |
| 6225 | _ = try fg.wip.brCond(is_err, return_block, continue_block, if (err_cold) .none else .else_likely); | | |
| 6226 | | | |
| 6227 | fg.wip.cursor = .{ .block = return_block }; | | |
| 6228 | if (err_cold) _ = try fg.wip.callIntrinsicAssumeCold(); | | |
| 6229 | try fg.genBodyDebugScope(null, body, .poi); | | |
| 6230 | | | |
| 6231 | fg.wip.cursor = .{ .block = continue_block }; | | |
| 6232 | } | | |
| 6233 | if (is_unused) return .none; | | |
| 6234 | if (!payload_has_bits) return if (operand_is_ptr) err_union else .none; | | |
| 6235 | const offset = try errUnionPayloadOffset(payload_ty, pt); | | |
| 6236 | if (operand_is_ptr) { | | |
| 6237 | return fg.wip.gepStruct(err_union_llvm_ty, err_union, offset, ""); | | |
| 6238 | } else if (isByRef(err_union_ty, zcu)) { | | |
| 6239 | const payload_ptr = try fg.wip.gepStruct(err_union_llvm_ty, err_union, offset, ""); | | |
| 6240 | if (isByRef(payload_ty, zcu)) { | | |
| 6241 | if (can_elide_load) | | |
| 6242 | return payload_ptr; | | |
| 6243 | | | |
| 6244 | return fg.loadByRef(payload_ptr, payload_ty, payload_align.toLlvm(), .normal); | | |
| 6245 | } | | |
| 6246 | const load_ty = err_union_llvm_ty.structFields(&o.builder)[offset]; | | |
| 6247 | return fg.wip.load(.normal, load_ty, payload_ptr, payload_align.toLlvm(), ""); | | |
| 6248 | } | | |
| 6249 | return fg.wip.extractValue(err_union, &.{offset}, ""); | | |
| 6250 | } | | |
| 6251 | | | |
| 6252 | fn airSwitchBr(self: *FuncGen, inst: Air.Inst.Index, is_dispatch_loop: bool) !void { | | |
| 6253 | const o = self.ng.object; | | |
| 6254 | const pt = self.ng.pt; | | |
| 6255 | const zcu = pt.zcu; | | |
| 6256 | | | |
| 6257 | const switch_br = self.air.unwrapSwitch(inst); | | |
| 6258 | | | |
| 6259 | // For `loop_switch_br`, we need these BBs prepared ahead of time to generate dispatches. | | |
| 6260 | // For `switch_br`, they allow us to sometimes generate better IR by sharing a BB between | | |
| 6261 | // scalar and range cases in the same prong. | | |
| 6262 | // +1 for `else` case. This is not the same as the LLVM `else` prong, as that may first contain | | |
| 6263 | // conditionals to handle ranges. | | |
| 6264 | const case_blocks = try self.gpa.alloc(Builder.Function.Block.Index, switch_br.cases_len + 1); | | |
| 6265 | defer self.gpa.free(case_blocks); | | |
| 6266 | // We set incoming as 0 for now, and increment it as we construct dispatches. | | |
| 6267 | for (case_blocks[0 .. case_blocks.len - 1]) |*b| b.* = try self.wip.block(0, "Case"); | | |
| 6268 | case_blocks[case_blocks.len - 1] = try self.wip.block(0, "Default"); | | |
| 6269 | | | |
| 6270 | // There's a special case here to manually generate a jump table in some cases. | | |
| 6271 | // | | |
| 6272 | // Labeled switch in Zig is intended to follow the "direct threading" pattern. We would ideally use a jump | | |
| 6273 | // table, and each `continue` has its own indirect `jmp`, to allow the branch predictor to more accurately | | |
| 6274 | // use data patterns to predict future dispatches. The problem, however, is that LLVM emits fascinatingly | | |
| 6275 | // bad asm for this. Not only does it not share the jump table -- which we really need it to do to prevent | | |
| 6276 | // destroying the cache -- but it also actually generates slightly different jump tables for each case, | | |
| 6277 | // and *a separate conditional branch beforehand* to handle dispatching back to the case we're currently | | |
| 6278 | // within(!!). | | |
| 6279 | // | | |
| 6280 | // This asm is really, really, not what we want. As such, we will construct the jump table manually where | | |
| 6281 | // appropriate (the values are dense and relatively few), and use it when lowering dispatches. | | |
| 6282 | | | |
| 6283 | const jmp_table: ?SwitchDispatchInfo.JmpTable = jmp_table: { | | |
| 6284 | if (!is_dispatch_loop) break :jmp_table null; | | |
| 6285 | | | |
| 6286 | // Workaround for: | | |
| 6287 | // * https://github.com/llvm/llvm-project/blob/56905dab7da50bccfcceaeb496b206ff476127e1/llvm/lib/MC/WasmObjectWriter.cpp#L560 | | |
| 6288 | // * https://github.com/llvm/llvm-project/blob/56905dab7da50bccfcceaeb496b206ff476127e1/llvm/test/MC/WebAssembly/blockaddress.ll | | |
| 6289 | if (zcu.comp.getTarget().cpu.arch.isWasm()) break :jmp_table null; | | |
| 6290 | | | |
| 6291 | // On a 64-bit target, 1024 pointers in our jump table is about 8K of pointers. This seems just | | |
| 6292 | // about acceptable - it won't fill L1d cache on most CPUs. | | |
| 6293 | const max_table_len = 1024; | | |
| 6294 | | | |
| 6295 | const cond_ty = self.typeOf(switch_br.operand); | | |
| 6296 | switch (cond_ty.zigTypeTag(zcu)) { | | |
| 6297 | .bool, .pointer => break :jmp_table null, | | |
| 6298 | .@"enum", .int, .error_set, .@"struct", .@"union" => {}, | | |
| 6299 | else => unreachable, | | |
| 6300 | } | | |
| 6301 | | | |
| 6302 | if (cond_ty.intInfo(zcu).signedness == .signed) break :jmp_table null; | | |
| 6303 | | | |
| 6304 | // Don't worry about the size of the type -- it's irrelevant, because the prong values could be fairly dense. | | |
| 6305 | // If they are, then we will construct a jump table. | | |
| 6306 | const min, const max = self.switchCaseItemRange(switch_br) orelse break :jmp_table null; | | |
| 6307 | const min_int = min.getUnsignedInt(zcu) orelse break :jmp_table null; | | |
| 6308 | const max_int = max.getUnsignedInt(zcu) orelse break :jmp_table null; | | |
| 6309 | const table_len = max_int - min_int + 1; | | |
| 6310 | if (table_len > max_table_len) break :jmp_table null; | | |
| 6311 | | | |
| 6312 | const table_elems = try self.gpa.alloc(Builder.Constant, @intCast(table_len)); | | |
| 6313 | defer self.gpa.free(table_elems); | | |
| 6314 | | | |
| 6315 | // Set them all to the `else` branch, then iterate over the AIR switch | | |
| 6316 | // and replace all values which correspond to other prongs. | | |
| 6317 | @memset(table_elems, try o.builder.blockAddrConst( | | |
| 6318 | self.wip.function, | | |
| 6319 | case_blocks[case_blocks.len - 1], | | |
| 6320 | )); | | |
| 6321 | var item_count: u32 = 0; | | |
| 6322 | var it = switch_br.iterateCases(); | | |
| 6323 | while (it.next()) |case| { | | |
| 6324 | const case_block = case_blocks[case.idx]; | | |
| 6325 | const case_block_addr = try o.builder.blockAddrConst( | | |
| 6326 | self.wip.function, | | |
| 6327 | case_block, | | |
| 6328 | ); | | |
| 6329 | for (case.items) |item| { | | |
| 6330 | const val = Value.fromInterned(item.toInterned().?); | | |
| 6331 | const table_idx = val.toUnsignedInt(zcu) - min_int; | | |
| 6332 | table_elems[@intCast(table_idx)] = case_block_addr; | | |
| 6333 | item_count += 1; | | |
| 6334 | } | | |
| 6335 | for (case.ranges) |range| { | | |
| 6336 | const low = Value.fromInterned(range[0].toInterned().?); | | |
| 6337 | const high = Value.fromInterned(range[1].toInterned().?); | | |
| 6338 | const low_idx = low.toUnsignedInt(zcu) - min_int; | | |
| 6339 | const high_idx = high.toUnsignedInt(zcu) - min_int; | | |
| 6340 | @memset(table_elems[@intCast(low_idx)..@intCast(high_idx + 1)], case_block_addr); | | |
| 6341 | item_count += @intCast(high_idx + 1 - low_idx); | | |
| 6342 | } | | |
| 6343 | } | | |
| 6344 | | | |
| 6345 | const table_llvm_ty = try o.builder.arrayType(table_elems.len, .ptr); | | |
| 6346 | const table_val = try o.builder.arrayConst(table_llvm_ty, table_elems); | | |
| 6347 | | | |
| 6348 | const table_variable = try o.builder.addVariable( | | |
| 6349 | try o.builder.strtabStringFmt("__jmptab_{d}", .{@intFromEnum(inst)}), | | |
| 6350 | table_llvm_ty, | | |
| 6351 | .default, | | |
| 6352 | ); | | |
| 6353 | try table_variable.setInitializer(table_val, &o.builder); | | |
| 6354 | table_variable.setLinkage(if (o.builder.strip) .private else .internal, &o.builder); | | |
| 6355 | table_variable.setUnnamedAddr(.unnamed_addr, &o.builder); | | |
| 6356 | | | |
| 6357 | const table_includes_else = item_count != table_len; | | |
| 6358 | | | |
| 6359 | break :jmp_table .{ | | |
| 6360 | .min = try o.lowerValue(pt, min.toIntern()), | | |
| 6361 | .max = try o.lowerValue(pt, max.toIntern()), | | |
| 6362 | .in_bounds_hint = if (table_includes_else) .none else switch (switch_br.getElseHint()) { | | |
| 6363 | .none, .cold => .none, | | |
| 6364 | .unpredictable => .unpredictable, | | |
| 6365 | .likely => .likely, | | |
| 6366 | .unlikely => .unlikely, | | |
| 6367 | }, | | |
| 6368 | .table = table_variable.toConst(&o.builder), | | |
| 6369 | .table_includes_else = table_includes_else, | | |
| 6370 | }; | | |
| 6371 | }; | | |
| 6372 | | | |
| 6373 | const weights: Builder.Function.Instruction.BrCond.Weights = weights: { | | |
| 6374 | if (jmp_table != null) break :weights .none; // not used | | |
| 6375 | | | |
| 6376 | // First pass. If any weights are `.unpredictable`, unpredictable. | | |
| 6377 | // If all are `.none` or `.cold`, none. | | |
| 6378 | var any_likely = false; | | |
| 6379 | for (0..switch_br.cases_len) |case_idx| { | | |
| 6380 | switch (switch_br.getHint(@intCast(case_idx))) { | | |
| 6381 | .none, .cold => {}, | | |
| 6382 | .likely, .unlikely => any_likely = true, | | |
| 6383 | .unpredictable => break :weights .unpredictable, | | |
| 6384 | } | | |
| 6385 | } | | |
| 6386 | switch (switch_br.getElseHint()) { | | |
| 6387 | .none, .cold => {}, | | |
| 6388 | .likely, .unlikely => any_likely = true, | | |
| 6389 | .unpredictable => break :weights .unpredictable, | | |
| 6390 | } | | |
| 6391 | if (!any_likely) break :weights .none; | | |
| 6392 | | | |
| 6393 | const llvm_cases_len = llvm_cases_len: { | | |
| 6394 | var len: u32 = 0; | | |
| 6395 | var it = switch_br.iterateCases(); | | |
| 6396 | while (it.next()) |case| len += @intCast(case.items.len); | | |
| 6397 | break :llvm_cases_len len; | | |
| 6398 | }; | | |
| 6399 | | | |
| 6400 | var weights = try self.gpa.alloc(Builder.Metadata, 1 + llvm_cases_len + 1); | | |
| 6401 | defer self.gpa.free(weights); | | |
| 6402 | var weight_idx: usize = 0; | | |
| 6403 | | | |
| 6404 | const branch_weights_str = try o.builder.metadataString("branch_weights"); | | |
| 6405 | weights[weight_idx] = branch_weights_str.toMetadata(); | | |
| 6406 | weight_idx += 1; | | |
| 6407 | | | |
| 6408 | const else_weight: u32 = switch (switch_br.getElseHint()) { | | |
| 6409 | .unpredictable => unreachable, | | |
| 6410 | .none, .cold => 1000, | | |
| 6411 | .likely => 2000, | | |
| 6412 | .unlikely => 1, | | |
| 6413 | }; | | |
| 6414 | weights[weight_idx] = try o.builder.metadataConstant(try o.builder.intConst(.i32, else_weight)); | | |
| 6415 | weight_idx += 1; | | |
| 6416 | | | |
| 6417 | var it = switch_br.iterateCases(); | | |
| 6418 | while (it.next()) |case| { | | |
| 6419 | const weight_val: u32 = switch (switch_br.getHint(case.idx)) { | | |
| 6420 | .unpredictable => unreachable, | | |
| 6421 | .none, .cold => 1000, | | |
| 6422 | .likely => 2000, | | |
| 6423 | .unlikely => 1, | | |
| 6424 | }; | | |
| 6425 | const weight_meta = try o.builder.metadataConstant(try o.builder.intConst(.i32, weight_val)); | | |
| 6426 | @memset(weights[weight_idx..][0..case.items.len], weight_meta); | | |
| 6427 | weight_idx += case.items.len; | | |
| 6428 | } | | |
| 6429 | | | |
| 6430 | assert(weight_idx == weights.len); | | |
| 6431 | break :weights .fromMetadata(try o.builder.metadataTuple(weights)); | | |
| 6432 | }; | | |
| 6433 | | | |
| 6434 | const dispatch_info: SwitchDispatchInfo = .{ | | |
| 6435 | .case_blocks = case_blocks, | | |
| 6436 | .switch_weights = weights, | | |
| 6437 | .jmp_table = jmp_table, | | |
| 6438 | }; | | |
| 6439 | | | |
| 6440 | if (is_dispatch_loop) { | | |
| 6441 | try self.switch_dispatch_info.putNoClobber(self.gpa, inst, dispatch_info); | | |
| 6442 | } | | |
| 6443 | defer if (is_dispatch_loop) { | | |
| 6444 | assert(self.switch_dispatch_info.remove(inst)); | | |
| 6445 | }; | | |
| 6446 | | | |
| 6447 | // Generate the initial dispatch. | | |
| 6448 | // If this is a simple `switch_br`, this is the only dispatch. | | |
| 6449 | try self.lowerSwitchDispatch(inst, switch_br.operand, dispatch_info); | | |
| 6450 | | | |
| 6451 | // Iterate the cases and generate their bodies. | | |
| 6452 | var it = switch_br.iterateCases(); | | |
| 6453 | while (it.next()) |case| { | | |
| 6454 | const case_block = case_blocks[case.idx]; | | |
| 6455 | self.wip.cursor = .{ .block = case_block }; | | |
| 6456 | if (switch_br.getHint(case.idx) == .cold) _ = try self.wip.callIntrinsicAssumeCold(); | | |
| 6457 | try self.genBodyDebugScope(null, case.body, .none); | | |
| 6458 | } | | |
| 6459 | self.wip.cursor = .{ .block = case_blocks[case_blocks.len - 1] }; | | |
| 6460 | const else_body = it.elseBody(); | | |
| 6461 | if (switch_br.getElseHint() == .cold) _ = try self.wip.callIntrinsicAssumeCold(); | | |
| 6462 | if (else_body.len > 0) { | | |
| 6463 | try self.genBodyDebugScope(null, it.elseBody(), .none); | | |
| 6464 | } else { | | |
| 6465 | _ = try self.wip.@"unreachable"(); | | |
| 6466 | } | | |
| 6467 | } | | |
| 6468 | | | |
| 6469 | fn switchCaseItemRange(self: *FuncGen, switch_br: Air.UnwrappedSwitch) ?[2]Value { | | |
| 6470 | const zcu = self.ng.pt.zcu; | | |
| 6471 | var it = switch_br.iterateCases(); | | |
| 6472 | var min: ?Value = null; | | |
| 6473 | var max: ?Value = null; | | |
| 6474 | while (it.next()) |case| { | | |
| 6475 | for (case.items) |item| { | | |
| 6476 | const val = Value.fromInterned(item.toInterned().?); | | |
| 6477 | const low = if (min) |m| val.compareHetero(.lt, m, zcu) else true; | | |
| 6478 | const high = if (max) |m| val.compareHetero(.gt, m, zcu) else true; | | |
| 6479 | if (low) min = val; | | |
| 6480 | if (high) max = val; | | |
| 6481 | } | | |
| 6482 | for (case.ranges) |range| { | | |
| 6483 | const vals: [2]Value = .{ | | |
| 6484 | Value.fromInterned(range[0].toInterned().?), | | |
| 6485 | Value.fromInterned(range[1].toInterned().?), | | |
| 6486 | }; | | |
| 6487 | const low = if (min) |m| vals[0].compareHetero(.lt, m, zcu) else true; | | |
| 6488 | const high = if (max) |m| vals[1].compareHetero(.gt, m, zcu) else true; | | |
| 6489 | if (low) min = vals[0]; | | |
| 6490 | if (high) max = vals[1]; | | |
| 6491 | } | | |
| 6492 | } | | |
| 6493 | if (min == null) { | | |
| 6494 | assert(max == null); | | |
| 6495 | return null; | | |
| 6496 | } | | |
| 6497 | return .{ min.?, max.? }; | | |
| 6498 | } | | |
| 6499 | | | |
| 6500 | fn airLoop(self: *FuncGen, inst: Air.Inst.Index) !void { | | |
| 6501 | const block = self.air.unwrapBlock(inst); | | |
| 6502 | const body = block.body; | | |
| 6503 | const loop_block = try self.wip.block(1, "Loop"); // `airRepeat` will increment incoming each time | | |
| 6504 | _ = try self.wip.br(loop_block); | | |
| 6505 | | | |
| 6506 | try self.loops.putNoClobber(self.gpa, inst, loop_block); | | |
| 6507 | defer assert(self.loops.remove(inst)); | | |
| 6508 | | | |
| 6509 | self.wip.cursor = .{ .block = loop_block }; | | |
| 6510 | try self.genBodyDebugScope(null, body, .none); | | |
| 6511 | } | | |
| 6512 | | | |
| 6513 | fn airArrayToSlice(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 6514 | const o = self.ng.object; | | |
| 6515 | const pt = self.ng.pt; | | |
| 6516 | const zcu = pt.zcu; | | |
| 6517 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 6518 | const operand_ty = self.typeOf(ty_op.operand); | | |
| 6519 | const array_ty = operand_ty.childType(zcu); | | |
| 6520 | const llvm_usize = try o.lowerType(pt, Type.usize); | | |
| 6521 | const len = try o.builder.intValue(llvm_usize, array_ty.arrayLen(zcu)); | | |
| 6522 | const slice_llvm_ty = try o.lowerType(pt, self.typeOfIndex(inst)); | | |
| 6523 | const operand = try self.resolveInst(ty_op.operand); | | |
| 6524 | if (!array_ty.hasRuntimeBits(zcu)) | | |
| 6525 | return self.wip.buildAggregate(slice_llvm_ty, &.{ operand, len }, ""); | | |
| 6526 | const ptr = try self.wip.gep(.inbounds, try o.lowerType(pt, array_ty), operand, &.{ | | |
| 6527 | try o.builder.intValue(llvm_usize, 0), try o.builder.intValue(llvm_usize, 0), | | |
| 6528 | }, ""); | | |
| 6529 | return self.wip.buildAggregate(slice_llvm_ty, &.{ ptr, len }, ""); | | |
| 6530 | } | | |
| 6531 | | | |
| 6532 | fn airFloatFromInt(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 6533 | const o = self.ng.object; | | |
| 6534 | const pt = self.ng.pt; | | |
| 6535 | const zcu = pt.zcu; | | |
| 6536 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 6537 | | | |
| 6538 | const operand = try self.resolveInst(ty_op.operand); | | |
| 6539 | const operand_ty = self.typeOf(ty_op.operand); | | |
| 6540 | const operand_scalar_ty = operand_ty.scalarType(zcu); | | |
| 6541 | const is_signed_int = operand_scalar_ty.isSignedInt(zcu); | | |
| 6542 | | | |
| 6543 | const dest_ty = self.typeOfIndex(inst); | | |
| 6544 | const dest_scalar_ty = dest_ty.scalarType(zcu); | | |
| 6545 | const dest_llvm_ty = try o.lowerType(pt, dest_ty); | | |
| 6546 | const target = zcu.getTarget(); | | |
| 6547 | | | |
| 6548 | if (intrinsicsAllowed(dest_scalar_ty, target)) return self.wip.conv( | | |
| 6549 | if (is_signed_int) .signed else .unsigned, | | |
| 6550 | operand, | | |
| 6551 | dest_llvm_ty, | | |
| 6552 | "", | | |
| 6553 | ); | | |
| 6554 | | | |
| 6555 | const rt_int_bits = compilerRtIntBits(@intCast(operand_scalar_ty.bitSize(zcu))) orelse { | | |
| 6556 | return self.todo("float_from_int from '{f}' without intrinsics", .{operand_scalar_ty.fmt(pt)}); | | |
| 6557 | }; | | |
| 6558 | const rt_int_ty = try o.builder.intType(rt_int_bits); | | |
| 6559 | var extended = try self.wip.conv( | | |
| 6560 | if (is_signed_int) .signed else .unsigned, | | |
| 6561 | operand, | | |
| 6562 | rt_int_ty, | | |
| 6563 | "", | | |
| 6564 | ); | | |
| 6565 | const dest_bits = dest_scalar_ty.floatBits(target); | | |
| 6566 | const compiler_rt_operand_abbrev = compilerRtIntAbbrev(rt_int_bits); | | |
| 6567 | const compiler_rt_dest_abbrev = compilerRtFloatAbbrev(dest_bits); | | |
| 6568 | const sign_prefix = if (is_signed_int) "" else "un"; | | |
| 6569 | const fn_name = try o.builder.strtabStringFmt("__float{s}{s}i{s}f", .{ | | |
| 6570 | sign_prefix, | | |
| 6571 | compiler_rt_operand_abbrev, | | |
| 6572 | compiler_rt_dest_abbrev, | | |
| 6573 | }); | | |
| 6574 | | | |
| 6575 | var param_type = rt_int_ty; | | |
| 6576 | if (rt_int_bits == 128 and (target.os.tag == .windows and target.cpu.arch == .x86_64)) { | | |
| 6577 | // On Windows x86-64, "ti" functions must use Vector(2, u64) instead of the standard | | |
| 6578 | // i128 calling convention to adhere to the ABI that LLVM expects compiler-rt to have. | | |
| 6579 | param_type = try o.builder.vectorType(.normal, 2, .i64); | | |
| 6580 | extended = try self.wip.cast(.bitcast, extended, param_type, ""); | | |
| 6581 | } | | |
| 6582 | | | |
| 6583 | const libc_fn = try self.getLibcFunction(fn_name, &.{param_type}, dest_llvm_ty); | | |
| 6584 | return self.wip.call( | | |
| 6585 | .normal, | | |
| 6586 | .ccc, | | |
| 6587 | .none, | | |
| 6588 | libc_fn.typeOf(&o.builder), | | |
| 6589 | libc_fn.toValue(&o.builder), | | |
| 6590 | &.{extended}, | | |
| 6591 | "", | | |
| 6592 | ); | | |
| 6593 | } | | |
| 6594 | | | |
| 6595 | fn airIntFromFloat( | | |
| 6596 | self: *FuncGen, | | |
| 6597 | inst: Air.Inst.Index, | | |
| 6598 | fast: Builder.FastMathKind, | | |
| 6599 | ) !Builder.Value { | | |
| 6600 | _ = fast; | | |
| 6601 | | | |
| 6602 | const o = self.ng.object; | | |
| 6603 | const pt = self.ng.pt; | | |
| 6604 | const zcu = pt.zcu; | | |
| 6605 | const target = zcu.getTarget(); | | |
| 6606 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 6607 | | | |
| 6608 | const operand = try self.resolveInst(ty_op.operand); | | |
| 6609 | const operand_ty = self.typeOf(ty_op.operand); | | |
| 6610 | const operand_scalar_ty = operand_ty.scalarType(zcu); | | |
| 6611 | | | |
| 6612 | const dest_ty = self.typeOfIndex(inst); | | |
| 6613 | const dest_scalar_ty = dest_ty.scalarType(zcu); | | |
| 6614 | const dest_llvm_ty = try o.lowerType(pt, dest_ty); | | |
| 6615 | | | |
| 6616 | if (intrinsicsAllowed(operand_scalar_ty, target)) { | | |
| 6617 | // TODO set fast math flag | | |
| 6618 | return self.wip.conv( | | |
| 6619 | if (dest_scalar_ty.isSignedInt(zcu)) .signed else .unsigned, | | |
| 6620 | operand, | | |
| 6621 | dest_llvm_ty, | | |
| 6622 | "", | | |
| 6623 | ); | | |
| 6624 | } | | |
| 6625 | | | |
| 6626 | const rt_int_bits = compilerRtIntBits(@intCast(dest_scalar_ty.bitSize(zcu))) orelse { | | |
| 6627 | return self.todo("int_from_float to '{f}' without intrinsics", .{dest_scalar_ty.fmt(pt)}); | | |
| 6628 | }; | | |
| 6629 | const ret_ty = try o.builder.intType(rt_int_bits); | | |
| 6630 | const libc_ret_ty = if (rt_int_bits == 128 and (target.os.tag == .windows and target.cpu.arch == .x86_64)) b: { | | |
| 6631 | // On Windows x86-64, "ti" functions must use Vector(2, u64) instead of the standard | | |
| 6632 | // i128 calling convention to adhere to the ABI that LLVM expects compiler-rt to have. | | |
| 6633 | break :b try o.builder.vectorType(.normal, 2, .i64); | | |
| 6634 | } else ret_ty; | | |
| 6635 | | | |
| 6636 | const operand_bits = operand_scalar_ty.floatBits(target); | | |
| 6637 | const compiler_rt_operand_abbrev = compilerRtFloatAbbrev(operand_bits); | | |
| 6638 | | | |
| 6639 | const compiler_rt_dest_abbrev = compilerRtIntAbbrev(rt_int_bits); | | |
| 6640 | const sign_prefix = if (dest_scalar_ty.isSignedInt(zcu)) "" else "uns"; | | |
| 6641 | | | |
| 6642 | const fn_name = try o.builder.strtabStringFmt("__fix{s}{s}f{s}i", .{ | | |
| 6643 | sign_prefix, | | |
| 6644 | compiler_rt_operand_abbrev, | | |
| 6645 | compiler_rt_dest_abbrev, | | |
| 6646 | }); | | |
| 6647 | | | |
| 6648 | const operand_llvm_ty = try o.lowerType(pt, operand_ty); | | |
| 6649 | const libc_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, libc_ret_ty); | | |
| 6650 | var result = try self.wip.call( | | |
| 6651 | .normal, | | |
| 6652 | .ccc, | | |
| 6653 | .none, | | |
| 6654 | libc_fn.typeOf(&o.builder), | | |
| 6655 | libc_fn.toValue(&o.builder), | | |
| 6656 | &.{operand}, | | |
| 6657 | "", | | |
| 6658 | ); | | |
| 6659 | | | |
| 6660 | if (libc_ret_ty != ret_ty) result = try self.wip.cast(.bitcast, result, ret_ty, ""); | | |
| 6661 | if (ret_ty != dest_llvm_ty) result = try self.wip.cast(.trunc, result, dest_llvm_ty, ""); | | |
| 6662 | return result; | | |
| 6663 | } | | |
| 6664 | | | |
| 6665 | fn sliceOrArrayPtr(fg: *FuncGen, ptr: Builder.Value, ty: Type) Allocator.Error!Builder.Value { | | |
| 6666 | const zcu = fg.ng.pt.zcu; | | |
| 6667 | return if (ty.isSlice(zcu)) fg.wip.extractValue(ptr, &.{0}, "") else ptr; | | |
| 6668 | } | | |
| 6669 | | | |
| 6670 | fn sliceOrArrayLenInBytes(fg: *FuncGen, ptr: Builder.Value, ty: Type) Allocator.Error!Builder.Value { | | |
| 6671 | const o = fg.ng.object; | | |
| 6672 | const pt = fg.ng.pt; | | |
| 6673 | const zcu = pt.zcu; | | |
| 6674 | const llvm_usize = try o.lowerType(pt, Type.usize); | | |
| 6675 | switch (ty.ptrSize(zcu)) { | | |
| 6676 | .slice => { | | |
| 6677 | const len = try fg.wip.extractValue(ptr, &.{1}, ""); | | |
| 6678 | const elem_ty = ty.childType(zcu); | | |
| 6679 | const abi_size = elem_ty.abiSize(zcu); | | |
| 6680 | if (abi_size == 1) return len; | | |
| 6681 | const abi_size_llvm_val = try o.builder.intValue(llvm_usize, abi_size); | | |
| 6682 | return fg.wip.bin(.@"mul nuw", len, abi_size_llvm_val, ""); | | |
| 6683 | }, | | |
| 6684 | .one => { | | |
| 6685 | const array_ty = ty.childType(zcu); | | |
| 6686 | const elem_ty = array_ty.childType(zcu); | | |
| 6687 | const abi_size = elem_ty.abiSize(zcu); | | |
| 6688 | return o.builder.intValue(llvm_usize, array_ty.arrayLen(zcu) * abi_size); | | |
| 6689 | }, | | |
| 6690 | .many, .c => unreachable, | | |
| 6691 | } | | |
| 6692 | } | | |
| 6693 | | | |
| 6694 | fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: u32) !Builder.Value { | | |
| 6695 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 6696 | const operand = try self.resolveInst(ty_op.operand); | | |
| 6697 | return self.wip.extractValue(operand, &.{index}, ""); | | |
| 6698 | } | | |
| 6699 | | | |
| 6700 | fn airPtrSliceFieldPtr(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !Builder.Value { | | |
| 6701 | const o = self.ng.object; | | |
| 6702 | const pt = self.ng.pt; | | |
| 6703 | const zcu = pt.zcu; | | |
| 6704 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 6705 | const slice_ptr = try self.resolveInst(ty_op.operand); | | |
| 6706 | const slice_ptr_ty = self.typeOf(ty_op.operand); | | |
| 6707 | const slice_llvm_ty = try o.lowerType(pt, slice_ptr_ty.childType(zcu)); | | |
| 6708 | | | |
| 6709 | return self.wip.gepStruct(slice_llvm_ty, slice_ptr, index, ""); | | |
| 6710 | } | | |
| 6711 | | | |
| 6712 | fn airSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 6713 | const o = self.ng.object; | | |
| 6714 | const pt = self.ng.pt; | | |
| 6715 | const zcu = pt.zcu; | | |
| 6716 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 6717 | const slice_ty = self.typeOf(bin_op.lhs); | | |
| 6718 | const slice = try self.resolveInst(bin_op.lhs); | | |
| 6719 | const index = try self.resolveInst(bin_op.rhs); | | |
| 6720 | const slice_info = slice_ty.ptrInfo(zcu); | | |
| 6721 | assert(slice_info.flags.size == .slice); | | |
| 6722 | const elem_ty: Type = .fromInterned(slice_info.child); | | |
| 6723 | const llvm_elem_ty = try o.lowerType(pt, elem_ty); | | |
| 6724 | const base_ptr = try self.wip.extractValue(slice, &.{0}, ""); | | |
| 6725 | const ptr = try self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, &.{index}, ""); | | |
| 6726 | const elem_align = slice_ty.ptrAlignment(zcu).min(elem_ty.abiAlignment(zcu)); | | |
| 6727 | const access_kind: Builder.MemoryAccessKind = if (slice_info.flags.is_volatile) .@"volatile" else .normal; | | |
| 6728 | self.maybeMarkAllowZeroAccess(slice_info); | | |
| 6729 | if (isByRef(elem_ty, zcu)) { | | |
| 6730 | return self.loadByRef(ptr, elem_ty, elem_align.toLlvm(), access_kind); | | |
| 6731 | } else { | | |
| 6732 | return self.loadTruncate(access_kind, elem_ty, ptr, elem_align.toLlvm()); | | |
| 6733 | } | | |
| 6734 | } | | |
| 6735 | | | |
| 6736 | fn airSliceElemPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 6737 | const o = self.ng.object; | | |
| 6738 | const pt = self.ng.pt; | | |
| 6739 | const zcu = pt.zcu; | | |
| 6740 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | | |
| 6741 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; | | |
| 6742 | const slice_ty = self.typeOf(bin_op.lhs); | | |
| 6743 | | | |
| 6744 | const slice = try self.resolveInst(bin_op.lhs); | | |
| 6745 | const index = try self.resolveInst(bin_op.rhs); | | |
| 6746 | const llvm_elem_ty = try o.lowerType(pt, slice_ty.childType(zcu)); | | |
| 6747 | const base_ptr = try self.wip.extractValue(slice, &.{0}, ""); | | |
| 6748 | return self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, &.{index}, ""); | | |
| 6749 | } | | |
| 6750 | | | |
| 6751 | fn airArrayElemVal(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 6752 | const o = self.ng.object; | | |
| 6753 | const pt = self.ng.pt; | | |
| 6754 | const zcu = pt.zcu; | | |
| 6755 | | | |
| 6756 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 6757 | const array_ty = self.typeOf(bin_op.lhs); | | |
| 6758 | const array_llvm_val = try self.resolveInst(bin_op.lhs); | | |
| 6759 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 6760 | const array_llvm_ty = try o.lowerType(pt, array_ty); | | |
| 6761 | const elem_ty = array_ty.childType(zcu); | | |
| 6762 | if (isByRef(array_ty, zcu)) { | | |
| 6763 | const elem_ptr = try self.wip.gep(.inbounds, array_llvm_ty, array_llvm_val, &.{ | | |
| 6764 | try o.builder.intValue(try o.lowerType(pt, Type.usize), 0), | | |
| 6765 | rhs, | | |
| 6766 | }, ""); | | |
| 6767 | if (isByRef(elem_ty, zcu)) { | | |
| 6768 | const elem_alignment = elem_ty.abiAlignment(zcu).toLlvm(); | | |
| 6769 | return self.loadByRef(elem_ptr, elem_ty, elem_alignment, .normal); | | |
| 6770 | } else { | | |
| 6771 | return self.loadTruncate(.normal, elem_ty, elem_ptr, .default); | | |
| 6772 | } | | |
| 6773 | } | | |
| 6774 | | | |
| 6775 | // This branch can be reached for vectors, which are always by-value. | | |
| 6776 | return self.wip.extractElement(array_llvm_val, rhs, ""); | | |
| 6777 | } | | |
| 6778 | | | |
| 6779 | fn airPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 6780 | const o = self.ng.object; | | |
| 6781 | const pt = self.ng.pt; | | |
| 6782 | const zcu = pt.zcu; | | |
| 6783 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 6784 | const ptr_ty = self.typeOf(bin_op.lhs); | | |
| 6785 | const elem_ty = ptr_ty.indexableElem(zcu); | | |
| 6786 | const llvm_elem_ty = try o.lowerType(pt, elem_ty); | | |
| 6787 | const base_ptr = try self.resolveInst(bin_op.lhs); | | |
| 6788 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 6789 | const ptr = try self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, &.{rhs}, ""); | | |
| 6790 | if (isByRef(elem_ty, zcu)) { | | |
| 6791 | self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu)); | | |
| 6792 | const ptr_align = (ptr_ty.ptrAlignment(zcu).min(elem_ty.abiAlignment(zcu))).toLlvm(); | | |
| 6793 | return self.loadByRef(ptr, elem_ty, ptr_align, if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal); | | |
| 6794 | } | | |
| 6795 | | | |
| 6796 | self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu)); | | |
| 6797 | | | |
| 6798 | return self.load(ptr, ptr_ty); | | |
| 6799 | } | | |
| 6800 | | | |
| 6801 | fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 6802 | const o = self.ng.object; | | |
| 6803 | const pt = self.ng.pt; | | |
| 6804 | const zcu = pt.zcu; | | |
| 6805 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | | |
| 6806 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; | | |
| 6807 | const ptr_ty = self.typeOf(bin_op.lhs); | | |
| 6808 | const elem_ty = ptr_ty.indexableElem(zcu); | | |
| 6809 | assert(elem_ty.hasRuntimeBits(zcu)); | | |
| 6810 | | | |
| 6811 | const base_ptr = try self.resolveInst(bin_op.lhs); | | |
| 6812 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 6813 | | | |
| 6814 | const elem_ptr = ty_pl.ty.toType(); | | |
| 6815 | if (elem_ptr.ptrInfo(zcu).flags.vector_index != .none) return base_ptr; | | |
| 6816 | | | |
| 6817 | const llvm_elem_ty = try o.lowerType(pt, elem_ty); | | |
| 6818 | return self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, &.{rhs}, ""); | | |
| 6819 | } | | |
| 6820 | | | |
| 6821 | fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 6822 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | | |
| 6823 | const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; | | |
| 6824 | const struct_ptr = try self.resolveInst(struct_field.struct_operand); | | |
| 6825 | const struct_ptr_ty = self.typeOf(struct_field.struct_operand); | | |
| 6826 | return self.fieldPtr(struct_ptr, struct_ptr_ty, struct_field.field_index); | | |
| 6827 | } | | |
| 6828 | | | |
| 6829 | fn airStructFieldPtrIndex( | | |
| 6830 | self: *FuncGen, | | |
| 6831 | inst: Air.Inst.Index, | | |
| 6832 | field_index: u32, | | |
| 6833 | ) !Builder.Value { | | |
| 6834 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 6835 | const struct_ptr = try self.resolveInst(ty_op.operand); | | |
| 6836 | const struct_ptr_ty = self.typeOf(ty_op.operand); | | |
| 6837 | return self.fieldPtr(struct_ptr, struct_ptr_ty, field_index); | | |
| 6838 | } | | |
| 6839 | | | |
| 6840 | fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 6841 | const o = self.ng.object; | | |
| 6842 | const pt = self.ng.pt; | | |
| 6843 | const zcu = pt.zcu; | | |
| 6844 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | | |
| 6845 | const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; | | |
| 6846 | const struct_ty = self.typeOf(struct_field.struct_operand); | | |
| 6847 | const struct_llvm_val = try self.resolveInst(struct_field.struct_operand); | | |
| 6848 | const field_index = struct_field.field_index; | | |
| 6849 | const field_ty = struct_ty.fieldType(field_index, zcu); | | |
| 6850 | if (!field_ty.hasRuntimeBits(zcu)) return .none; | | |
| 6851 | | | |
| 6852 | if (!isByRef(struct_ty, zcu)) { | | |
| 6853 | assert(!isByRef(field_ty, zcu)); | | |
| 6854 | switch (struct_ty.zigTypeTag(zcu)) { | | |
| 6855 | .@"struct" => switch (struct_ty.containerLayout(zcu)) { | | |
| 6856 | .@"packed" => { | | |
| 6857 | const struct_type = zcu.typeToStruct(struct_ty).?; | | |
| 6858 | const bit_offset = zcu.structPackedFieldBitOffset(struct_type, field_index); | | |
| 6859 | const containing_int = struct_llvm_val; | | |
| 6860 | const shift_amt = | | |
| 6861 | try o.builder.intValue(containing_int.typeOfWip(&self.wip), bit_offset); | | |
| 6862 | const shifted_value = try self.wip.bin(.lshr, containing_int, shift_amt, ""); | | |
| 6863 | const elem_llvm_ty = try o.lowerType(pt, field_ty); | | |
| 6864 | if (field_ty.zigTypeTag(zcu) == .float or field_ty.zigTypeTag(zcu) == .vector) { | | |
| 6865 | const same_size_int = try o.builder.intType(@intCast(field_ty.bitSize(zcu))); | | |
| 6866 | const truncated_int = | | |
| 6867 | try self.wip.cast(.trunc, shifted_value, same_size_int, ""); | | |
| 6868 | return self.wip.cast(.bitcast, truncated_int, elem_llvm_ty, ""); | | |
| 6869 | } | | |
| 6870 | return self.wip.cast(.trunc, shifted_value, elem_llvm_ty, ""); | | |
| 6871 | }, | | |
| 6872 | else => { | | |
| 6873 | const llvm_field_index = o.llvmFieldIndex(struct_ty, field_index).?; | | |
| 6874 | return self.wip.extractValue(struct_llvm_val, &.{llvm_field_index}, ""); | | |
| 6875 | }, | | |
| 6876 | }, | | |
| 6877 | .@"union" => { | | |
| 6878 | assert(struct_ty.containerLayout(zcu) == .@"packed"); | | |
| 6879 | const containing_int = struct_llvm_val; | | |
| 6880 | const elem_llvm_ty = try o.lowerType(pt, field_ty); | | |
| 6881 | if (field_ty.zigTypeTag(zcu) == .float or field_ty.zigTypeTag(zcu) == .vector) { | | |
| 6882 | const same_size_int = try o.builder.intType(@intCast(field_ty.bitSize(zcu))); | | |
| 6883 | const truncated_int = | | |
| 6884 | try self.wip.cast(.trunc, containing_int, same_size_int, ""); | | |
| 6885 | return self.wip.cast(.bitcast, truncated_int, elem_llvm_ty, ""); | | |
| 6886 | } | | |
| 6887 | return self.wip.cast(.trunc, containing_int, elem_llvm_ty, ""); | | |
| 6888 | }, | | |
| 6889 | else => unreachable, | | |
| 6890 | } | | |
| 6891 | } | | |
| 6892 | | | |
| 6893 | switch (struct_ty.zigTypeTag(zcu)) { | | |
| 6894 | .@"struct" => { | | |
| 6895 | const layout = struct_ty.containerLayout(zcu); | | |
| 6896 | assert(layout != .@"packed"); | | |
| 6897 | const struct_llvm_ty = try o.lowerType(pt, struct_ty); | | |
| 6898 | const llvm_field_index = o.llvmFieldIndex(struct_ty, field_index).?; | | |
| 6899 | const field_ptr = | | |
| 6900 | try self.wip.gepStruct(struct_llvm_ty, struct_llvm_val, llvm_field_index, ""); | | |
| 6901 | const explicit_alignment = struct_ty.explicitFieldAlignment(field_index, zcu); | | |
| 6902 | const field_ptr_ty = try pt.ptrType(.{ | | |
| 6903 | .child = field_ty.toIntern(), | | |
| 6904 | .flags = .{ .alignment = explicit_alignment }, | | |
| 6905 | }); | | |
| 6906 | if (isByRef(field_ty, zcu)) { | | |
| 6907 | const alignment = switch (explicit_alignment) { | | |
| 6908 | .none => field_ty.abiAlignment(zcu), | | |
| 6909 | else => |a| a, | | |
| 6910 | }; | | |
| 6911 | return self.loadByRef(field_ptr, field_ty, alignment.toLlvm(), .normal); | | |
| 6912 | } else { | | |
| 6913 | return self.load(field_ptr, field_ptr_ty); | | |
| 6914 | } | | |
| 6915 | }, | | |
| 6916 | .@"union" => { | | |
| 6917 | const union_llvm_ty = try o.lowerType(pt, struct_ty); | | |
| 6918 | const layout = struct_ty.unionGetLayout(zcu); | | |
| 6919 | const payload_index = @intFromBool(layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align)); | | |
| 6920 | const field_ptr = | | |
| 6921 | try self.wip.gepStruct(union_llvm_ty, struct_llvm_val, payload_index, ""); | | |
| 6922 | const payload_alignment = layout.payload_align.toLlvm(); | | |
| 6923 | if (isByRef(field_ty, zcu)) { | | |
| 6924 | return self.loadByRef(field_ptr, field_ty, payload_alignment, .normal); | | |
| 6925 | } else { | | |
| 6926 | return self.loadTruncate(.normal, field_ty, field_ptr, payload_alignment); | | |
| 6927 | } | | |
| 6928 | }, | | |
| 6929 | else => unreachable, | | |
| 6930 | } | | |
| 6931 | } | | |
| 6932 | | | |
| 6933 | fn airFieldParentPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 6934 | const o = self.ng.object; | | |
| 6935 | const pt = self.ng.pt; | | |
| 6936 | const zcu = pt.zcu; | | |
| 6937 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | | |
| 6938 | const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; | | |
| 6939 | | | |
| 6940 | const field_ptr = try self.resolveInst(extra.field_ptr); | | |
| 6941 | | | |
| 6942 | const parent_ty = ty_pl.ty.toType().childType(zcu); | | |
| 6943 | const field_offset = parent_ty.structFieldOffset(extra.field_index, zcu); | | |
| 6944 | if (field_offset == 0) return field_ptr; | | |
| 6945 | | | |
| 6946 | const res_ty = try o.lowerType(pt, ty_pl.ty.toType()); | | |
| 6947 | const llvm_usize = try o.lowerType(pt, Type.usize); | | |
| 6948 | | | |
| 6949 | const field_ptr_int = try self.wip.cast(.ptrtoint, field_ptr, llvm_usize, ""); | | |
| 6950 | const base_ptr_int = try self.wip.bin( | | |
| 6951 | .@"sub nuw", | | |
| 6952 | field_ptr_int, | | |
| 6953 | try o.builder.intValue(llvm_usize, field_offset), | | |
| 6954 | "", | | |
| 6955 | ); | | |
| 6956 | return self.wip.cast(.inttoptr, base_ptr_int, res_ty, ""); | | |
| 6957 | } | | |
| 6958 | | | |
| 6959 | fn airNot(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 6960 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 6961 | const operand = try self.resolveInst(ty_op.operand); | | |
| 6962 | | | |
| 6963 | return self.wip.not(operand, ""); | | |
| 6964 | } | | |
| 6965 | | | |
| 6966 | fn airUnreach(self: *FuncGen, inst: Air.Inst.Index) !void { | | |
| 6967 | _ = inst; | | |
| 6968 | _ = try self.wip.@"unreachable"(); | | |
| 6969 | } | | |
| 6970 | | | |
| 6971 | fn airDbgStmt(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 6972 | const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; | | |
| 6973 | self.prev_dbg_line = @intCast(self.base_line + dbg_stmt.line + 1); | | |
| 6974 | self.prev_dbg_column = @intCast(dbg_stmt.column + 1); | | |
| 6975 | | | |
| 6976 | self.wip.debug_location = .{ .location = .{ | | |
| 6977 | .line = self.prev_dbg_line, | | |
| 6978 | .column = self.prev_dbg_column, | | |
| 6979 | .scope = self.scope.toOptional(), | | |
| 6980 | .inlined_at = self.inlined_at, | | |
| 6981 | } }; | | |
| 6982 | | | |
| 6983 | return .none; | | |
| 6984 | } | | |
| 6985 | | | |
| 6986 | fn airDbgEmptyStmt(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 6987 | _ = self; | | |
| 6988 | _ = inst; | | |
| 6989 | return .none; | | |
| 6990 | } | | |
| 6991 | | | |
| 6992 | fn airDbgInlineBlock(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 6993 | const block = self.air.unwrapDbgBlock(inst); | | |
| 6994 | self.arg_inline_index = 0; | | |
| 6995 | return self.lowerBlock(inst, block.func, block.body); | | |
| 6996 | } | | |
| 6997 | | | |
| 6998 | fn airDbgVarPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 6999 | const o = self.ng.object; | | |
| 7000 | const pt = self.ng.pt; | | |
| 7001 | const zcu = pt.zcu; | | |
| 7002 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | | |
| 7003 | const operand = try self.resolveInst(pl_op.operand); | | |
| 7004 | const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); | | |
| 7005 | const ptr_ty = self.typeOf(pl_op.operand); | | |
| 7006 | | | |
| 7007 | const debug_local_var = try o.builder.debugLocalVar( | | |
| 7008 | try o.builder.metadataString(name.toSlice(self.air)), | | |
| 7009 | self.file, | | |
| 7010 | self.scope, | | |
| 7011 | self.prev_dbg_line, | | |
| 7012 | try o.getDebugType(pt, ptr_ty.childType(zcu)), | | |
| 7013 | ); | | |
| 7014 | | | |
| 7015 | _ = try self.wip.callIntrinsic( | | |
| 7016 | .normal, | | |
| 7017 | .none, | | |
| 7018 | .@"dbg.declare", | | |
| 7019 | &.{}, | | |
| 7020 | &.{ | | |
| 7021 | (try self.wip.debugValue(operand)).toValue(), | | |
| 7022 | debug_local_var.toValue(), | | |
| 7023 | (try o.builder.debugExpression(&.{})).toValue(), | | |
| 7024 | }, | | |
| 7025 | "", | | |
| 7026 | ); | | |
| 7027 | | | |
| 7028 | return .none; | | |
| 7029 | } | | |
| 7030 | | | |
| 7031 | fn airDbgVarVal(self: *FuncGen, inst: Air.Inst.Index, is_arg: bool) !Builder.Value { | | |
| 7032 | const o = self.ng.object; | | |
| 7033 | const pt = self.ng.pt; | | |
| 7034 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | | |
| 7035 | const operand = try self.resolveInst(pl_op.operand); | | |
| 7036 | const operand_ty = self.typeOf(pl_op.operand); | | |
| 7037 | const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); | | |
| 7038 | const name_slice = name.toSlice(self.air); | | |
| 7039 | const metadata_name = if (name_slice.len > 0) try o.builder.metadataString(name_slice) else null; | | |
| 7040 | const debug_local_var = if (is_arg) try o.builder.debugParameter( | | |
| 7041 | metadata_name, | | |
| 7042 | self.file, | | |
| 7043 | self.scope, | | |
| 7044 | self.prev_dbg_line, | | |
| 7045 | try o.getDebugType(pt, operand_ty), | | |
| 7046 | arg_no: { | | |
| 7047 | self.arg_inline_index += 1; | | |
| 7048 | break :arg_no self.arg_inline_index; | | |
| 7049 | }, | | |
| 7050 | ) else try o.builder.debugLocalVar( | | |
| 7051 | metadata_name, | | |
| 7052 | self.file, | | |
| 7053 | self.scope, | | |
| 7054 | self.prev_dbg_line, | | |
| 7055 | try o.getDebugType(pt, operand_ty), | | |
| 7056 | ); | | |
| 7057 | | | |
| 7058 | const zcu = pt.zcu; | | |
| 7059 | const owner_mod = self.ng.ownerModule(); | | |
| 7060 | if (isByRef(operand_ty, zcu)) { | | |
| 7061 | _ = try self.wip.callIntrinsic( | | |
| 7062 | .normal, | | |
| 7063 | .none, | | |
| 7064 | .@"dbg.declare", | | |
| 7065 | &.{}, | | |
| 7066 | &.{ | | |
| 7067 | (try self.wip.debugValue(operand)).toValue(), | | |
| 7068 | debug_local_var.toValue(), | | |
| 7069 | (try o.builder.debugExpression(&.{})).toValue(), | | |
| 7070 | }, | | |
| 7071 | "", | | |
| 7072 | ); | | |
| 7073 | } else if (owner_mod.optimize_mode == .Debug and !self.is_naked) { | | |
| 7074 | // We avoid taking this path for naked functions because there's no guarantee that such | | |
| 7075 | // functions even have a valid stack pointer, making the `alloca` + `store` unsafe. | | |
| 7076 | | | |
| 7077 | const alignment = operand_ty.abiAlignment(zcu).toLlvm(); | | |
| 7078 | const alloca = try self.buildAlloca(operand.typeOfWip(&self.wip), alignment); | | |
| 7079 | _ = try self.wip.store(.normal, operand, alloca, alignment); | | |
| 7080 | _ = try self.wip.callIntrinsic( | | |
| 7081 | .normal, | | |
| 7082 | .none, | | |
| 7083 | .@"dbg.declare", | | |
| 7084 | &.{}, | | |
| 7085 | &.{ | | |
| 7086 | (try self.wip.debugValue(alloca)).toValue(), | | |
| 7087 | debug_local_var.toValue(), | | |
| 7088 | (try o.builder.debugExpression(&.{})).toValue(), | | |
| 7089 | }, | | |
| 7090 | "", | | |
| 7091 | ); | | |
| 7092 | } else { | | |
| 7093 | _ = try self.wip.callIntrinsic( | | |
| 7094 | .normal, | | |
| 7095 | .none, | | |
| 7096 | .@"dbg.value", | | |
| 7097 | &.{}, | | |
| 7098 | &.{ | | |
| 7099 | (try self.wip.debugValue(operand)).toValue(), | | |
| 7100 | debug_local_var.toValue(), | | |
| 7101 | (try o.builder.debugExpression(&.{})).toValue(), | | |
| 7102 | }, | | |
| 7103 | "", | | |
| 7104 | ); | | |
| 7105 | } | | |
| 7106 | return .none; | | |
| 7107 | } | | |
| 7108 | | | |
| 7109 | fn airAssembly(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 7110 | // Eventually, the Zig compiler needs to be reworked to have inline | | |
| 7111 | // assembly go through the same parsing code regardless of backend, and | | |
| 7112 | // have LLVM-flavored inline assembly be *output* from that assembler. | | |
| 7113 | // We don't have such an assembler implemented yet though. For now, | | |
| 7114 | // this implementation feeds the inline assembly code directly to LLVM. | | |
| 7115 | | | |
| 7116 | const o = self.ng.object; | | |
| 7117 | const unwrapped_asm = self.air.unwrapAsm(inst); | | |
| 7118 | const is_volatile = unwrapped_asm.is_volatile; | | |
| 7119 | const gpa = self.gpa; | | |
| 7120 | | | |
| 7121 | const outputs = unwrapped_asm.outputs; | | |
| 7122 | const inputs = unwrapped_asm.inputs; | | |
| 7123 | | | |
| 7124 | var llvm_constraints: std.ArrayList(u8) = .empty; | | |
| 7125 | defer llvm_constraints.deinit(gpa); | | |
| 7126 | | | |
| 7127 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); | | |
| 7128 | defer arena_allocator.deinit(); | | |
| 7129 | const arena = arena_allocator.allocator(); | | |
| 7130 | | | |
| 7131 | // The exact number of return / parameter values depends on which output values | | |
| 7132 | // are passed by reference as indirect outputs (determined below). | | |
| 7133 | const max_return_count = outputs.len; | | |
| 7134 | const llvm_ret_types = try arena.alloc(Builder.Type, max_return_count); | | |
| 7135 | const llvm_ret_indirect = try arena.alloc(bool, max_return_count); | | |
| 7136 | const llvm_rw_vals = try arena.alloc(Builder.Value, max_return_count); | | |
| 7137 | | | |
| 7138 | const max_param_count = max_return_count + inputs.len + outputs.len; | | |
| 7139 | const llvm_param_types = try arena.alloc(Builder.Type, max_param_count); | | |
| 7140 | const llvm_param_values = try arena.alloc(Builder.Value, max_param_count); | | |
| 7141 | // This stores whether we need to add an elementtype attribute and | | |
| 7142 | // if so, the element type itself. | | |
| 7143 | const llvm_param_attrs = try arena.alloc(Builder.Type, max_param_count); | | |
| 7144 | const pt = self.ng.pt; | | |
| 7145 | const zcu = pt.zcu; | | |
| 7146 | const ip = &zcu.intern_pool; | | |
| 7147 | const target = zcu.getTarget(); | | |
| 7148 | | | |
| 7149 | var llvm_ret_i: usize = 0; | | |
| 7150 | var llvm_param_i: usize = 0; | | |
| 7151 | var total_i: usize = 0; | | |
| 7152 | | | |
| 7153 | var name_map: std.StringArrayHashMapUnmanaged(u16) = .empty; | | |
| 7154 | try name_map.ensureUnusedCapacity(arena, max_param_count); | | |
| 7155 | | | |
| 7156 | var it = unwrapped_asm.iterateOutputs(); | | |
| 7157 | while (it.next()) |output| { | | |
| 7158 | const constraint = output.constraint; | | |
| 7159 | const name = output.name; | | |
| 7160 | | | |
| 7161 | try llvm_constraints.ensureUnusedCapacity(gpa, constraint.len + 3); | | |
| 7162 | if (total_i != 0) { | | |
| 7163 | llvm_constraints.appendAssumeCapacity(','); | | |
| 7164 | } | | |
| 7165 | llvm_constraints.appendAssumeCapacity('='); | | |
| 7166 | | | |
| 7167 | if (output.operand != .none) { | | |
| 7168 | const output_inst = try self.resolveInst(output.operand); | | |
| 7169 | const output_ty = self.typeOf(output.operand); | | |
| 7170 | assert(output_ty.zigTypeTag(zcu) == .pointer); | | |
| 7171 | const elem_llvm_ty = try o.lowerType(pt, output_ty.childType(zcu)); | | |
| 7172 | | | |
| 7173 | switch (constraint[0]) { | | |
| 7174 | '=' => {}, | | |
| 7175 | '+' => llvm_rw_vals[output.index] = output_inst, | | |
| 7176 | else => return self.todo("unsupported output constraint on output type '{c}'", .{ | | |
| 7177 | constraint[0], | | |
| 7178 | }), | | |
| 7179 | } | | |
| 7180 | | | |
| 7181 | self.maybeMarkAllowZeroAccess(output_ty.ptrInfo(zcu)); | | |
| 7182 | | | |
| 7183 | // Pass any non-return outputs indirectly, if the constraint accepts a memory location | | |
| 7184 | llvm_ret_indirect[output.index] = constraintAllowsMemory(constraint); | | |
| 7185 | if (llvm_ret_indirect[output.index]) { | | |
| 7186 | // Pass the result by reference as an indirect output (e.g. "=*m") | | |
| 7187 | llvm_constraints.appendAssumeCapacity('*'); | | |
| 7188 | | | |
| 7189 | llvm_param_values[llvm_param_i] = output_inst; | | |
| 7190 | llvm_param_types[llvm_param_i] = output_inst.typeOfWip(&self.wip); | | |
| 7191 | llvm_param_attrs[llvm_param_i] = elem_llvm_ty; | | |
| 7192 | llvm_param_i += 1; | | |
| 7193 | } else { | | |
| 7194 | // Pass the result directly (e.g. "=r") | | |
| 7195 | llvm_ret_types[llvm_ret_i] = elem_llvm_ty; | | |
| 7196 | llvm_ret_i += 1; | | |
| 7197 | } | | |
| 7198 | } else { | | |
| 7199 | switch (constraint[0]) { | | |
| 7200 | '=' => {}, | | |
| 7201 | else => return self.todo("unsupported output constraint on result type '{s}'", .{ | | |
| 7202 | constraint, | | |
| 7203 | }), | | |
| 7204 | } | | |
| 7205 | | | |
| 7206 | llvm_ret_indirect[output.index] = false; | | |
| 7207 | | | |
| 7208 | const ret_ty = self.typeOfIndex(inst); | | |
| 7209 | llvm_ret_types[llvm_ret_i] = try o.lowerType(pt, ret_ty); | | |
| 7210 | llvm_ret_i += 1; | | |
| 7211 | } | | |
| 7212 | | | |
| 7213 | // LLVM uses commas internally to separate different constraints, | | |
| 7214 | // alternative constraints are achieved with pipes. | | |
| 7215 | // We still allow the user to use commas in a way that is similar | | |
| 7216 | // to GCC's inline assembly. | | |
| 7217 | // http://llvm.org/docs/LangRef.html#constraint-codes | | |
| 7218 | for (constraint[1..]) |byte| { | | |
| 7219 | switch (byte) { | | |
| 7220 | ',' => llvm_constraints.appendAssumeCapacity('|'), | | |
| 7221 | '*' => {}, // Indirect outputs are handled above | | |
| 7222 | else => llvm_constraints.appendAssumeCapacity(byte), | | |
| 7223 | } | | |
| 7224 | } | | |
| 7225 | | | |
| 7226 | if (!std.mem.eql(u8, name, "_")) { | | |
| 7227 | const gop = name_map.getOrPutAssumeCapacity(name); | | |
| 7228 | if (gop.found_existing) return self.todo("duplicate asm output name '{s}'", .{name}); | | |
| 7229 | gop.value_ptr.* = @intCast(total_i); | | |
| 7230 | } | | |
| 7231 | total_i += 1; | | |
| 7232 | } | | |
| 7233 | | | |
| 7234 | it = unwrapped_asm.iterateInputs(); | | |
| 7235 | while (it.next()) |input| { | | |
| 7236 | const constraint = input.constraint; | | |
| 7237 | const name = input.name; | | |
| 7238 | | | |
| 7239 | const arg_llvm_value = try self.resolveInst(input.operand); | | |
| 7240 | const arg_ty = self.typeOf(input.operand); | | |
| 7241 | const is_by_ref = isByRef(arg_ty, zcu); | | |
| 7242 | if (is_by_ref) { | | |
| 7243 | if (constraintAllowsMemory(constraint)) { | | |
| 7244 | llvm_param_values[llvm_param_i] = arg_llvm_value; | | |
| 7245 | llvm_param_types[llvm_param_i] = arg_llvm_value.typeOfWip(&self.wip); | | |
| 7246 | } else { | | |
| 7247 | const alignment = arg_ty.abiAlignment(zcu).toLlvm(); | | |
| 7248 | const arg_llvm_ty = try o.lowerType(pt, arg_ty); | | |
| 7249 | const load_inst = | | |
| 7250 | try self.wip.load(.normal, arg_llvm_ty, arg_llvm_value, alignment, ""); | | |
| 7251 | llvm_param_values[llvm_param_i] = load_inst; | | |
| 7252 | llvm_param_types[llvm_param_i] = arg_llvm_ty; | | |
| 7253 | } | | |
| 7254 | } else { | | |
| 7255 | if (constraintAllowsRegister(constraint)) { | | |
| 7256 | llvm_param_values[llvm_param_i] = arg_llvm_value; | | |
| 7257 | llvm_param_types[llvm_param_i] = arg_llvm_value.typeOfWip(&self.wip); | | |
| 7258 | } else { | | |
| 7259 | const alignment = arg_ty.abiAlignment(zcu).toLlvm(); | | |
| 7260 | const arg_ptr = try self.buildAlloca(arg_llvm_value.typeOfWip(&self.wip), alignment); | | |
| 7261 | _ = try self.wip.store(.normal, arg_llvm_value, arg_ptr, alignment); | | |
| 7262 | llvm_param_values[llvm_param_i] = arg_ptr; | | |
| 7263 | llvm_param_types[llvm_param_i] = arg_ptr.typeOfWip(&self.wip); | | |
| 7264 | } | | |
| 7265 | } | | |
| 7266 | | | |
| 7267 | try llvm_constraints.ensureUnusedCapacity(gpa, constraint.len + 1); | | |
| 7268 | if (total_i != 0) { | | |
| 7269 | llvm_constraints.appendAssumeCapacity(','); | | |
| 7270 | } | | |
| 7271 | for (constraint) |byte| { | | |
| 7272 | llvm_constraints.appendAssumeCapacity(switch (byte) { | | |
| 7273 | ',' => '|', | | |
| 7274 | else => byte, | | |
| 7275 | }); | | |
| 7276 | } | | |
| 7277 | | | |
| 7278 | if (!std.mem.eql(u8, name, "_")) { | | |
| 7279 | const gop = name_map.getOrPutAssumeCapacity(name); | | |
| 7280 | if (gop.found_existing) return self.todo("duplicate asm input name '{s}'", .{name}); | | |
| 7281 | gop.value_ptr.* = @intCast(total_i); | | |
| 7282 | } | | |
| 7283 | | | |
| 7284 | // In the case of indirect inputs, LLVM requires the callsite to have | | |
| 7285 | // an elementtype(<ty>) attribute. | | |
| 7286 | llvm_param_attrs[llvm_param_i] = if (constraint[0] == '*') blk: { | | |
| 7287 | if (!is_by_ref) self.maybeMarkAllowZeroAccess(arg_ty.ptrInfo(zcu)); | | |
| 7288 | | | |
| 7289 | break :blk try o.lowerType(pt, if (is_by_ref) arg_ty else arg_ty.childType(zcu)); | | |
| 7290 | } else .none; | | |
| 7291 | | | |
| 7292 | llvm_param_i += 1; | | |
| 7293 | total_i += 1; | | |
| 7294 | } | | |
| 7295 | | | |
| 7296 | it = unwrapped_asm.iterateOutputs(); | | |
| 7297 | while (it.next()) |output| { | | |
| 7298 | const constraint = output.constraint; | | |
| 7299 | | | |
| 7300 | if (constraint[0] != '+') continue; | | |
| 7301 | | | |
| 7302 | const rw_ty = self.typeOf(output.operand); | | |
| 7303 | const llvm_elem_ty = try o.lowerType(pt, rw_ty.childType(zcu)); | | |
| 7304 | if (llvm_ret_indirect[output.index]) { | | |
| 7305 | llvm_param_values[llvm_param_i] = llvm_rw_vals[output.index]; | | |
| 7306 | llvm_param_types[llvm_param_i] = llvm_rw_vals[output.index].typeOfWip(&self.wip); | | |
| 7307 | } else { | | |
| 7308 | const alignment = rw_ty.abiAlignment(zcu).toLlvm(); | | |
| 7309 | const loaded = try self.wip.load( | | |
| 7310 | if (rw_ty.isVolatilePtr(zcu)) .@"volatile" else .normal, | | |
| 7311 | llvm_elem_ty, | | |
| 7312 | llvm_rw_vals[output.index], | | |
| 7313 | alignment, | | |
| 7314 | "", | | |
| 7315 | ); | | |
| 7316 | llvm_param_values[llvm_param_i] = loaded; | | |
| 7317 | llvm_param_types[llvm_param_i] = llvm_elem_ty; | | |
| 7318 | } | | |
| 7319 | | | |
| 7320 | try llvm_constraints.print(gpa, ",{d}", .{output.index}); | | |
| 7321 | | | |
| 7322 | // In the case of indirect inputs, LLVM requires the callsite to have | | |
| 7323 | // an elementtype(<ty>) attribute. | | |
| 7324 | llvm_param_attrs[llvm_param_i] = if (llvm_ret_indirect[output.index]) llvm_elem_ty else .none; | | |
| 7325 | | | |
| 7326 | llvm_param_i += 1; | | |
| 7327 | total_i += 1; | | |
| 7328 | } | | |
| 7329 | | | |
| 7330 | if (total_i != 0) try llvm_constraints.append(gpa, ','); | | |
| 7331 | const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers); | | |
| 7332 | const clobbers_ty = clobbers_val.typeOf(zcu); | | |
| 7333 | var clobbers_bigint_buf: Value.BigIntSpace = undefined; | | |
| 7334 | const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu); | | |
| 7335 | for (0..clobbers_ty.structFieldCount(zcu)) |field_index| { | | |
| 7336 | assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type); | | |
| 7337 | const limb_bits = @bitSizeOf(std.math.big.Limb); | | |
| 7338 | if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false | | |
| 7339 | switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) { | | |
| 7340 | 0 => continue, // field is false | | |
| 7341 | 1 => {}, // field is true | | |
| 7342 | } | | |
| 7343 | const name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?; | | |
| 7344 | total_i += try appendConstraints(gpa, &llvm_constraints, name, target); | | |
| 7345 | } | | |
| 7346 | | | |
| 7347 | // We have finished scanning through all inputs/outputs, so the number of | | |
| 7348 | // parameters and return values is known. | | |
| 7349 | const param_count = llvm_param_i; | | |
| 7350 | const return_count = llvm_ret_i; | | |
| 7351 | | | |
| 7352 | // For some targets, Clang unconditionally adds some clobbers to all inline assembly. | | |
| 7353 | // While this is probably not strictly necessary, if we don't follow Clang's lead | | |
| 7354 | // here then we may risk tripping LLVM bugs since anything not used by Clang tends | | |
| 7355 | // to be buggy and regress often. | | |
| 7356 | switch (target.cpu.arch) { | | |
| 7357 | .x86_64, .x86 => { | | |
| 7358 | try llvm_constraints.appendSlice(gpa, "~{dirflag},~{fpsr},~{flags},"); | | |
| 7359 | total_i += 3; | | |
| 7360 | }, | | |
| 7361 | .mips, .mipsel, .mips64, .mips64el => { | | |
| 7362 | try llvm_constraints.appendSlice(gpa, "~{$1},"); | | |
| 7363 | total_i += 1; | | |
| 7364 | }, | | |
| 7365 | else => {}, | | |
| 7366 | } | | |
| 7367 | | | |
| 7368 | if (std.mem.endsWith(u8, llvm_constraints.items, ",")) llvm_constraints.items.len -= 1; | | |
| 7369 | | | |
| 7370 | const asm_source = unwrapped_asm.source; | | |
| 7371 | | | |
| 7372 | // hackety hacks until stage2 has proper inline asm in the frontend. | | |
| 7373 | var rendered_template = std.array_list.Managed(u8).init(gpa); | | |
| 7374 | defer rendered_template.deinit(); | | |
| 7375 | | | |
| 7376 | const State = enum { start, percent, input, modifier }; | | |
| 7377 | | | |
| 7378 | var state: State = .start; | | |
| 7379 | | | |
| 7380 | var name_start: usize = undefined; | | |
| 7381 | var modifier_start: usize = undefined; | | |
| 7382 | for (asm_source, 0..) |byte, i| { | | |
| 7383 | switch (state) { | | |
| 7384 | .start => switch (byte) { | | |
| 7385 | '%' => state = .percent, | | |
| 7386 | '$' => try rendered_template.appendSlice("$$"), | | |
| 7387 | else => try rendered_template.append(byte), | | |
| 7388 | }, | | |
| 7389 | .percent => switch (byte) { | | |
| 7390 | '%' => { | | |
| 7391 | try rendered_template.append('%'); | | |
| 7392 | state = .start; | | |
| 7393 | }, | | |
| 7394 | '[' => { | | |
| 7395 | try rendered_template.append('$'); | | |
| 7396 | try rendered_template.append('{'); | | |
| 7397 | name_start = i + 1; | | |
| 7398 | state = .input; | | |
| 7399 | }, | | |
| 7400 | '=' => { | | |
| 7401 | try rendered_template.appendSlice("${:uid}"); | | |
| 7402 | state = .start; | | |
| 7403 | }, | | |
| 7404 | else => { | | |
| 7405 | try rendered_template.append('%'); | | |
| 7406 | try rendered_template.append(byte); | | |
| 7407 | state = .start; | | |
| 7408 | }, | | |
| 7409 | }, | | |
| 7410 | .input => switch (byte) { | | |
| 7411 | ']', ':' => { | | |
| 7412 | const name = asm_source[name_start..i]; | | |
| 7413 | | | |
| 7414 | const index = name_map.get(name) orelse { | | |
| 7415 | // we should validate the assembly in Sema; by now it is too late | | |
| 7416 | return self.todo("unknown input or output name: '{s}'", .{name}); | | |
| 7417 | }; | | |
| 7418 | try rendered_template.print("{d}", .{index}); | | |
| 7419 | if (byte == ':') { | | |
| 7420 | try rendered_template.append(':'); | | |
| 7421 | modifier_start = i + 1; | | |
| 7422 | state = .modifier; | | |
| 7423 | } else { | | |
| 7424 | try rendered_template.append('}'); | | |
| 7425 | state = .start; | | |
| 7426 | } | | |
| 7427 | }, | | |
| 7428 | else => {}, | | |
| 7429 | }, | | |
| 7430 | .modifier => switch (byte) { | | |
| 7431 | ']' => { | | |
| 7432 | try rendered_template.appendSlice(asm_source[modifier_start..i]); | | |
| 7433 | try rendered_template.append('}'); | | |
| 7434 | state = .start; | | |
| 7435 | }, | | |
| 7436 | else => {}, | | |
| 7437 | }, | | |
| 7438 | } | | |
| 7439 | } | | |
| 7440 | | | |
| 7441 | var attributes: Builder.FunctionAttributes.Wip = .{}; | | |
| 7442 | defer attributes.deinit(&o.builder); | | |
| 7443 | for (llvm_param_attrs[0..param_count], 0..) |llvm_elem_ty, i| if (llvm_elem_ty != .none) | | |
| 7444 | try attributes.addParamAttr(i, .{ .elementtype = llvm_elem_ty }, &o.builder); | | |
| 7445 | | | |
| 7446 | const ret_llvm_ty = switch (return_count) { | | |
| 7447 | 0 => .void, | | |
| 7448 | 1 => llvm_ret_types[0], | | |
| 7449 | else => try o.builder.structType(.normal, llvm_ret_types), | | |
| 7450 | }; | | |
| 7451 | const llvm_fn_ty = try o.builder.fnType(ret_llvm_ty, llvm_param_types[0..param_count], .normal); | | |
| 7452 | const call = try self.wip.callAsm( | | |
| 7453 | try attributes.finish(&o.builder), | | |
| 7454 | llvm_fn_ty, | | |
| 7455 | .{ .sideeffect = is_volatile }, | | |
| 7456 | try o.builder.string(rendered_template.items), | | |
| 7457 | try o.builder.string(llvm_constraints.items), | | |
| 7458 | llvm_param_values[0..param_count], | | |
| 7459 | "", | | |
| 7460 | ); | | |
| 7461 | | | |
| 7462 | var ret_val = call; | | |
| 7463 | llvm_ret_i = 0; | | |
| 7464 | for (outputs, 0..) |output, i| { | | |
| 7465 | if (llvm_ret_indirect[i]) continue; | | |
| 7466 | | | |
| 7467 | const output_value = if (return_count > 1) | | |
| 7468 | try self.wip.extractValue(call, &[_]u32{@intCast(llvm_ret_i)}, "") | | |
| 7469 | else | | |
| 7470 | call; | | |
| 7471 | | | |
| 7472 | if (output != .none) { | | |
| 7473 | const output_ptr = try self.resolveInst(output); | | |
| 7474 | const output_ptr_ty = self.typeOf(output); | | |
| 7475 | const alignment = output_ptr_ty.ptrAlignment(zcu).toLlvm(); | | |
| 7476 | _ = try self.wip.store( | | |
| 7477 | if (output_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal, | | |
| 7478 | output_value, | | |
| 7479 | output_ptr, | | |
| 7480 | alignment, | | |
| 7481 | ); | | |
| 7482 | } else { | | |
| 7483 | ret_val = output_value; | | |
| 7484 | } | | |
| 7485 | llvm_ret_i += 1; | | |
| 7486 | } | | |
| 7487 | | | |
| 7488 | return ret_val; | | |
| 7489 | } | | |
| 7490 | | | |
| 7491 | fn airIsNonNull( | | |
| 7492 | self: *FuncGen, | | |
| 7493 | inst: Air.Inst.Index, | | |
| 7494 | operand_is_ptr: bool, | | |
| 7495 | cond: Builder.IntegerCondition, | | |
| 7496 | ) !Builder.Value { | | |
| 7497 | const o = self.ng.object; | | |
| 7498 | const pt = self.ng.pt; | | |
| 7499 | const zcu = pt.zcu; | | |
| 7500 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | | |
| 7501 | const operand = try self.resolveInst(un_op); | | |
| 7502 | const operand_ty = self.typeOf(un_op); | | |
| 7503 | const optional_ty = if (operand_is_ptr) operand_ty.childType(zcu) else operand_ty; | | |
| 7504 | const optional_llvm_ty = try o.lowerType(pt, optional_ty); | | |
| 7505 | const payload_ty = optional_ty.optionalChild(zcu); | | |
| 7506 | | | |
| 7507 | const access_kind: Builder.MemoryAccessKind = | | |
| 7508 | if (operand_is_ptr and operand_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; | | |
| 7509 | | | |
| 7510 | if (operand_is_ptr) self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu)); | | |
| 7511 | | | |
| 7512 | if (optional_ty.optionalReprIsPayload(zcu)) { | | |
| 7513 | const loaded = if (operand_is_ptr) | | |
| 7514 | try self.wip.load(access_kind, optional_llvm_ty, operand, operand_ty.ptrAlignment(zcu).toLlvm(), "") | | |
| 7515 | else | | |
| 7516 | operand; | | |
| 7517 | if (payload_ty.isSlice(zcu)) { | | |
| 7518 | const slice_ptr = try self.wip.extractValue(loaded, &.{0}, ""); | | |
| 7519 | const ptr_ty = try o.builder.ptrType(toLlvmAddressSpace( | | |
| 7520 | payload_ty.ptrAddressSpace(zcu), | | |
| 7521 | zcu.getTarget(), | | |
| 7522 | )); | | |
| 7523 | return self.wip.icmp(cond, slice_ptr, try o.builder.nullValue(ptr_ty), ""); | | |
| 7524 | } | | |
| 7525 | return self.wip.icmp(cond, loaded, try o.builder.zeroInitValue(optional_llvm_ty), ""); | | |
| 7526 | } | | |
| 7527 | | | |
| 7528 | comptime assert(optional_layout_version == 3); | | |
| 7529 | | | |
| 7530 | if (!payload_ty.hasRuntimeBits(zcu)) { | | |
| 7531 | const loaded = if (operand_is_ptr) | | |
| 7532 | try self.wip.load(access_kind, optional_llvm_ty, operand, operand_ty.ptrAlignment(zcu).toLlvm(), "") | | |
| 7533 | else | | |
| 7534 | operand; | | |
| 7535 | return self.wip.icmp(cond, loaded, try o.builder.intValue(.i8, 0), ""); | | |
| 7536 | } | | |
| 7537 | | | |
| 7538 | const is_by_ref = operand_is_ptr or isByRef(optional_ty, zcu); | | |
| 7539 | return self.optCmpNull(cond, optional_llvm_ty, operand, is_by_ref, access_kind); | | |
| 7540 | } | | |
| 7541 | | | |
| 7542 | fn airIsErr( | | |
| 7543 | self: *FuncGen, | | |
| 7544 | inst: Air.Inst.Index, | | |
| 7545 | cond: Builder.IntegerCondition, | | |
| 7546 | operand_is_ptr: bool, | | |
| 7547 | ) !Builder.Value { | | |
| 7548 | const o = self.ng.object; | | |
| 7549 | const pt = self.ng.pt; | | |
| 7550 | const zcu = pt.zcu; | | |
| 7551 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | | |
| 7552 | const operand = try self.resolveInst(un_op); | | |
| 7553 | const operand_ty = self.typeOf(un_op); | | |
| 7554 | const err_union_ty = if (operand_is_ptr) operand_ty.childType(zcu) else operand_ty; | | |
| 7555 | const payload_ty = err_union_ty.errorUnionPayload(zcu); | | |
| 7556 | const error_type = try o.errorIntType(pt); | | |
| 7557 | const zero = try o.builder.intValue(error_type, 0); | | |
| 7558 | | | |
| 7559 | const access_kind: Builder.MemoryAccessKind = | | |
| 7560 | if (operand_is_ptr and operand_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; | | |
| 7561 | | | |
| 7562 | if (err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) { | | |
| 7563 | const val: Builder.Constant = switch (cond) { | | |
| 7564 | .eq => .true, // 0 == 0 | | |
| 7565 | .ne => .false, // 0 != 0 | | |
| 7566 | else => unreachable, | | |
| 7567 | }; | | |
| 7568 | return val.toValue(); | | |
| 7569 | } | | |
| 7570 | | | |
| 7571 | if (operand_is_ptr) self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu)); | | |
| 7572 | | | |
| 7573 | if (!payload_ty.hasRuntimeBits(zcu)) { | | |
| 7574 | const loaded = if (operand_is_ptr) | | |
| 7575 | try self.wip.load(access_kind, try o.lowerType(pt, err_union_ty), operand, operand_ty.ptrAlignment(zcu).toLlvm(), "") | | |
| 7576 | else | | |
| 7577 | operand; | | |
| 7578 | return self.wip.icmp(cond, loaded, zero, ""); | | |
| 7579 | } | | |
| 7580 | | | |
| 7581 | const err_field_index = try errUnionErrorOffset(payload_ty, pt); | | |
| 7582 | | | |
| 7583 | const loaded = if (operand_is_ptr or isByRef(err_union_ty, zcu)) loaded: { | | |
| 7584 | const err_union_llvm_ty = try o.lowerType(pt, err_union_ty); | | |
| 7585 | const err_alignment = if (operand_is_ptr) | | |
| 7586 | operand_ty.ptrAlignment(zcu).minStrict(Type.anyerror.abiAlignment(zcu)) | | |
| 7587 | else | | |
| 7588 | .none; | | |
| 7589 | const err_field_ptr = | | |
| 7590 | try self.wip.gepStruct(err_union_llvm_ty, operand, err_field_index, ""); | | |
| 7591 | break :loaded try self.wip.load(access_kind, error_type, err_field_ptr, err_alignment.toLlvm(), ""); | | |
| 7592 | } else try self.wip.extractValue(operand, &.{err_field_index}, ""); | | |
| 7593 | return self.wip.icmp(cond, loaded, zero, ""); | | |
| 7594 | } | | |
| 7595 | | | |
| 7596 | fn airOptionalPayloadPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 7597 | const o = self.ng.object; | | |
| 7598 | const pt = self.ng.pt; | | |
| 7599 | const zcu = pt.zcu; | | |
| 7600 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 7601 | const operand = try self.resolveInst(ty_op.operand); | | |
| 7602 | const optional_ty = self.typeOf(ty_op.operand).childType(zcu); | | |
| 7603 | const payload_ty = optional_ty.optionalChild(zcu); | | |
| 7604 | if (!payload_ty.hasRuntimeBits(zcu)) { | | |
| 7605 | // We have a pointer to a zero-bit value and we need to return | | |
| 7606 | // a pointer to a zero-bit value. | | |
| 7607 | return operand; | | |
| 7608 | } | | |
| 7609 | if (optional_ty.optionalReprIsPayload(zcu)) { | | |
| 7610 | // The payload and the optional are the same value. | | |
| 7611 | return operand; | | |
| 7612 | } | | |
| 7613 | return self.wip.gepStruct(try o.lowerType(pt, optional_ty), operand, 0, ""); | | |
| 7614 | } | | |
| 7615 | | | |
| 7616 | fn airOptionalPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 7617 | comptime assert(optional_layout_version == 3); | | |
| 7618 | | | |
| 7619 | const o = self.ng.object; | | |
| 7620 | const pt = self.ng.pt; | | |
| 7621 | const zcu = pt.zcu; | | |
| 7622 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 7623 | const operand = try self.resolveInst(ty_op.operand); | | |
| 7624 | const optional_ptr_ty = self.typeOf(ty_op.operand); | | |
| 7625 | const optional_ty = optional_ptr_ty.childType(zcu); | | |
| 7626 | const payload_ty = optional_ty.optionalChild(zcu); | | |
| 7627 | const non_null_bit = try o.builder.intValue(.i8, 1); | | |
| 7628 | | | |
| 7629 | const access_kind: Builder.MemoryAccessKind = | | |
| 7630 | if (optional_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; | | |
| 7631 | | | |
| 7632 | if (!payload_ty.hasRuntimeBits(zcu)) { | | |
| 7633 | self.maybeMarkAllowZeroAccess(optional_ptr_ty.ptrInfo(zcu)); | | |
| 7634 | | | |
| 7635 | // We have a pointer to a i8. We need to set it to 1 and then return the same pointer. | | |
| 7636 | // Default alignment store because align of the non null bit is 1 anyway. | | |
| 7637 | _ = try self.wip.store(access_kind, non_null_bit, operand, .default); | | |
| 7638 | return operand; | | |
| 7639 | } | | |
| 7640 | if (optional_ty.optionalReprIsPayload(zcu)) { | | |
| 7641 | // The payload and the optional are the same value. | | |
| 7642 | // Setting to non-null will be done when the payload is set. | | |
| 7643 | return operand; | | |
| 7644 | } | | |
| 7645 | | | |
| 7646 | // First set the non-null bit. | | |
| 7647 | const optional_llvm_ty = try o.lowerType(pt, optional_ty); | | |
| 7648 | const non_null_ptr = try self.wip.gepStruct(optional_llvm_ty, operand, 1, ""); | | |
| 7649 | | | |
| 7650 | self.maybeMarkAllowZeroAccess(optional_ptr_ty.ptrInfo(zcu)); | | |
| 7651 | | | |
| 7652 | // Default alignment store because align of the non null bit is 1 anyway. | | |
| 7653 | _ = try self.wip.store(access_kind, non_null_bit, non_null_ptr, .default); | | |
| 7654 | | | |
| 7655 | // Then return the payload pointer (only if it's used). | | |
| 7656 | if (self.liveness.isUnused(inst)) return .none; | | |
| 7657 | | | |
| 7658 | return self.wip.gepStruct(optional_llvm_ty, operand, 0, ""); | | |
| 7659 | } | | |
| 7660 | | | |
| 7661 | fn airOptionalPayload(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 7662 | const o = self.ng.object; | | |
| 7663 | const pt = self.ng.pt; | | |
| 7664 | const zcu = pt.zcu; | | |
| 7665 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 7666 | const operand = try self.resolveInst(ty_op.operand); | | |
| 7667 | const optional_ty = self.typeOf(ty_op.operand); | | |
| 7668 | const payload_ty = self.typeOfIndex(inst); | | |
| 7669 | if (!payload_ty.hasRuntimeBits(zcu)) return .none; | | |
| 7670 | | | |
| 7671 | if (optional_ty.optionalReprIsPayload(zcu)) { | | |
| 7672 | // Payload value is the same as the optional value. | | |
| 7673 | return operand; | | |
| 7674 | } | | |
| 7675 | | | |
| 7676 | const opt_llvm_ty = try o.lowerType(pt, optional_ty); | | |
| 7677 | return self.optPayloadHandle(opt_llvm_ty, operand, optional_ty, false); | | |
| 7678 | } | | |
| 7679 | | | |
| 7680 | fn airErrUnionPayload(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) !Builder.Value { | | |
| 7681 | const o = self.ng.object; | | |
| 7682 | const pt = self.ng.pt; | | |
| 7683 | const zcu = pt.zcu; | | |
| 7684 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 7685 | const operand = try self.resolveInst(ty_op.operand); | | |
| 7686 | const operand_ty = self.typeOf(ty_op.operand); | | |
| 7687 | const err_union_ty = if (operand_is_ptr) operand_ty.childType(zcu) else operand_ty; | | |
| 7688 | const result_ty = self.typeOfIndex(inst); | | |
| 7689 | const payload_ty = if (operand_is_ptr) result_ty.childType(zcu) else result_ty; | | |
| 7690 | | | |
| 7691 | if (!payload_ty.hasRuntimeBits(zcu)) { | | |
| 7692 | return if (operand_is_ptr) operand else .none; | | |
| 7693 | } | | |
| 7694 | const offset = try errUnionPayloadOffset(payload_ty, pt); | | |
| 7695 | const err_union_llvm_ty = try o.lowerType(pt, err_union_ty); | | |
| 7696 | if (operand_is_ptr) { | | |
| 7697 | return self.wip.gepStruct(err_union_llvm_ty, operand, offset, ""); | | |
| 7698 | } else if (isByRef(err_union_ty, zcu)) { | | |
| 7699 | const payload_alignment = payload_ty.abiAlignment(zcu).toLlvm(); | | |
| 7700 | const payload_ptr = try self.wip.gepStruct(err_union_llvm_ty, operand, offset, ""); | | |
| 7701 | if (isByRef(payload_ty, zcu)) { | | |
| 7702 | return self.loadByRef(payload_ptr, payload_ty, payload_alignment, .normal); | | |
| 7703 | } | | |
| 7704 | const payload_llvm_ty = err_union_llvm_ty.structFields(&o.builder)[offset]; | | |
| 7705 | return self.wip.load(.normal, payload_llvm_ty, payload_ptr, payload_alignment, ""); | | |
| 7706 | } | | |
| 7707 | return self.wip.extractValue(operand, &.{offset}, ""); | | |
| 7708 | } | | |
| 7709 | | | |
| 7710 | fn airErrUnionErr( | | |
| 7711 | self: *FuncGen, | | |
| 7712 | inst: Air.Inst.Index, | | |
| 7713 | operand_is_ptr: bool, | | |
| 7714 | ) !Builder.Value { | | |
| 7715 | const o = self.ng.object; | | |
| 7716 | const pt = self.ng.pt; | | |
| 7717 | const zcu = pt.zcu; | | |
| 7718 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 7719 | const operand = try self.resolveInst(ty_op.operand); | | |
| 7720 | const operand_ty = self.typeOf(ty_op.operand); | | |
| 7721 | const error_type = try o.errorIntType(pt); | | |
| 7722 | const err_union_ty = if (operand_is_ptr) operand_ty.childType(zcu) else operand_ty; | | |
| 7723 | if (err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) { | | |
| 7724 | if (operand_is_ptr) { | | |
| 7725 | return operand; | | |
| 7726 | } else { | | |
| 7727 | return o.builder.intValue(error_type, 0); | | |
| 7728 | } | | |
| 7729 | } | | |
| 7730 | | | |
| 7731 | const access_kind: Builder.MemoryAccessKind = | | |
| 7732 | if (operand_is_ptr and operand_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; | | |
| 7733 | | | |
| 7734 | const payload_ty = err_union_ty.errorUnionPayload(zcu); | | |
| 7735 | if (!payload_ty.hasRuntimeBits(zcu)) { | | |
| 7736 | if (!operand_is_ptr) return operand; | | |
| 7737 | | | |
| 7738 | self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu)); | | |
| 7739 | | | |
| 7740 | return self.wip.load(access_kind, error_type, operand, operand_ty.ptrAlignment(zcu).toLlvm(), ""); | | |
| 7741 | } | | |
| 7742 | | | |
| 7743 | const offset = try errUnionErrorOffset(payload_ty, pt); | | |
| 7744 | | | |
| 7745 | if (operand_is_ptr or isByRef(err_union_ty, zcu)) { | | |
| 7746 | if (operand_is_ptr) self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu)); | | |
| 7747 | | | |
| 7748 | const err_union_llvm_ty = try o.lowerType(pt, err_union_ty); | | |
| 7749 | const err_field_ptr = try self.wip.gepStruct(err_union_llvm_ty, operand, offset, ""); | | |
| 7750 | return self.wip.load(access_kind, error_type, err_field_ptr, .default, ""); | | |
| 7751 | } | | |
| 7752 | | | |
| 7753 | return self.wip.extractValue(operand, &.{offset}, ""); | | |
| 7754 | } | | |
| 7755 | | | |
| 7756 | fn airErrUnionPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 7757 | const o = self.ng.object; | | |
| 7758 | const pt = self.ng.pt; | | |
| 7759 | const zcu = pt.zcu; | | |
| 7760 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 7761 | const operand = try self.resolveInst(ty_op.operand); | | |
| 7762 | const err_union_ptr_ty = self.typeOf(ty_op.operand); | | |
| 7763 | const err_union_ty = err_union_ptr_ty.childType(zcu); | | |
| 7764 | const err_union_ptr_align = err_union_ptr_ty.ptrAlignment(zcu); | | |
| 7765 | | | |
| 7766 | const payload_ty = err_union_ty.errorUnionPayload(zcu); | | |
| 7767 | const non_error_val = try o.builder.intValue(try o.errorIntType(pt), 0); | | |
| 7768 | | | |
| 7769 | const access_kind: Builder.MemoryAccessKind = | | |
| 7770 | if (err_union_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; | | |
| 7771 | | | |
| 7772 | if (!payload_ty.hasRuntimeBits(zcu)) { | | |
| 7773 | self.maybeMarkAllowZeroAccess(err_union_ptr_ty.ptrInfo(zcu)); | | |
| 7774 | _ = try self.wip.store(access_kind, non_error_val, operand, err_union_ptr_align.toLlvm()); | | |
| 7775 | return operand; | | |
| 7776 | } | | |
| 7777 | const err_union_llvm_ty = try o.lowerType(pt, err_union_ty); | | |
| 7778 | { | | |
| 7779 | self.maybeMarkAllowZeroAccess(err_union_ptr_ty.ptrInfo(zcu)); | | |
| 7780 | | | |
| 7781 | const err_int_ty = try pt.errorIntType(); | | |
| 7782 | const error_alignment = err_int_ty.abiAlignment(zcu).minStrict(err_union_ptr_align).toLlvm(); | | |
| 7783 | const error_offset = try errUnionErrorOffset(payload_ty, pt); | | |
| 7784 | // First set the non-error value. | | |
| 7785 | const non_null_ptr = try self.wip.gepStruct(err_union_llvm_ty, operand, error_offset, ""); | | |
| 7786 | _ = try self.wip.store(access_kind, non_error_val, non_null_ptr, error_alignment); | | |
| 7787 | } | | |
| 7788 | // Then return the payload pointer (only if it is used). | | |
| 7789 | if (self.liveness.isUnused(inst)) return .none; | | |
| 7790 | | | |
| 7791 | const payload_offset = try errUnionPayloadOffset(payload_ty, pt); | | |
| 7792 | return self.wip.gepStruct(err_union_llvm_ty, operand, payload_offset, ""); | | |
| 7793 | } | | |
| 7794 | | | |
| 7795 | fn airErrReturnTrace(self: *FuncGen, _: Air.Inst.Index) !Builder.Value { | | |
| 7796 | assert(self.err_ret_trace != .none); | | |
| 7797 | return self.err_ret_trace; | | |
| 7798 | } | | |
| 7799 | | | |
| 7800 | fn airSetErrReturnTrace(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 7801 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | | |
| 7802 | self.err_ret_trace = try self.resolveInst(un_op); | | |
| 7803 | return .none; | | |
| 7804 | } | | |
| 7805 | | | |
| 7806 | fn airSaveErrReturnTraceIndex(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 7807 | const o = self.ng.object; | | |
| 7808 | const pt = self.ng.pt; | | |
| 7809 | const zcu = pt.zcu; | | |
| 7810 | | | |
| 7811 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | | |
| 7812 | const struct_ty = ty_pl.ty.toType(); | | |
| 7813 | const field_index = ty_pl.payload; | | |
| 7814 | | | |
| 7815 | const struct_llvm_ty = try o.lowerType(pt, struct_ty); | | |
| 7816 | const llvm_field_index = o.llvmFieldIndex(struct_ty, field_index).?; | | |
| 7817 | assert(self.err_ret_trace != .none); | | |
| 7818 | const field_ptr = try self.wip.gepStruct(struct_llvm_ty, self.err_ret_trace, llvm_field_index, ""); | | |
| 7819 | const field_alignment = struct_ty.explicitFieldAlignment(field_index, zcu); | | |
| 7820 | const field_ty = struct_ty.fieldType(field_index, zcu); | | |
| 7821 | const field_ptr_ty = try pt.ptrType(.{ | | |
| 7822 | .child = field_ty.toIntern(), | | |
| 7823 | .flags = .{ .alignment = field_alignment }, | | |
| 7824 | }); | | |
| 7825 | return self.load(field_ptr, field_ptr_ty); | | |
| 7826 | } | | |
| 7827 | | | |
| 7828 | /// As an optimization, we want to avoid unnecessary copies of | | |
| 7829 | /// error union/optional types when returning from a function. | | |
| 7830 | /// Here, we scan forward in the current block, looking to see | | |
| 7831 | /// if the next instruction is a return (ignoring debug instructions). | | |
| 7832 | /// | | |
| 7833 | /// The first instruction of `body_tail` is a wrap instruction. | | |
| 7834 | fn isNextRet( | | |
| 7835 | self: *FuncGen, | | |
| 7836 | body_tail: []const Air.Inst.Index, | | |
| 7837 | ) bool { | | |
| 7838 | const air_tags = self.air.instructions.items(.tag); | | |
| 7839 | for (body_tail[1..]) |body_inst| { | | |
| 7840 | switch (air_tags[@intFromEnum(body_inst)]) { | | |
| 7841 | .ret => return true, | | |
| 7842 | .dbg_stmt => continue, | | |
| 7843 | else => return false, | | |
| 7844 | } | | |
| 7845 | } | | |
| 7846 | // The only way to get here is to hit the end of a loop instruction | | |
| 7847 | // (implicit repeat). | | |
| 7848 | return false; | | |
| 7849 | } | | |
| 7850 | | | |
| 7851 | fn airWrapOptional(self: *FuncGen, body_tail: []const Air.Inst.Index) !Builder.Value { | | |
| 7852 | const o = self.ng.object; | | |
| 7853 | const pt = self.ng.pt; | | |
| 7854 | const zcu = pt.zcu; | | |
| 7855 | const inst = body_tail[0]; | | |
| 7856 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 7857 | const payload_ty = self.typeOf(ty_op.operand); | | |
| 7858 | const non_null_bit = try o.builder.intValue(.i8, 1); | | |
| 7859 | comptime assert(optional_layout_version == 3); | | |
| 7860 | assert(payload_ty.hasRuntimeBits(zcu)); | | |
| 7861 | const operand = try self.resolveInst(ty_op.operand); | | |
| 7862 | const optional_ty = self.typeOfIndex(inst); | | |
| 7863 | if (optional_ty.optionalReprIsPayload(zcu)) return operand; | | |
| 7864 | const llvm_optional_ty = try o.lowerType(pt, optional_ty); | | |
| 7865 | if (isByRef(optional_ty, zcu)) { | | |
| 7866 | const directReturn = self.isNextRet(body_tail); | | |
| 7867 | const optional_ptr = if (directReturn) | | |
| 7868 | self.ret_ptr | | |
| 7869 | else brk: { | | |
| 7870 | const alignment = optional_ty.abiAlignment(zcu).toLlvm(); | | |
| 7871 | const optional_ptr = try self.buildAlloca(llvm_optional_ty, alignment); | | |
| 7872 | break :brk optional_ptr; | | |
| 7873 | }; | | |
| 7874 | | | |
| 7875 | const payload_ptr = try self.wip.gepStruct(llvm_optional_ty, optional_ptr, 0, ""); | | |
| 7876 | const payload_ptr_ty = try pt.singleMutPtrType(payload_ty); | | |
| 7877 | try self.store(payload_ptr, payload_ptr_ty, operand, .none); | | |
| 7878 | const non_null_ptr = try self.wip.gepStruct(llvm_optional_ty, optional_ptr, 1, ""); | | |
| 7879 | _ = try self.wip.store(.normal, non_null_bit, non_null_ptr, .default); | | |
| 7880 | return optional_ptr; | | |
| 7881 | } | | |
| 7882 | return self.wip.buildAggregate(llvm_optional_ty, &.{ operand, non_null_bit }, ""); | | |
| 7883 | } | | |
| 7884 | | | |
| 7885 | fn airWrapErrUnionPayload(self: *FuncGen, body_tail: []const Air.Inst.Index) !Builder.Value { | | |
| 7886 | const o = self.ng.object; | | |
| 7887 | const pt = self.ng.pt; | | |
| 7888 | const zcu = pt.zcu; | | |
| 7889 | const inst = body_tail[0]; | | |
| 7890 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 7891 | const err_un_ty = self.typeOfIndex(inst); | | |
| 7892 | const operand = try self.resolveInst(ty_op.operand); | | |
| 7893 | const payload_ty = self.typeOf(ty_op.operand); | | |
| 7894 | assert(payload_ty.hasRuntimeBits(zcu)); | | |
| 7895 | const ok_err_code = try o.builder.intValue(try o.errorIntType(pt), 0); | | |
| 7896 | const err_un_llvm_ty = try o.lowerType(pt, err_un_ty); | | |
| 7897 | | | |
| 7898 | const payload_offset = try errUnionPayloadOffset(payload_ty, pt); | | |
| 7899 | const error_offset = try errUnionErrorOffset(payload_ty, pt); | | |
| 7900 | if (isByRef(err_un_ty, zcu)) { | | |
| 7901 | const directReturn = self.isNextRet(body_tail); | | |
| 7902 | const result_ptr = if (directReturn) | | |
| 7903 | self.ret_ptr | | |
| 7904 | else brk: { | | |
| 7905 | const alignment = err_un_ty.abiAlignment(pt.zcu).toLlvm(); | | |
| 7906 | const result_ptr = try self.buildAlloca(err_un_llvm_ty, alignment); | | |
| 7907 | break :brk result_ptr; | | |
| 7908 | }; | | |
| 7909 | | | |
| 7910 | const err_ptr = try self.wip.gepStruct(err_un_llvm_ty, result_ptr, error_offset, ""); | | |
| 7911 | const err_int_ty = try pt.errorIntType(); | | |
| 7912 | const error_alignment = err_int_ty.abiAlignment(pt.zcu).toLlvm(); | | |
| 7913 | _ = try self.wip.store(.normal, ok_err_code, err_ptr, error_alignment); | | |
| 7914 | const payload_ptr = try self.wip.gepStruct(err_un_llvm_ty, result_ptr, payload_offset, ""); | | |
| 7915 | const payload_ptr_ty = try pt.singleMutPtrType(payload_ty); | | |
| 7916 | try self.store(payload_ptr, payload_ptr_ty, operand, .none); | | |
| 7917 | return result_ptr; | | |
| 7918 | } | | |
| 7919 | var fields: [2]Builder.Value = undefined; | | |
| 7920 | fields[payload_offset] = operand; | | |
| 7921 | fields[error_offset] = ok_err_code; | | |
| 7922 | return self.wip.buildAggregate(err_un_llvm_ty, &fields, ""); | | |
| 7923 | } | | |
| 7924 | | | |
| 7925 | fn airWrapErrUnionErr(self: *FuncGen, body_tail: []const Air.Inst.Index) !Builder.Value { | | |
| 7926 | const o = self.ng.object; | | |
| 7927 | const pt = self.ng.pt; | | |
| 7928 | const zcu = pt.zcu; | | |
| 7929 | const inst = body_tail[0]; | | |
| 7930 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 7931 | const err_un_ty = self.typeOfIndex(inst); | | |
| 7932 | const payload_ty = err_un_ty.errorUnionPayload(zcu); | | |
| 7933 | const operand = try self.resolveInst(ty_op.operand); | | |
| 7934 | if (!payload_ty.hasRuntimeBits(zcu)) return operand; | | |
| 7935 | const err_un_llvm_ty = try o.lowerType(pt, err_un_ty); | | |
| 7936 | | | |
| 7937 | const payload_offset = try errUnionPayloadOffset(payload_ty, pt); | | |
| 7938 | const error_offset = try errUnionErrorOffset(payload_ty, pt); | | |
| 7939 | if (isByRef(err_un_ty, zcu)) { | | |
| 7940 | const directReturn = self.isNextRet(body_tail); | | |
| 7941 | const result_ptr = if (directReturn) | | |
| 7942 | self.ret_ptr | | |
| 7943 | else brk: { | | |
| 7944 | const alignment = err_un_ty.abiAlignment(zcu).toLlvm(); | | |
| 7945 | const result_ptr = try self.buildAlloca(err_un_llvm_ty, alignment); | | |
| 7946 | break :brk result_ptr; | | |
| 7947 | }; | | |
| 7948 | | | |
| 7949 | const err_ptr = try self.wip.gepStruct(err_un_llvm_ty, result_ptr, error_offset, ""); | | |
| 7950 | const err_int_ty = try pt.errorIntType(); | | |
| 7951 | const error_alignment = err_int_ty.abiAlignment(zcu).toLlvm(); | | |
| 7952 | _ = try self.wip.store(.normal, operand, err_ptr, error_alignment); | | |
| 7953 | const payload_ptr = try self.wip.gepStruct(err_un_llvm_ty, result_ptr, payload_offset, ""); | | |
| 7954 | const payload_ptr_ty = try pt.singleMutPtrType(payload_ty); | | |
| 7955 | // TODO store undef to payload_ptr | | |
| 7956 | _ = payload_ptr; | | |
| 7957 | _ = payload_ptr_ty; | | |
| 7958 | return result_ptr; | | |
| 7959 | } | | |
| 7960 | | | |
| 7961 | // TODO set payload bytes to undef | | |
| 7962 | const undef = try o.builder.undefValue(err_un_llvm_ty); | | |
| 7963 | return self.wip.insertValue(undef, operand, &.{error_offset}, ""); | | |
| 7964 | } | | |
| 7965 | | | |
| 7966 | fn airWasmMemorySize(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 7967 | const o = self.ng.object; | | |
| 7968 | const pt = self.ng.pt; | | |
| 7969 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | | |
| 7970 | const index = pl_op.payload; | | |
| 7971 | const llvm_usize = try o.lowerType(pt, Type.usize); | | |
| 7972 | return self.wip.callIntrinsic(.normal, .none, .@"wasm.memory.size", &.{llvm_usize}, &.{ | | |
| 7973 | try o.builder.intValue(.i32, index), | | |
| 7974 | }, ""); | | |
| 7975 | } | | |
| 7976 | | | |
| 7977 | fn airWasmMemoryGrow(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 7978 | const o = self.ng.object; | | |
| 7979 | const pt = self.ng.pt; | | |
| 7980 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | | |
| 7981 | const index = pl_op.payload; | | |
| 7982 | const llvm_isize = try o.lowerType(pt, Type.isize); | | |
| 7983 | return self.wip.callIntrinsic(.normal, .none, .@"wasm.memory.grow", &.{llvm_isize}, &.{ | | |
| 7984 | try o.builder.intValue(.i32, index), try self.resolveInst(pl_op.operand), | | |
| 7985 | }, ""); | | |
| 7986 | } | | |
| 7987 | | | |
| 7988 | fn airRuntimeNavPtr(fg: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 7989 | const o = fg.ng.object; | | |
| 7990 | const pt = fg.ng.pt; | | |
| 7991 | const ty_nav = fg.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; | | |
| 7992 | const llvm_ptr_const = try o.lowerNavRefValue(pt, ty_nav.nav); | | |
| 7993 | return llvm_ptr_const.toValue(); | | |
| 7994 | } | | |
| 7995 | | | |
| 7996 | fn airMin(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 7997 | const o = self.ng.object; | | |
| 7998 | const pt = self.ng.pt; | | |
| 7999 | const zcu = pt.zcu; | | |
| 8000 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8001 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8002 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8003 | const inst_ty = self.typeOfIndex(inst); | | |
| 8004 | const scalar_ty = inst_ty.scalarType(zcu); | | |
| 8005 | | | |
| 8006 | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.fmin, .normal, inst_ty, 2, .{ lhs, rhs }); | | |
| 8007 | return self.wip.callIntrinsic( | | |
| 8008 | .normal, | | |
| 8009 | .none, | | |
| 8010 | if (scalar_ty.isSignedInt(zcu)) .smin else .umin, | | |
| 8011 | &.{try o.lowerType(pt, inst_ty)}, | | |
| 8012 | &.{ lhs, rhs }, | | |
| 8013 | "", | | |
| 8014 | ); | | |
| 8015 | } | | |
| 8016 | | | |
| 8017 | fn airMax(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8018 | const o = self.ng.object; | | |
| 8019 | const pt = self.ng.pt; | | |
| 8020 | const zcu = pt.zcu; | | |
| 8021 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8022 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8023 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8024 | const inst_ty = self.typeOfIndex(inst); | | |
| 8025 | const scalar_ty = inst_ty.scalarType(zcu); | | |
| 8026 | | | |
| 8027 | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.fmax, .normal, inst_ty, 2, .{ lhs, rhs }); | | |
| 8028 | return self.wip.callIntrinsic( | | |
| 8029 | .normal, | | |
| 8030 | .none, | | |
| 8031 | if (scalar_ty.isSignedInt(zcu)) .smax else .umax, | | |
| 8032 | &.{try o.lowerType(pt, inst_ty)}, | | |
| 8033 | &.{ lhs, rhs }, | | |
| 8034 | "", | | |
| 8035 | ); | | |
| 8036 | } | | |
| 8037 | | | |
| 8038 | fn airSlice(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8039 | const o = self.ng.object; | | |
| 8040 | const pt = self.ng.pt; | | |
| 8041 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | | |
| 8042 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; | | |
| 8043 | const ptr = try self.resolveInst(bin_op.lhs); | | |
| 8044 | const len = try self.resolveInst(bin_op.rhs); | | |
| 8045 | const inst_ty = self.typeOfIndex(inst); | | |
| 8046 | return self.wip.buildAggregate(try o.lowerType(pt, inst_ty), &.{ ptr, len }, ""); | | |
| 8047 | } | | |
| 8048 | | | |
| 8049 | fn airAdd(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) !Builder.Value { | | |
| 8050 | const zcu = self.ng.pt.zcu; | | |
| 8051 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8052 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8053 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8054 | const inst_ty = self.typeOfIndex(inst); | | |
| 8055 | const scalar_ty = inst_ty.scalarType(zcu); | | |
| 8056 | | | |
| 8057 | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.add, fast, inst_ty, 2, .{ lhs, rhs }); | | |
| 8058 | return self.wip.bin(if (scalar_ty.isSignedInt(zcu)) .@"add nsw" else .@"add nuw", lhs, rhs, ""); | | |
| 8059 | } | | |
| 8060 | | | |
| 8061 | fn airSafeArithmetic( | | |
| 8062 | fg: *FuncGen, | | |
| 8063 | inst: Air.Inst.Index, | | |
| 8064 | signed_intrinsic: Builder.Intrinsic, | | |
| 8065 | unsigned_intrinsic: Builder.Intrinsic, | | |
| 8066 | ) !Builder.Value { | | |
| 8067 | const o = fg.ng.object; | | |
| 8068 | const pt = fg.ng.pt; | | |
| 8069 | const zcu = pt.zcu; | | |
| 8070 | | | |
| 8071 | const bin_op = fg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8072 | const lhs = try fg.resolveInst(bin_op.lhs); | | |
| 8073 | const rhs = try fg.resolveInst(bin_op.rhs); | | |
| 8074 | const inst_ty = fg.typeOfIndex(inst); | | |
| 8075 | const scalar_ty = inst_ty.scalarType(zcu); | | |
| 8076 | | | |
| 8077 | const intrinsic = if (scalar_ty.isSignedInt(zcu)) signed_intrinsic else unsigned_intrinsic; | | |
| 8078 | const llvm_inst_ty = try o.lowerType(pt, inst_ty); | | |
| 8079 | const results = | | |
| 8080 | try fg.wip.callIntrinsic(.normal, .none, intrinsic, &.{llvm_inst_ty}, &.{ lhs, rhs }, ""); | | |
| 8081 | | | |
| 8082 | const overflow_bits = try fg.wip.extractValue(results, &.{1}, ""); | | |
| 8083 | const overflow_bits_ty = overflow_bits.typeOfWip(&fg.wip); | | |
| 8084 | const overflow_bit = if (overflow_bits_ty.isVector(&o.builder)) | | |
| 8085 | try fg.wip.callIntrinsic( | | |
| 8086 | .normal, | | |
| 8087 | .none, | | |
| 8088 | .@"vector.reduce.or", | | |
| 8089 | &.{overflow_bits_ty}, | | |
| 8090 | &.{overflow_bits}, | | |
| 8091 | "", | | |
| 8092 | ) | | |
| 8093 | else | | |
| 8094 | overflow_bits; | | |
| 8095 | | | |
| 8096 | const fail_block = try fg.wip.block(1, "OverflowFail"); | | |
| 8097 | const ok_block = try fg.wip.block(1, "OverflowOk"); | | |
| 8098 | _ = try fg.wip.brCond(overflow_bit, fail_block, ok_block, .none); | | |
| 8099 | | | |
| 8100 | fg.wip.cursor = .{ .block = fail_block }; | | |
| 8101 | try fg.buildSimplePanic(.integer_overflow); | | |
| 8102 | | | |
| 8103 | fg.wip.cursor = .{ .block = ok_block }; | | |
| 8104 | return fg.wip.extractValue(results, &.{0}, ""); | | |
| 8105 | } | | |
| 8106 | | | |
| 8107 | fn airAddWrap(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8108 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8109 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8110 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8111 | | | |
| 8112 | return self.wip.bin(.add, lhs, rhs, ""); | | |
| 8113 | } | | |
| 8114 | | | |
| 8115 | fn airAddSat(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8116 | const o = self.ng.object; | | |
| 8117 | const pt = self.ng.pt; | | |
| 8118 | const zcu = pt.zcu; | | |
| 8119 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8120 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8121 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8122 | const inst_ty = self.typeOfIndex(inst); | | |
| 8123 | const scalar_ty = inst_ty.scalarType(zcu); | | |
| 8124 | assert(scalar_ty.zigTypeTag(zcu) == .int); | | |
| 8125 | return self.wip.callIntrinsic( | | |
| 8126 | .normal, | | |
| 8127 | .none, | | |
| 8128 | if (scalar_ty.isSignedInt(zcu)) .@"sadd.sat" else .@"uadd.sat", | | |
| 8129 | &.{try o.lowerType(pt, inst_ty)}, | | |
| 8130 | &.{ lhs, rhs }, | | |
| 8131 | "", | | |
| 8132 | ); | | |
| 8133 | } | | |
| 8134 | | | |
| 8135 | fn airSub(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) !Builder.Value { | | |
| 8136 | const zcu = self.ng.pt.zcu; | | |
| 8137 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8138 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8139 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8140 | const inst_ty = self.typeOfIndex(inst); | | |
| 8141 | const scalar_ty = inst_ty.scalarType(zcu); | | |
| 8142 | | | |
| 8143 | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.sub, fast, inst_ty, 2, .{ lhs, rhs }); | | |
| 8144 | return self.wip.bin(if (scalar_ty.isSignedInt(zcu)) .@"sub nsw" else .@"sub nuw", lhs, rhs, ""); | | |
| 8145 | } | | |
| 8146 | | | |
| 8147 | fn airSubWrap(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8148 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8149 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8150 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8151 | | | |
| 8152 | return self.wip.bin(.sub, lhs, rhs, ""); | | |
| 8153 | } | | |
| 8154 | | | |
| 8155 | fn airSubSat(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8156 | const o = self.ng.object; | | |
| 8157 | const pt = self.ng.pt; | | |
| 8158 | const zcu = pt.zcu; | | |
| 8159 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8160 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8161 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8162 | const inst_ty = self.typeOfIndex(inst); | | |
| 8163 | const scalar_ty = inst_ty.scalarType(zcu); | | |
| 8164 | assert(scalar_ty.zigTypeTag(zcu) == .int); | | |
| 8165 | return self.wip.callIntrinsic( | | |
| 8166 | .normal, | | |
| 8167 | .none, | | |
| 8168 | if (scalar_ty.isSignedInt(zcu)) .@"ssub.sat" else .@"usub.sat", | | |
| 8169 | &.{try o.lowerType(pt, inst_ty)}, | | |
| 8170 | &.{ lhs, rhs }, | | |
| 8171 | "", | | |
| 8172 | ); | | |
| 8173 | } | | |
| 8174 | | | |
| 8175 | fn airMul(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) !Builder.Value { | | |
| 8176 | const zcu = self.ng.pt.zcu; | | |
| 8177 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8178 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8179 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8180 | const inst_ty = self.typeOfIndex(inst); | | |
| 8181 | const scalar_ty = inst_ty.scalarType(zcu); | | |
| 8182 | | | |
| 8183 | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.mul, fast, inst_ty, 2, .{ lhs, rhs }); | | |
| 8184 | return self.wip.bin(if (scalar_ty.isSignedInt(zcu)) .@"mul nsw" else .@"mul nuw", lhs, rhs, ""); | | |
| 8185 | } | | |
| 8186 | | | |
| 8187 | fn airMulWrap(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8188 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8189 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8190 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8191 | | | |
| 8192 | return self.wip.bin(.mul, lhs, rhs, ""); | | |
| 8193 | } | | |
| 8194 | | | |
| 8195 | fn airMulSat(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8196 | const o = self.ng.object; | | |
| 8197 | const pt = self.ng.pt; | | |
| 8198 | const zcu = pt.zcu; | | |
| 8199 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8200 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8201 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8202 | const inst_ty = self.typeOfIndex(inst); | | |
| 8203 | const scalar_ty = inst_ty.scalarType(zcu); | | |
| 8204 | assert(scalar_ty.zigTypeTag(zcu) == .int); | | |
| 8205 | return self.wip.callIntrinsic( | | |
| 8206 | .normal, | | |
| 8207 | .none, | | |
| 8208 | if (scalar_ty.isSignedInt(zcu)) .@"smul.fix.sat" else .@"umul.fix.sat", | | |
| 8209 | &.{try o.lowerType(pt, inst_ty)}, | | |
| 8210 | &.{ lhs, rhs, .@"0" }, | | |
| 8211 | "", | | |
| 8212 | ); | | |
| 8213 | } | | |
| 8214 | | | |
| 8215 | fn airDivFloat(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) !Builder.Value { | | |
| 8216 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8217 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8218 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8219 | const inst_ty = self.typeOfIndex(inst); | | |
| 8220 | | | |
| 8221 | return self.buildFloatOp(.div, fast, inst_ty, 2, .{ lhs, rhs }); | | |
| 8222 | } | | |
| 8223 | | | |
| 8224 | fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) !Builder.Value { | | |
| 8225 | const zcu = self.ng.pt.zcu; | | |
| 8226 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8227 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8228 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8229 | const inst_ty = self.typeOfIndex(inst); | | |
| 8230 | const scalar_ty = inst_ty.scalarType(zcu); | | |
| 8231 | | | |
| 8232 | if (scalar_ty.isRuntimeFloat()) { | | |
| 8233 | const result = try self.buildFloatOp(.div, fast, inst_ty, 2, .{ lhs, rhs }); | | |
| 8234 | return self.buildFloatOp(.trunc, fast, inst_ty, 1, .{result}); | | |
| 8235 | } | | |
| 8236 | return self.wip.bin(if (scalar_ty.isSignedInt(zcu)) .sdiv else .udiv, lhs, rhs, ""); | | |
| 8237 | } | | |
| 8238 | | | |
| 8239 | fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) !Builder.Value { | | |
| 8240 | const o = self.ng.object; | | |
| 8241 | const pt = self.ng.pt; | | |
| 8242 | const zcu = pt.zcu; | | |
| 8243 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8244 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8245 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8246 | const inst_ty = self.typeOfIndex(inst); | | |
| 8247 | const scalar_ty = inst_ty.scalarType(zcu); | | |
| 8248 | | | |
| 8249 | if (scalar_ty.isRuntimeFloat()) { | | |
| 8250 | const result = try self.buildFloatOp(.div, fast, inst_ty, 2, .{ lhs, rhs }); | | |
| 8251 | return self.buildFloatOp(.floor, fast, inst_ty, 1, .{result}); | | |
| 8252 | } | | |
| 8253 | if (scalar_ty.isSignedInt(zcu)) { | | |
| 8254 | const inst_llvm_ty = try o.lowerType(pt, inst_ty); | | |
| 8255 | | | |
| 8256 | const ExpectedContents = [std.math.big.int.calcTwosCompLimbCount(256)]std.math.big.Limb; | | |
| 8257 | var stack align(@max( | | |
| 8258 | @alignOf(std.heap.StackFallbackAllocator(0)), | | |
| 8259 | @alignOf(ExpectedContents), | | |
| 8260 | )) = std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); | | |
| 8261 | const allocator = stack.get(); | | |
| 8262 | | | |
| 8263 | const scalar_bits = inst_llvm_ty.scalarBits(&o.builder); | | |
| 8264 | var smin_big_int: std.math.big.int.Mutable = .{ | | |
| 8265 | .limbs = try allocator.alloc( | | |
| 8266 | std.math.big.Limb, | | |
| 8267 | std.math.big.int.calcTwosCompLimbCount(scalar_bits), | | |
| 8268 | ), | | |
| 8269 | .len = undefined, | | |
| 8270 | .positive = undefined, | | |
| 8271 | }; | | |
| 8272 | defer allocator.free(smin_big_int.limbs); | | |
| 8273 | smin_big_int.setTwosCompIntLimit(.min, .signed, scalar_bits); | | |
| 8274 | const smin = try o.builder.splatValue(inst_llvm_ty, try o.builder.bigIntConst( | | |
| 8275 | inst_llvm_ty.scalarType(&o.builder), | | |
| 8276 | smin_big_int.toConst(), | | |
| 8277 | )); | | |
| 8278 | | | |
| 8279 | const div = try self.wip.bin(.sdiv, lhs, rhs, "divFloor.div"); | | |
| 8280 | const rem = try self.wip.bin(.srem, lhs, rhs, "divFloor.rem"); | | |
| 8281 | const rhs_sign = try self.wip.bin(.@"and", rhs, smin, "divFloor.rhs_sign"); | | |
| 8282 | const rem_xor_rhs_sign = try self.wip.bin(.xor, rem, rhs_sign, "divFloor.rem_xor_rhs_sign"); | | |
| 8283 | const need_correction = try self.wip.icmp(.ugt, rem_xor_rhs_sign, smin, "divFloor.need_correction"); | | |
| 8284 | const correction = try self.wip.cast(.sext, need_correction, inst_llvm_ty, "divFloor.correction"); | | |
| 8285 | return self.wip.bin(.@"add nsw", div, correction, "divFloor"); | | |
| 8286 | } | | |
| 8287 | return self.wip.bin(.udiv, lhs, rhs, ""); | | |
| 8288 | } | | |
| 8289 | | | |
| 8290 | fn airDivExact(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) !Builder.Value { | | |
| 8291 | const zcu = self.ng.pt.zcu; | | |
| 8292 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8293 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8294 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8295 | const inst_ty = self.typeOfIndex(inst); | | |
| 8296 | const scalar_ty = inst_ty.scalarType(zcu); | | |
| 8297 | | | |
| 8298 | if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.div, fast, inst_ty, 2, .{ lhs, rhs }); | | |
| 8299 | return self.wip.bin( | | |
| 8300 | if (scalar_ty.isSignedInt(zcu)) .@"sdiv exact" else .@"udiv exact", | | |
| 8301 | lhs, | | |
| 8302 | rhs, | | |
| 8303 | "", | | |
| 8304 | ); | | |
| 8305 | } | | |
| 8306 | | | |
| 8307 | fn airRem(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) !Builder.Value { | | |
| 8308 | const zcu = self.ng.pt.zcu; | | |
| 8309 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8310 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8311 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8312 | const inst_ty = self.typeOfIndex(inst); | | |
| 8313 | const scalar_ty = inst_ty.scalarType(zcu); | | |
| 8314 | | | |
| 8315 | if (scalar_ty.isRuntimeFloat()) | | |
| 8316 | return self.buildFloatOp(.fmod, fast, inst_ty, 2, .{ lhs, rhs }); | | |
| 8317 | return self.wip.bin(if (scalar_ty.isSignedInt(zcu)) | | |
| 8318 | .srem | | |
| 8319 | else | | |
| 8320 | .urem, lhs, rhs, ""); | | |
| 8321 | } | | |
| 8322 | | | |
| 8323 | fn airMod(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) !Builder.Value { | | |
| 8324 | const o = self.ng.object; | | |
| 8325 | const pt = self.ng.pt; | | |
| 8326 | const zcu = pt.zcu; | | |
| 8327 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8328 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8329 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8330 | const inst_ty = self.typeOfIndex(inst); | | |
| 8331 | const inst_llvm_ty = try o.lowerType(pt, inst_ty); | | |
| 8332 | const scalar_ty = inst_ty.scalarType(zcu); | | |
| 8333 | | | |
| 8334 | if (scalar_ty.isRuntimeFloat()) { | | |
| 8335 | const a = try self.buildFloatOp(.fmod, fast, inst_ty, 2, .{ lhs, rhs }); | | |
| 8336 | const b = try self.buildFloatOp(.add, fast, inst_ty, 2, .{ a, rhs }); | | |
| 8337 | const c = try self.buildFloatOp(.fmod, fast, inst_ty, 2, .{ b, rhs }); | | |
| 8338 | const zero = try o.builder.zeroInitValue(inst_llvm_ty); | | |
| 8339 | const ltz = try self.buildFloatCmp(fast, .lt, inst_ty, .{ lhs, zero }); | | |
| 8340 | return self.wip.select(fast, ltz, c, a, ""); | | |
| 8341 | } | | |
| 8342 | if (scalar_ty.isSignedInt(zcu)) { | | |
| 8343 | const ExpectedContents = [std.math.big.int.calcTwosCompLimbCount(256)]std.math.big.Limb; | | |
| 8344 | var stack align(@max( | | |
| 8345 | @alignOf(std.heap.StackFallbackAllocator(0)), | | |
| 8346 | @alignOf(ExpectedContents), | | |
| 8347 | )) = std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); | | |
| 8348 | const allocator = stack.get(); | | |
| 8349 | | | |
| 8350 | const scalar_bits = inst_llvm_ty.scalarBits(&o.builder); | | |
| 8351 | var smin_big_int: std.math.big.int.Mutable = .{ | | |
| 8352 | .limbs = try allocator.alloc( | | |
| 8353 | std.math.big.Limb, | | |
| 8354 | std.math.big.int.calcTwosCompLimbCount(scalar_bits), | | |
| 8355 | ), | | |
| 8356 | .len = undefined, | | |
| 8357 | .positive = undefined, | | |
| 8358 | }; | | |
| 8359 | defer allocator.free(smin_big_int.limbs); | | |
| 8360 | smin_big_int.setTwosCompIntLimit(.min, .signed, scalar_bits); | | |
| 8361 | const smin = try o.builder.splatValue(inst_llvm_ty, try o.builder.bigIntConst( | | |
| 8362 | inst_llvm_ty.scalarType(&o.builder), | | |
| 8363 | smin_big_int.toConst(), | | |
| 8364 | )); | | |
| 8365 | | | |
| 8366 | const rem = try self.wip.bin(.srem, lhs, rhs, "mod.rem"); | | |
| 8367 | const rhs_sign = try self.wip.bin(.@"and", rhs, smin, "mod.rhs_sign"); | | |
| 8368 | const rem_xor_rhs_sign = try self.wip.bin(.xor, rem, rhs_sign, "mod.rem_xor_rhs_sign"); | | |
| 8369 | const need_correction = try self.wip.icmp(.ugt, rem_xor_rhs_sign, smin, "mod.need_correction"); | | |
| 8370 | const zero = try o.builder.zeroInitValue(inst_llvm_ty); | | |
| 8371 | const correction = try self.wip.select(.normal, need_correction, rhs, zero, "mod.correction"); | | |
| 8372 | return self.wip.bin(.@"add nsw", correction, rem, "mod"); | | |
| 8373 | } | | |
| 8374 | return self.wip.bin(.urem, lhs, rhs, ""); | | |
| 8375 | } | | |
| 8376 | | | |
| 8377 | fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8378 | const o = self.ng.object; | | |
| 8379 | const pt = self.ng.pt; | | |
| 8380 | const zcu = pt.zcu; | | |
| 8381 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | | |
| 8382 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; | | |
| 8383 | const ptr = try self.resolveInst(bin_op.lhs); | | |
| 8384 | const offset = try self.resolveInst(bin_op.rhs); | | |
| 8385 | const ptr_ty = self.typeOf(bin_op.lhs); | | |
| 8386 | const llvm_elem_ty = try o.lowerType(pt, ptr_ty.childType(zcu)); | | |
| 8387 | switch (ptr_ty.ptrSize(zcu)) { | | |
| 8388 | // It's a pointer to an array, so according to LLVM we need an extra GEP index. | | |
| 8389 | .one => return self.wip.gep(.inbounds, llvm_elem_ty, ptr, &.{ | | |
| 8390 | try o.builder.intValue(try o.lowerType(pt, Type.usize), 0), offset, | | |
| 8391 | }, ""), | | |
| 8392 | .c, .many => return self.wip.gep(.inbounds, llvm_elem_ty, ptr, &.{offset}, ""), | | |
| 8393 | .slice => { | | |
| 8394 | const base = try self.wip.extractValue(ptr, &.{0}, ""); | | |
| 8395 | return self.wip.gep(.inbounds, llvm_elem_ty, base, &.{offset}, ""); | | |
| 8396 | }, | | |
| 8397 | } | | |
| 8398 | } | | |
| 8399 | | | |
| 8400 | fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8401 | const o = self.ng.object; | | |
| 8402 | const pt = self.ng.pt; | | |
| 8403 | const zcu = pt.zcu; | | |
| 8404 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | | |
| 8405 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; | | |
| 8406 | const ptr = try self.resolveInst(bin_op.lhs); | | |
| 8407 | const offset = try self.resolveInst(bin_op.rhs); | | |
| 8408 | const negative_offset = try self.wip.neg(offset, ""); | | |
| 8409 | const ptr_ty = self.typeOf(bin_op.lhs); | | |
| 8410 | const llvm_elem_ty = try o.lowerType(pt, ptr_ty.childType(zcu)); | | |
| 8411 | switch (ptr_ty.ptrSize(zcu)) { | | |
| 8412 | // It's a pointer to an array, so according to LLVM we need an extra GEP index. | | |
| 8413 | .one => return self.wip.gep(.inbounds, llvm_elem_ty, ptr, &.{ | | |
| 8414 | try o.builder.intValue(try o.lowerType(pt, Type.usize), 0), negative_offset, | | |
| 8415 | }, ""), | | |
| 8416 | .c, .many => return self.wip.gep(.inbounds, llvm_elem_ty, ptr, &.{negative_offset}, ""), | | |
| 8417 | .slice => { | | |
| 8418 | const base = try self.wip.extractValue(ptr, &.{0}, ""); | | |
| 8419 | return self.wip.gep(.inbounds, llvm_elem_ty, base, &.{negative_offset}, ""); | | |
| 8420 | }, | | |
| 8421 | } | | |
| 8422 | } | | |
| 8423 | | | |
| 8424 | fn airOverflow( | | |
| 8425 | self: *FuncGen, | | |
| 8426 | inst: Air.Inst.Index, | | |
| 8427 | signed_intrinsic: Builder.Intrinsic, | | |
| 8428 | unsigned_intrinsic: Builder.Intrinsic, | | |
| 8429 | ) !Builder.Value { | | |
| 8430 | const o = self.ng.object; | | |
| 8431 | const pt = self.ng.pt; | | |
| 8432 | const zcu = pt.zcu; | | |
| 8433 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | | |
| 8434 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | | |
| 8435 | | | |
| 8436 | const lhs = try self.resolveInst(extra.lhs); | | |
| 8437 | const rhs = try self.resolveInst(extra.rhs); | | |
| 8438 | | | |
| 8439 | const lhs_ty = self.typeOf(extra.lhs); | | |
| 8440 | const scalar_ty = lhs_ty.scalarType(zcu); | | |
| 8441 | const inst_ty = self.typeOfIndex(inst); | | |
| 8442 | | | |
| 8443 | const intrinsic = if (scalar_ty.isSignedInt(zcu)) signed_intrinsic else unsigned_intrinsic; | | |
| 8444 | const llvm_inst_ty = try o.lowerType(pt, inst_ty); | | |
| 8445 | const llvm_lhs_ty = try o.lowerType(pt, lhs_ty); | | |
| 8446 | const results = | | |
| 8447 | try self.wip.callIntrinsic(.normal, .none, intrinsic, &.{llvm_lhs_ty}, &.{ lhs, rhs }, ""); | | |
| 8448 | | | |
| 8449 | const result_val = try self.wip.extractValue(results, &.{0}, ""); | | |
| 8450 | const overflow_bit = try self.wip.extractValue(results, &.{1}, ""); | | |
| 8451 | | | |
| 8452 | const result_index = o.llvmFieldIndex(inst_ty, 0).?; | | |
| 8453 | const overflow_index = o.llvmFieldIndex(inst_ty, 1).?; | | |
| 8454 | | | |
| 8455 | if (isByRef(inst_ty, zcu)) { | | |
| 8456 | const result_alignment = inst_ty.abiAlignment(zcu).toLlvm(); | | |
| 8457 | const alloca_inst = try self.buildAlloca(llvm_inst_ty, result_alignment); | | |
| 8458 | { | | |
| 8459 | const field_ptr = try self.wip.gepStruct(llvm_inst_ty, alloca_inst, result_index, ""); | | |
| 8460 | _ = try self.wip.store(.normal, result_val, field_ptr, result_alignment); | | |
| 8461 | } | | |
| 8462 | { | | |
| 8463 | const field_ptr = try self.wip.gepStruct(llvm_inst_ty, alloca_inst, overflow_index, ""); | | |
| 8464 | _ = try self.wip.store(.normal, overflow_bit, field_ptr, comptime .fromByteUnits(1)); | | |
| 8465 | } | | |
| 8466 | | | |
| 8467 | return alloca_inst; | | |
| 8468 | } | | |
| 8469 | | | |
| 8470 | var fields: [2]Builder.Value = undefined; | | |
| 8471 | fields[result_index] = result_val; | | |
| 8472 | fields[overflow_index] = overflow_bit; | | |
| 8473 | return self.wip.buildAggregate(llvm_inst_ty, &fields, ""); | | |
| 8474 | } | | |
| 8475 | | | |
| 8476 | fn buildElementwiseCall( | | |
| 8477 | self: *FuncGen, | | |
| 8478 | llvm_fn: Builder.Function.Index, | | |
| 8479 | args_vectors: []const Builder.Value, | | |
| 8480 | result_vector: Builder.Value, | | |
| 8481 | vector_len: usize, | | |
| 8482 | ) !Builder.Value { | | |
| 8483 | const o = self.ng.object; | | |
| 8484 | assert(args_vectors.len <= 3); | | |
| 8485 | | | |
| 8486 | var i: usize = 0; | | |
| 8487 | var result = result_vector; | | |
| 8488 | while (i < vector_len) : (i += 1) { | | |
| 8489 | const index_i32 = try o.builder.intValue(.i32, i); | | |
| 8490 | | | |
| 8491 | var args: [3]Builder.Value = undefined; | | |
| 8492 | for (args[0..args_vectors.len], args_vectors) |*arg_elem, arg_vector| { | | |
| 8493 | arg_elem.* = try self.wip.extractElement(arg_vector, index_i32, ""); | | |
| 8494 | } | | |
| 8495 | const result_elem = try self.wip.call( | | |
| 8496 | .normal, | | |
| 8497 | .ccc, | | |
| 8498 | .none, | | |
| 8499 | llvm_fn.typeOf(&o.builder), | | |
| 8500 | llvm_fn.toValue(&o.builder), | | |
| 8501 | args[0..args_vectors.len], | | |
| 8502 | "", | | |
| 8503 | ); | | |
| 8504 | result = try self.wip.insertElement(result, result_elem, index_i32, ""); | | |
| 8505 | } | | |
| 8506 | return result; | | |
| 8507 | } | | |
| 8508 | | | |
| 8509 | fn getLibcFunction( | | |
| 8510 | self: *FuncGen, | | |
| 8511 | fn_name: Builder.StrtabString, | | |
| 8512 | param_types: []const Builder.Type, | | |
| 8513 | return_type: Builder.Type, | | |
| 8514 | ) Allocator.Error!Builder.Function.Index { | | |
| 8515 | const o = self.ng.object; | | |
| 8516 | if (o.builder.getGlobal(fn_name)) |global| return switch (global.ptrConst(&o.builder).kind) { | | |
| 8517 | .alias => |alias| alias.getAliasee(&o.builder).ptrConst(&o.builder).kind.function, | | |
| 8518 | .function => |function| function, | | |
| 8519 | .variable, .replaced => unreachable, | | |
| 8520 | }; | | |
| 8521 | return o.builder.addFunction( | | |
| 8522 | try o.builder.fnType(return_type, param_types, .normal), | | |
| 8523 | fn_name, | | |
| 8524 | toLlvmAddressSpace(.generic, self.ng.pt.zcu.getTarget()), | | |
| 8525 | ); | | |
| 8526 | } | | |
| 8527 | | | |
| 8528 | /// Creates a floating point comparison by lowering to the appropriate | | |
| 8529 | /// hardware instruction or softfloat routine for the target | | |
| 8530 | fn buildFloatCmp( | | |
| 8531 | self: *FuncGen, | | |
| 8532 | fast: Builder.FastMathKind, | | |
| 8533 | pred: math.CompareOperator, | | |
| 8534 | ty: Type, | | |
| 8535 | params: [2]Builder.Value, | | |
| 8536 | ) !Builder.Value { | | |
| 8537 | const o = self.ng.object; | | |
| 8538 | const pt = self.ng.pt; | | |
| 8539 | const zcu = pt.zcu; | | |
| 8540 | const target = zcu.getTarget(); | | |
| 8541 | const scalar_ty = ty.scalarType(zcu); | | |
| 8542 | const scalar_llvm_ty = try o.lowerType(pt, scalar_ty); | | |
| 8543 | | | |
| 8544 | if (intrinsicsAllowed(scalar_ty, target)) { | | |
| 8545 | const cond: Builder.FloatCondition = switch (pred) { | | |
| 8546 | .eq => .oeq, | | |
| 8547 | .neq => .une, | | |
| 8548 | .lt => .olt, | | |
| 8549 | .lte => .ole, | | |
| 8550 | .gt => .ogt, | | |
| 8551 | .gte => .oge, | | |
| 8552 | }; | | |
| 8553 | return self.wip.fcmp(fast, cond, params[0], params[1], ""); | | |
| 8554 | } | | |
| 8555 | | | |
| 8556 | const float_bits = scalar_ty.floatBits(target); | | |
| 8557 | const compiler_rt_float_abbrev = compilerRtFloatAbbrev(float_bits); | | |
| 8558 | const fn_base_name = switch (pred) { | | |
| 8559 | .neq => "ne", | | |
| 8560 | .eq => "eq", | | |
| 8561 | .lt => "lt", | | |
| 8562 | .lte => "le", | | |
| 8563 | .gt => "gt", | | |
| 8564 | .gte => "ge", | | |
| 8565 | }; | | |
| 8566 | const fn_name = try o.builder.strtabStringFmt("__{s}{s}f2", .{ fn_base_name, compiler_rt_float_abbrev }); | | |
| 8567 | | | |
| 8568 | const libc_fn = try self.getLibcFunction(fn_name, &.{ scalar_llvm_ty, scalar_llvm_ty }, .i32); | | |
| 8569 | | | |
| 8570 | const int_cond: Builder.IntegerCondition = switch (pred) { | | |
| 8571 | .eq => .eq, | | |
| 8572 | .neq => .ne, | | |
| 8573 | .lt => .slt, | | |
| 8574 | .lte => .sle, | | |
| 8575 | .gt => .sgt, | | |
| 8576 | .gte => .sge, | | |
| 8577 | }; | | |
| 8578 | | | |
| 8579 | if (ty.zigTypeTag(zcu) == .vector) { | | |
| 8580 | const vec_len = ty.vectorLen(zcu); | | |
| 8581 | const vector_result_ty = try o.builder.vectorType(.normal, vec_len, .i32); | | |
| 8582 | | | |
| 8583 | const init = try o.builder.poisonValue(vector_result_ty); | | |
| 8584 | const result = try self.buildElementwiseCall(libc_fn, &params, init, vec_len); | | |
| 8585 | | | |
| 8586 | const zero_vector = try o.builder.splatValue(vector_result_ty, .@"0"); | | |
| 8587 | return self.wip.icmp(int_cond, result, zero_vector, ""); | | |
| 8588 | } | | |
| 8589 | | | |
| 8590 | const result = try self.wip.call( | | |
| 8591 | .normal, | | |
| 8592 | .ccc, | | |
| 8593 | .none, | | |
| 8594 | libc_fn.typeOf(&o.builder), | | |
| 8595 | libc_fn.toValue(&o.builder), | | |
| 8596 | &params, | | |
| 8597 | "", | | |
| 8598 | ); | | |
| 8599 | return self.wip.icmp(int_cond, result, .@"0", ""); | | |
| 8600 | } | | |
| 8601 | | | |
| 8602 | const FloatOp = enum { | | |
| 8603 | add, | | |
| 8604 | ceil, | | |
| 8605 | cos, | | |
| 8606 | div, | | |
| 8607 | exp, | | |
| 8608 | exp2, | | |
| 8609 | fabs, | | |
| 8610 | floor, | | |
| 8611 | fma, | | |
| 8612 | fmax, | | |
| 8613 | fmin, | | |
| 8614 | fmod, | | |
| 8615 | log, | | |
| 8616 | log10, | | |
| 8617 | log2, | | |
| 8618 | mul, | | |
| 8619 | neg, | | |
| 8620 | round, | | |
| 8621 | sin, | | |
| 8622 | sqrt, | | |
| 8623 | sub, | | |
| 8624 | tan, | | |
| 8625 | trunc, | | |
| 8626 | }; | | |
| 8627 | | | |
| 8628 | const FloatOpStrat = union(enum) { | | |
| 8629 | intrinsic: []const u8, | | |
| 8630 | libc: Builder.String, | | |
| 8631 | }; | | |
| 8632 | | | |
| 8633 | /// Creates a floating point operation (add, sub, fma, sqrt, exp, etc.) | | |
| 8634 | /// by lowering to the appropriate hardware instruction or softfloat | | |
| 8635 | /// routine for the target | | |
| 8636 | fn buildFloatOp( | | |
| 8637 | self: *FuncGen, | | |
| 8638 | comptime op: FloatOp, | | |
| 8639 | fast: Builder.FastMathKind, | | |
| 8640 | ty: Type, | | |
| 8641 | comptime params_len: usize, | | |
| 8642 | params: [params_len]Builder.Value, | | |
| 8643 | ) !Builder.Value { | | |
| 8644 | const o = self.ng.object; | | |
| 8645 | const pt = self.ng.pt; | | |
| 8646 | const zcu = pt.zcu; | | |
| 8647 | const target = zcu.getTarget(); | | |
| 8648 | const scalar_ty = ty.scalarType(zcu); | | |
| 8649 | const llvm_ty = try o.lowerType(pt, ty); | | |
| 8650 | | | |
| 8651 | if (op != .tan and intrinsicsAllowed(scalar_ty, target)) switch (op) { | | |
| 8652 | // Some operations are dedicated LLVM instructions, not available as intrinsics | | |
| 8653 | .neg => return self.wip.un(.fneg, params[0], ""), | | |
| 8654 | .add, .sub, .mul, .div, .fmod => return self.wip.bin(switch (fast) { | | |
| 8655 | .normal => switch (op) { | | |
| 8656 | .add => .fadd, | | |
| 8657 | .sub => .fsub, | | |
| 8658 | .mul => .fmul, | | |
| 8659 | .div => .fdiv, | | |
| 8660 | .fmod => .frem, | | |
| 8661 | else => unreachable, | | |
| 8662 | }, | | |
| 8663 | .fast => switch (op) { | | |
| 8664 | .add => .@"fadd fast", | | |
| 8665 | .sub => .@"fsub fast", | | |
| 8666 | .mul => .@"fmul fast", | | |
| 8667 | .div => .@"fdiv fast", | | |
| 8668 | .fmod => .@"frem fast", | | |
| 8669 | else => unreachable, | | |
| 8670 | }, | | |
| 8671 | }, params[0], params[1], ""), | | |
| 8672 | .fmax, | | |
| 8673 | .fmin, | | |
| 8674 | .ceil, | | |
| 8675 | .cos, | | |
| 8676 | .exp, | | |
| 8677 | .exp2, | | |
| 8678 | .fabs, | | |
| 8679 | .floor, | | |
| 8680 | .log, | | |
| 8681 | .log10, | | |
| 8682 | .log2, | | |
| 8683 | .round, | | |
| 8684 | .sin, | | |
| 8685 | .sqrt, | | |
| 8686 | .trunc, | | |
| 8687 | .fma, | | |
| 8688 | => return self.wip.callIntrinsic(fast, .none, switch (op) { | | |
| 8689 | .fmax => .maxnum, | | |
| 8690 | .fmin => .minnum, | | |
| 8691 | .ceil => .ceil, | | |
| 8692 | .cos => .cos, | | |
| 8693 | .exp => .exp, | | |
| 8694 | .exp2 => .exp2, | | |
| 8695 | .fabs => .fabs, | | |
| 8696 | .floor => .floor, | | |
| 8697 | .log => .log, | | |
| 8698 | .log10 => .log10, | | |
| 8699 | .log2 => .log2, | | |
| 8700 | .round => .round, | | |
| 8701 | .sin => .sin, | | |
| 8702 | .sqrt => .sqrt, | | |
| 8703 | .trunc => .trunc, | | |
| 8704 | .fma => .fma, | | |
| 8705 | else => unreachable, | | |
| 8706 | }, &.{llvm_ty}, &params, ""), | | |
| 8707 | .tan => unreachable, | | |
| 8708 | }; | | |
| 8709 | | | |
| 8710 | const float_bits = scalar_ty.floatBits(target); | | |
| 8711 | const fn_name = switch (op) { | | |
| 8712 | .neg => { | | |
| 8713 | // In this case we can generate a softfloat negation by XORing the | | |
| 8714 | // bits with a constant. | | |
| 8715 | const int_ty = try o.builder.intType(@intCast(float_bits)); | | |
| 8716 | const cast_ty = try llvm_ty.changeScalar(int_ty, &o.builder); | | |
| 8717 | const sign_mask = try o.builder.splatValue( | | |
| 8718 | cast_ty, | | |
| 8719 | try o.builder.intConst(int_ty, @as(u128, 1) << @intCast(float_bits - 1)), | | |
| 8720 | ); | | |
| 8721 | const bitcasted_operand = try self.wip.cast(.bitcast, params[0], cast_ty, ""); | | |
| 8722 | const result = try self.wip.bin(.xor, bitcasted_operand, sign_mask, ""); | | |
| 8723 | return self.wip.cast(.bitcast, result, llvm_ty, ""); | | |
| 8724 | }, | | |
| 8725 | .add, .sub, .div, .mul => try o.builder.strtabStringFmt("__{s}{s}f3", .{ | | |
| 8726 | @tagName(op), compilerRtFloatAbbrev(float_bits), | | |
| 8727 | }), | | |
| 8728 | .ceil, | | |
| 8729 | .cos, | | |
| 8730 | .exp, | | |
| 8731 | .exp2, | | |
| 8732 | .fabs, | | |
| 8733 | .floor, | | |
| 8734 | .fma, | | |
| 8735 | .fmax, | | |
| 8736 | .fmin, | | |
| 8737 | .fmod, | | |
| 8738 | .log, | | |
| 8739 | .log10, | | |
| 8740 | .log2, | | |
| 8741 | .round, | | |
| 8742 | .sin, | | |
| 8743 | .sqrt, | | |
| 8744 | .tan, | | |
| 8745 | .trunc, | | |
| 8746 | => try o.builder.strtabStringFmt("{s}{s}{s}", .{ | | |
| 8747 | libcFloatPrefix(float_bits), @tagName(op), libcFloatSuffix(float_bits), | | |
| 8748 | }), | | |
| 8749 | }; | | |
| 8750 | | | |
| 8751 | const scalar_llvm_ty = llvm_ty.scalarType(&o.builder); | | |
| 8752 | const libc_fn = try self.getLibcFunction( | | |
| 8753 | fn_name, | | |
| 8754 | ([1]Builder.Type{scalar_llvm_ty} ** 3)[0..params.len], | | |
| 8755 | scalar_llvm_ty, | | |
| 8756 | ); | | |
| 8757 | if (ty.zigTypeTag(zcu) == .vector) { | | |
| 8758 | const result = try o.builder.poisonValue(llvm_ty); | | |
| 8759 | return self.buildElementwiseCall(libc_fn, &params, result, ty.vectorLen(zcu)); | | |
| 8760 | } | | |
| 8761 | | | |
| 8762 | return self.wip.call( | | |
| 8763 | fast.toCallKind(), | | |
| 8764 | .ccc, | | |
| 8765 | .none, | | |
| 8766 | libc_fn.typeOf(&o.builder), | | |
| 8767 | libc_fn.toValue(&o.builder), | | |
| 8768 | &params, | | |
| 8769 | "", | | |
| 8770 | ); | | |
| 8771 | } | | |
| 8772 | | | |
| 8773 | fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8774 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | | |
| 8775 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; | | |
| 8776 | | | |
| 8777 | const mulend1 = try self.resolveInst(extra.lhs); | | |
| 8778 | const mulend2 = try self.resolveInst(extra.rhs); | | |
| 8779 | const addend = try self.resolveInst(pl_op.operand); | | |
| 8780 | | | |
| 8781 | const ty = self.typeOfIndex(inst); | | |
| 8782 | return self.buildFloatOp(.fma, .normal, ty, 3, .{ mulend1, mulend2, addend }); | | |
| 8783 | } | | |
| 8784 | | | |
| 8785 | fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8786 | const o = self.ng.object; | | |
| 8787 | const pt = self.ng.pt; | | |
| 8788 | const zcu = pt.zcu; | | |
| 8789 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | | |
| 8790 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | | |
| 8791 | | | |
| 8792 | const lhs = try self.resolveInst(extra.lhs); | | |
| 8793 | const rhs = try self.resolveInst(extra.rhs); | | |
| 8794 | | | |
| 8795 | const lhs_ty = self.typeOf(extra.lhs); | | |
| 8796 | if (lhs_ty.isVector(zcu) and !self.typeOf(extra.rhs).isVector(zcu)) | | |
| 8797 | return self.ng.todo("implement vector shifts with scalar rhs", .{}); | | |
| 8798 | const lhs_scalar_ty = lhs_ty.scalarType(zcu); | | |
| 8799 | | | |
| 8800 | const dest_ty = self.typeOfIndex(inst); | | |
| 8801 | const llvm_dest_ty = try o.lowerType(pt, dest_ty); | | |
| 8802 | | | |
| 8803 | const casted_rhs = try self.wip.conv(.unsigned, rhs, try o.lowerType(pt, lhs_ty), ""); | | |
| 8804 | | | |
| 8805 | const result = try self.wip.bin(.shl, lhs, casted_rhs, ""); | | |
| 8806 | const reconstructed = try self.wip.bin(if (lhs_scalar_ty.isSignedInt(zcu)) | | |
| 8807 | .ashr | | |
| 8808 | else | | |
| 8809 | .lshr, result, casted_rhs, ""); | | |
| 8810 | | | |
| 8811 | const overflow_bit = try self.wip.icmp(.ne, lhs, reconstructed, ""); | | |
| 8812 | | | |
| 8813 | const result_index = o.llvmFieldIndex(dest_ty, 0).?; | | |
| 8814 | const overflow_index = o.llvmFieldIndex(dest_ty, 1).?; | | |
| 8815 | | | |
| 8816 | if (isByRef(dest_ty, zcu)) { | | |
| 8817 | const result_alignment = dest_ty.abiAlignment(zcu).toLlvm(); | | |
| 8818 | const alloca_inst = try self.buildAlloca(llvm_dest_ty, result_alignment); | | |
| 8819 | { | | |
| 8820 | const field_ptr = try self.wip.gepStruct(llvm_dest_ty, alloca_inst, result_index, ""); | | |
| 8821 | _ = try self.wip.store(.normal, result, field_ptr, result_alignment); | | |
| 8822 | } | | |
| 8823 | { | | |
| 8824 | const field_ptr = try self.wip.gepStruct(llvm_dest_ty, alloca_inst, overflow_index, ""); | | |
| 8825 | _ = try self.wip.store(.normal, overflow_bit, field_ptr, comptime .fromByteUnits(1)); | | |
| 8826 | } | | |
| 8827 | return alloca_inst; | | |
| 8828 | } | | |
| 8829 | | | |
| 8830 | var fields: [2]Builder.Value = undefined; | | |
| 8831 | fields[result_index] = result; | | |
| 8832 | fields[overflow_index] = overflow_bit; | | |
| 8833 | return self.wip.buildAggregate(llvm_dest_ty, &fields, ""); | | |
| 8834 | } | | |
| 8835 | | | |
| 8836 | fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8837 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8838 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8839 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8840 | return self.wip.bin(.@"and", lhs, rhs, ""); | | |
| 8841 | } | | |
| 8842 | | | |
| 8843 | fn airOr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8844 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8845 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8846 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8847 | return self.wip.bin(.@"or", lhs, rhs, ""); | | |
| 8848 | } | | |
| 8849 | | | |
| 8850 | fn airXor(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8851 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8852 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8853 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8854 | return self.wip.bin(.xor, lhs, rhs, ""); | | |
| 8855 | } | | |
| 8856 | | | |
| 8857 | fn airShlExact(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8858 | const o = self.ng.object; | | |
| 8859 | const pt = self.ng.pt; | | |
| 8860 | const zcu = pt.zcu; | | |
| 8861 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8862 | | | |
| 8863 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8864 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8865 | | | |
| 8866 | const lhs_ty = self.typeOf(bin_op.lhs); | | |
| 8867 | if (lhs_ty.isVector(zcu) and !self.typeOf(bin_op.rhs).isVector(zcu)) | | |
| 8868 | return self.ng.todo("implement vector shifts with scalar rhs", .{}); | | |
| 8869 | const lhs_scalar_ty = lhs_ty.scalarType(zcu); | | |
| 8870 | | | |
| 8871 | const casted_rhs = try self.wip.conv(.unsigned, rhs, try o.lowerType(pt, lhs_ty), ""); | | |
| 8872 | return self.wip.bin(if (lhs_scalar_ty.isSignedInt(zcu)) | | |
| 8873 | .@"shl nsw" | | |
| 8874 | else | | |
| 8875 | .@"shl nuw", lhs, casted_rhs, ""); | | |
| 8876 | } | | |
| 8877 | | | |
| 8878 | fn airShl(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8879 | const o = self.ng.object; | | |
| 8880 | const pt = self.ng.pt; | | |
| 8881 | const zcu = pt.zcu; | | |
| 8882 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8883 | | | |
| 8884 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8885 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8886 | | | |
| 8887 | const lhs_ty = self.typeOf(bin_op.lhs); | | |
| 8888 | if (lhs_ty.isVector(zcu) and !self.typeOf(bin_op.rhs).isVector(zcu)) | | |
| 8889 | return self.ng.todo("implement vector shifts with scalar rhs", .{}); | | |
| 8890 | | | |
| 8891 | const casted_rhs = try self.wip.conv(.unsigned, rhs, try o.lowerType(pt, lhs_ty), ""); | | |
| 8892 | return self.wip.bin(.shl, lhs, casted_rhs, ""); | | |
| 8893 | } | | |
| 8894 | | | |
| 8895 | fn airShlSat(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8896 | const o = self.ng.object; | | |
| 8897 | const pt = self.ng.pt; | | |
| 8898 | const zcu = pt.zcu; | | |
| 8899 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8900 | | | |
| 8901 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8902 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8903 | | | |
| 8904 | const lhs_ty = self.typeOf(bin_op.lhs); | | |
| 8905 | const lhs_info = lhs_ty.intInfo(zcu); | | |
| 8906 | const llvm_lhs_ty = try o.lowerType(pt, lhs_ty); | | |
| 8907 | const llvm_lhs_scalar_ty = llvm_lhs_ty.scalarType(&o.builder); | | |
| 8908 | | | |
| 8909 | const rhs_ty = self.typeOf(bin_op.rhs); | | |
| 8910 | if (lhs_ty.isVector(zcu) and !rhs_ty.isVector(zcu)) | | |
| 8911 | return self.ng.todo("implement vector shifts with scalar rhs", .{}); | | |
| 8912 | const rhs_info = rhs_ty.intInfo(zcu); | | |
| 8913 | assert(rhs_info.signedness == .unsigned); | | |
| 8914 | const llvm_rhs_ty = try o.lowerType(pt, rhs_ty); | | |
| 8915 | const llvm_rhs_scalar_ty = llvm_rhs_ty.scalarType(&o.builder); | | |
| 8916 | | | |
| 8917 | const result = try self.wip.callIntrinsic( | | |
| 8918 | .normal, | | |
| 8919 | .none, | | |
| 8920 | switch (lhs_info.signedness) { | | |
| 8921 | .signed => .@"sshl.sat", | | |
| 8922 | .unsigned => .@"ushl.sat", | | |
| 8923 | }, | | |
| 8924 | &.{llvm_lhs_ty}, | | |
| 8925 | &.{ lhs, try self.wip.conv(.unsigned, rhs, llvm_lhs_ty, "") }, | | |
| 8926 | "", | | |
| 8927 | ); | | |
| 8928 | | | |
| 8929 | // LLVM langref says "If b is (statically or dynamically) equal to or | | |
| 8930 | // larger than the integer bit width of the arguments, the result is a | | |
| 8931 | // poison value." | | |
| 8932 | // However Zig semantics says that saturating shift left can never produce | | |
| 8933 | // undefined; instead it saturates. | | |
| 8934 | if (rhs_info.bits <= math.log2_int(u16, lhs_info.bits)) return result; | | |
| 8935 | const bits = try o.builder.splatValue( | | |
| 8936 | llvm_rhs_ty, | | |
| 8937 | try o.builder.intConst(llvm_rhs_scalar_ty, lhs_info.bits), | | |
| 8938 | ); | | |
| 8939 | const in_range = try self.wip.icmp(.ult, rhs, bits, ""); | | |
| 8940 | const lhs_sat = lhs_sat: switch (lhs_info.signedness) { | | |
| 8941 | .signed => { | | |
| 8942 | const zero = try o.builder.splatValue( | | |
| 8943 | llvm_lhs_ty, | | |
| 8944 | try o.builder.intConst(llvm_lhs_scalar_ty, 0), | | |
| 8945 | ); | | |
| 8946 | const smin = try o.builder.splatValue( | | |
| 8947 | llvm_lhs_ty, | | |
| 8948 | try minIntConst(&o.builder, lhs_ty, llvm_lhs_ty, zcu), | | |
| 8949 | ); | | |
| 8950 | const smax = try o.builder.splatValue( | | |
| 8951 | llvm_lhs_ty, | | |
| 8952 | try maxIntConst(&o.builder, lhs_ty, llvm_lhs_ty, zcu), | | |
| 8953 | ); | | |
| 8954 | const lhs_lt_zero = try self.wip.icmp(.slt, lhs, zero, ""); | | |
| 8955 | const slimit = try self.wip.select(.normal, lhs_lt_zero, smin, smax, ""); | | |
| 8956 | const lhs_eq_zero = try self.wip.icmp(.eq, lhs, zero, ""); | | |
| 8957 | break :lhs_sat try self.wip.select(.normal, lhs_eq_zero, zero, slimit, ""); | | |
| 8958 | }, | | |
| 8959 | .unsigned => { | | |
| 8960 | const zero = try o.builder.splatValue( | | |
| 8961 | llvm_lhs_ty, | | |
| 8962 | try o.builder.intConst(llvm_lhs_scalar_ty, 0), | | |
| 8963 | ); | | |
| 8964 | const umax = try o.builder.splatValue( | | |
| 8965 | llvm_lhs_ty, | | |
| 8966 | try o.builder.intConst(llvm_lhs_scalar_ty, -1), | | |
| 8967 | ); | | |
| 8968 | const lhs_eq_zero = try self.wip.icmp(.eq, lhs, zero, ""); | | |
| 8969 | break :lhs_sat try self.wip.select(.normal, lhs_eq_zero, zero, umax, ""); | | |
| 8970 | }, | | |
| 8971 | }; | | |
| 8972 | return self.wip.select(.normal, in_range, result, lhs_sat, ""); | | |
| 8973 | } | | |
| 8974 | | | |
| 8975 | fn airShr(self: *FuncGen, inst: Air.Inst.Index, is_exact: bool) !Builder.Value { | | |
| 8976 | const o = self.ng.object; | | |
| 8977 | const pt = self.ng.pt; | | |
| 8978 | const zcu = pt.zcu; | | |
| 8979 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 8980 | | | |
| 8981 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 8982 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 8983 | | | |
| 8984 | const lhs_ty = self.typeOf(bin_op.lhs); | | |
| 8985 | if (lhs_ty.isVector(zcu) and !self.typeOf(bin_op.rhs).isVector(zcu)) | | |
| 8986 | return self.ng.todo("implement vector shifts with scalar rhs", .{}); | | |
| 8987 | const lhs_scalar_ty = lhs_ty.scalarType(zcu); | | |
| 8988 | | | |
| 8989 | const casted_rhs = try self.wip.conv(.unsigned, rhs, try o.lowerType(pt, lhs_ty), ""); | | |
| 8990 | const is_signed_int = lhs_scalar_ty.isSignedInt(zcu); | | |
| 8991 | | | |
| 8992 | return self.wip.bin(if (is_exact) | | |
| 8993 | if (is_signed_int) .@"ashr exact" else .@"lshr exact" | | |
| 8994 | else if (is_signed_int) .ashr else .lshr, lhs, casted_rhs, ""); | | |
| 8995 | } | | |
| 8996 | | | |
| 8997 | fn airAbs(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 8998 | const o = self.ng.object; | | |
| 8999 | const pt = self.ng.pt; | | |
| 9000 | const zcu = pt.zcu; | | |
| 9001 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 9002 | const operand = try self.resolveInst(ty_op.operand); | | |
| 9003 | const operand_ty = self.typeOf(ty_op.operand); | | |
| 9004 | const scalar_ty = operand_ty.scalarType(zcu); | | |
| 9005 | | | |
| 9006 | switch (scalar_ty.zigTypeTag(zcu)) { | | |
| 9007 | .int => return self.wip.callIntrinsic( | | |
| 9008 | .normal, | | |
| 9009 | .none, | | |
| 9010 | .abs, | | |
| 9011 | &.{try o.lowerType(pt, operand_ty)}, | | |
| 9012 | &.{ operand, try o.builder.intValue(.i1, 0) }, | | |
| 9013 | "", | | |
| 9014 | ), | | |
| 9015 | .float => return self.buildFloatOp(.fabs, .normal, operand_ty, 1, .{operand}), | | |
| 9016 | else => unreachable, | | |
| 9017 | } | | |
| 9018 | } | | |
| 9019 | | | |
| 9020 | fn airIntCast(fg: *FuncGen, inst: Air.Inst.Index, safety: bool) !Builder.Value { | | |
| 9021 | const o = fg.ng.object; | | |
| 9022 | const pt = fg.ng.pt; | | |
| 9023 | const zcu = pt.zcu; | | |
| 9024 | const ty_op = fg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 9025 | const dest_ty = fg.typeOfIndex(inst); | | |
| 9026 | const dest_llvm_ty = try o.lowerType(pt, dest_ty); | | |
| 9027 | const operand = try fg.resolveInst(ty_op.operand); | | |
| 9028 | const operand_ty = fg.typeOf(ty_op.operand); | | |
| 9029 | const operand_info = operand_ty.intInfo(zcu); | | |
| 9030 | | | |
| 9031 | const dest_is_enum = dest_ty.zigTypeTag(zcu) == .@"enum"; | | |
| 9032 | | | |
| 9033 | bounds_check: { | | |
| 9034 | const dest_scalar = dest_ty.scalarType(zcu); | | |
| 9035 | const operand_scalar = operand_ty.scalarType(zcu); | | |
| 9036 | | | |
| 9037 | const dest_info = dest_ty.intInfo(zcu); | | |
| 9038 | | | |
| 9039 | const have_min_check, const have_max_check = c: { | | |
| 9040 | const dest_pos_bits = dest_info.bits - @intFromBool(dest_info.signedness == .signed); | | |
| 9041 | const operand_pos_bits = operand_info.bits - @intFromBool(operand_info.signedness == .signed); | | |
| 9042 | | | |
| 9043 | const dest_allows_neg = dest_info.signedness == .signed and dest_info.bits > 0; | | |
| 9044 | const operand_maybe_neg = operand_info.signedness == .signed and operand_info.bits > 0; | | |
| 9045 | | | |
| 9046 | break :c .{ | | |
| 9047 | operand_maybe_neg and (!dest_allows_neg or dest_info.bits < operand_info.bits), | | |
| 9048 | dest_pos_bits < operand_pos_bits, | | |
| 9049 | }; | | |
| 9050 | }; | | |
| 9051 | | | |
| 9052 | if (!have_min_check and !have_max_check) break :bounds_check; | | |
| 9053 | | | |
| 9054 | const operand_llvm_ty = try o.lowerType(pt, operand_ty); | | |
| 9055 | const operand_scalar_llvm_ty = try o.lowerType(pt, operand_scalar); | | |
| 9056 | | | |
| 9057 | const is_vector = operand_ty.zigTypeTag(zcu) == .vector; | | |
| 9058 | assert(is_vector == (dest_ty.zigTypeTag(zcu) == .vector)); | | |
| 9059 | | | |
| 9060 | const panic_id: Zcu.SimplePanicId = if (dest_is_enum) .invalid_enum_value else .integer_out_of_bounds; | | |
| 9061 | | | |
| 9062 | if (have_min_check) { | | |
| 9063 | const min_const_scalar = try minIntConst(&o.builder, dest_scalar, operand_scalar_llvm_ty, zcu); | | |
| 9064 | const min_val = if (is_vector) try o.builder.splatValue(operand_llvm_ty, min_const_scalar) else min_const_scalar.toValue(); | | |
| 9065 | const ok_maybe_vec = try fg.cmp(.normal, .gte, operand_ty, operand, min_val); | | |
| 9066 | const ok = if (is_vector) ok: { | | |
| 9067 | const vec_ty = ok_maybe_vec.typeOfWip(&fg.wip); | | |
| 9068 | break :ok try fg.wip.callIntrinsic(.normal, .none, .@"vector.reduce.and", &.{vec_ty}, &.{ok_maybe_vec}, ""); | | |
| 9069 | } else ok_maybe_vec; | | |
| 9070 | if (safety) { | | |
| 9071 | const fail_block = try fg.wip.block(1, "IntMinFail"); | | |
| 9072 | const ok_block = try fg.wip.block(1, "IntMinOk"); | | |
| 9073 | _ = try fg.wip.brCond(ok, ok_block, fail_block, .none); | | |
| 9074 | fg.wip.cursor = .{ .block = fail_block }; | | |
| 9075 | try fg.buildSimplePanic(panic_id); | | |
| 9076 | fg.wip.cursor = .{ .block = ok_block }; | | |
| 9077 | } else { | | |
| 9078 | _ = try fg.wip.callIntrinsic(.normal, .none, .assume, &.{}, &.{ok}, ""); | | |
| 9079 | } | | |
| 9080 | } | | |
| 9081 | | | |
| 9082 | if (have_max_check) { | | |
| 9083 | const max_const_scalar = try maxIntConst(&o.builder, dest_scalar, operand_scalar_llvm_ty, zcu); | | |
| 9084 | const max_val = if (is_vector) try o.builder.splatValue(operand_llvm_ty, max_const_scalar) else max_const_scalar.toValue(); | | |
| 9085 | const ok_maybe_vec = try fg.cmp(.normal, .lte, operand_ty, operand, max_val); | | |
| 9086 | const ok = if (is_vector) ok: { | | |
| 9087 | const vec_ty = ok_maybe_vec.typeOfWip(&fg.wip); | | |
| 9088 | break :ok try fg.wip.callIntrinsic(.normal, .none, .@"vector.reduce.and", &.{vec_ty}, &.{ok_maybe_vec}, ""); | | |
| 9089 | } else ok_maybe_vec; | | |
| 9090 | if (safety) { | | |
| 9091 | const fail_block = try fg.wip.block(1, "IntMaxFail"); | | |
| 9092 | const ok_block = try fg.wip.block(1, "IntMaxOk"); | | |
| 9093 | _ = try fg.wip.brCond(ok, ok_block, fail_block, .none); | | |
| 9094 | fg.wip.cursor = .{ .block = fail_block }; | | |
| 9095 | try fg.buildSimplePanic(panic_id); | | |
| 9096 | fg.wip.cursor = .{ .block = ok_block }; | | |
| 9097 | } else { | | |
| 9098 | _ = try fg.wip.callIntrinsic(.normal, .none, .assume, &.{}, &.{ok}, ""); | | |
| 9099 | } | | |
| 9100 | } | | |
| 9101 | } | | |
| 9102 | | | |
| 9103 | const result = try fg.wip.conv(switch (operand_info.signedness) { | | |
| 9104 | .signed => .signed, | | |
| 9105 | .unsigned => .unsigned, | | |
| 9106 | }, operand, dest_llvm_ty, ""); | | |
| 9107 | | | |
| 9108 | if (safety and dest_is_enum and !dest_ty.isNonexhaustiveEnum(zcu)) { | | |
| 9109 | const llvm_fn = try fg.getIsNamedEnumValueFunction(dest_ty); | | |
| 9110 | const is_valid_enum_val = try fg.wip.call( | | |
| 9111 | .normal, | | |
| 9112 | .fastcc, | | |
| 9113 | .none, | | |
| 9114 | llvm_fn.typeOf(&o.builder), | | |
| 9115 | llvm_fn.toValue(&o.builder), | | |
| 9116 | &.{result}, | | |
| 9117 | "", | | |
| 9118 | ); | | |
| 9119 | const fail_block = try fg.wip.block(1, "ValidEnumFail"); | | |
| 9120 | const ok_block = try fg.wip.block(1, "ValidEnumOk"); | | |
| 9121 | _ = try fg.wip.brCond(is_valid_enum_val, ok_block, fail_block, .none); | | |
| 9122 | fg.wip.cursor = .{ .block = fail_block }; | | |
| 9123 | try fg.buildSimplePanic(.invalid_enum_value); | | |
| 9124 | fg.wip.cursor = .{ .block = ok_block }; | | |
| 9125 | } | | |
| 9126 | | | |
| 9127 | return result; | | |
| 9128 | } | | |
| 9129 | | | |
| 9130 | fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 9131 | const o = self.ng.object; | | |
| 9132 | const pt = self.ng.pt; | | |
| 9133 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 9134 | const operand = try self.resolveInst(ty_op.operand); | | |
| 9135 | const dest_llvm_ty = try o.lowerType(pt, self.typeOfIndex(inst)); | | |
| 9136 | return self.wip.cast(.trunc, operand, dest_llvm_ty, ""); | | |
| 9137 | } | | |
| 9138 | | | |
| 9139 | fn airFptrunc(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 9140 | const o = self.ng.object; | | |
| 9141 | const pt = self.ng.pt; | | |
| 9142 | const zcu = pt.zcu; | | |
| 9143 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 9144 | const operand = try self.resolveInst(ty_op.operand); | | |
| 9145 | const operand_ty = self.typeOf(ty_op.operand); | | |
| 9146 | const dest_ty = self.typeOfIndex(inst); | | |
| 9147 | const target = zcu.getTarget(); | | |
| 9148 | | | |
| 9149 | if (intrinsicsAllowed(dest_ty, target) and intrinsicsAllowed(operand_ty, target)) { | | |
| 9150 | return self.wip.cast(.fptrunc, operand, try o.lowerType(pt, dest_ty), ""); | | |
| 9151 | } else { | | |
| 9152 | const operand_llvm_ty = try o.lowerType(pt, operand_ty); | | |
| 9153 | const dest_llvm_ty = try o.lowerType(pt, dest_ty); | | |
| 9154 | | | |
| 9155 | const dest_bits = dest_ty.floatBits(target); | | |
| 9156 | const src_bits = operand_ty.floatBits(target); | | |
| 9157 | const fn_name = try o.builder.strtabStringFmt("__trunc{s}f{s}f2", .{ | | |
| 9158 | compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits), | | |
| 9159 | }); | | |
| 9160 | | | |
| 9161 | const libc_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty); | | |
| 9162 | return self.wip.call( | | |
| 9163 | .normal, | | |
| 9164 | .ccc, | | |
| 9165 | .none, | | |
| 9166 | libc_fn.typeOf(&o.builder), | | |
| 9167 | libc_fn.toValue(&o.builder), | | |
| 9168 | &.{operand}, | | |
| 9169 | "", | | |
| 9170 | ); | | |
| 9171 | } | | |
| 9172 | } | | |
| 9173 | | | |
| 9174 | fn airFpext(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 9175 | const o = self.ng.object; | | |
| 9176 | const pt = self.ng.pt; | | |
| 9177 | const zcu = pt.zcu; | | |
| 9178 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 9179 | const operand = try self.resolveInst(ty_op.operand); | | |
| 9180 | const operand_ty = self.typeOf(ty_op.operand); | | |
| 9181 | const dest_ty = self.typeOfIndex(inst); | | |
| 9182 | const target = zcu.getTarget(); | | |
| 9183 | | | |
| 9184 | if (intrinsicsAllowed(dest_ty, target) and intrinsicsAllowed(operand_ty, target)) { | | |
| 9185 | return self.wip.cast(.fpext, operand, try o.lowerType(pt, dest_ty), ""); | | |
| 9186 | } else { | | |
| 9187 | const operand_llvm_ty = try o.lowerType(pt, operand_ty); | | |
| 9188 | const dest_llvm_ty = try o.lowerType(pt, dest_ty); | | |
| 9189 | | | |
| 9190 | const dest_bits = dest_ty.scalarType(zcu).floatBits(target); | | |
| 9191 | const src_bits = operand_ty.scalarType(zcu).floatBits(target); | | |
| 9192 | const fn_name = try o.builder.strtabStringFmt("__extend{s}f{s}f2", .{ | | |
| 9193 | compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits), | | |
| 9194 | }); | | |
| 9195 | | | |
| 9196 | const libc_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty); | | |
| 9197 | if (dest_ty.isVector(zcu)) return self.buildElementwiseCall( | | |
| 9198 | libc_fn, | | |
| 9199 | &.{operand}, | | |
| 9200 | try o.builder.poisonValue(dest_llvm_ty), | | |
| 9201 | dest_ty.vectorLen(zcu), | | |
| 9202 | ); | | |
| 9203 | return self.wip.call( | | |
| 9204 | .normal, | | |
| 9205 | .ccc, | | |
| 9206 | .none, | | |
| 9207 | libc_fn.typeOf(&o.builder), | | |
| 9208 | libc_fn.toValue(&o.builder), | | |
| 9209 | &.{operand}, | | |
| 9210 | "", | | |
| 9211 | ); | | |
| 9212 | } | | |
| 9213 | } | | |
| 9214 | | | |
| 9215 | fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 9216 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 9217 | const operand_ty = self.typeOf(ty_op.operand); | | |
| 9218 | const inst_ty = self.typeOfIndex(inst); | | |
| 9219 | const operand = try self.resolveInst(ty_op.operand); | | |
| 9220 | return self.bitCast(operand, operand_ty, inst_ty); | | |
| 9221 | } | | |
| 9222 | | | |
| 9223 | fn bitCast(self: *FuncGen, operand: Builder.Value, operand_ty: Type, inst_ty: Type) !Builder.Value { | | |
| 9224 | const o = self.ng.object; | | |
| 9225 | const pt = self.ng.pt; | | |
| 9226 | const zcu = pt.zcu; | | |
| 9227 | const operand_is_ref = isByRef(operand_ty, zcu); | | |
| 9228 | const result_is_ref = isByRef(inst_ty, zcu); | | |
| 9229 | const llvm_dest_ty = try o.lowerType(pt, inst_ty); | | |
| 9230 | | | |
| 9231 | if (operand_is_ref and result_is_ref) { | | |
| 9232 | // They are both pointers, so just return the same opaque pointer :) | | |
| 9233 | return operand; | | |
| 9234 | } | | |
| 9235 | | | |
| 9236 | if (llvm_dest_ty.isInteger(&o.builder) and | | |
| 9237 | operand.typeOfWip(&self.wip).isInteger(&o.builder)) | | |
| 9238 | { | | |
| 9239 | return self.wip.conv(.unsigned, operand, llvm_dest_ty, ""); | | |
| 9240 | } | | |
| 9241 | | | |
| 9242 | const operand_scalar_ty = operand_ty.scalarType(zcu); | | |
| 9243 | const inst_scalar_ty = inst_ty.scalarType(zcu); | | |
| 9244 | if (operand_scalar_ty.zigTypeTag(zcu) == .int and inst_scalar_ty.isPtrAtRuntime(zcu)) { | | |
| 9245 | return self.wip.cast(.inttoptr, operand, llvm_dest_ty, ""); | | |
| 9246 | } | | |
| 9247 | if (operand_scalar_ty.isPtrAtRuntime(zcu) and inst_scalar_ty.zigTypeTag(zcu) == .int) { | | |
| 9248 | return self.wip.cast(.ptrtoint, operand, llvm_dest_ty, ""); | | |
| 9249 | } | | |
| 9250 | | | |
| 9251 | if (operand_ty.zigTypeTag(zcu) == .vector and inst_ty.zigTypeTag(zcu) == .array) { | | |
| 9252 | const elem_ty = operand_ty.childType(zcu); | | |
| 9253 | if (!result_is_ref) { | | |
| 9254 | return self.ng.todo("implement bitcast vector to non-ref array", .{}); | | |
| 9255 | } | | |
| 9256 | const alignment = inst_ty.abiAlignment(zcu).toLlvm(); | | |
| 9257 | const array_ptr = try self.buildAlloca(llvm_dest_ty, alignment); | | |
| 9258 | const bitcast_ok = elem_ty.bitSize(zcu) == elem_ty.abiSize(zcu) * 8; | | |
| 9259 | if (bitcast_ok) { | | |
| 9260 | _ = try self.wip.store(.normal, operand, array_ptr, alignment); | | |
| 9261 | } else { | | |
| 9262 | // If the ABI size of the element type is not evenly divisible by size in bits; | | |
| 9263 | // a simple bitcast will not work, and we fall back to extractelement. | | |
| 9264 | const llvm_usize = try o.lowerType(pt, Type.usize); | | |
| 9265 | const usize_zero = try o.builder.intValue(llvm_usize, 0); | | |
| 9266 | const vector_len = operand_ty.arrayLen(zcu); | | |
| 9267 | var i: u64 = 0; | | |
| 9268 | while (i < vector_len) : (i += 1) { | | |
| 9269 | const elem_ptr = try self.wip.gep(.inbounds, llvm_dest_ty, array_ptr, &.{ | | |
| 9270 | usize_zero, try o.builder.intValue(llvm_usize, i), | | |
| 9271 | }, ""); | | |
| 9272 | const elem = | | |
| 9273 | try self.wip.extractElement(operand, try o.builder.intValue(.i32, i), ""); | | |
| 9274 | _ = try self.wip.store(.normal, elem, elem_ptr, .default); | | |
| 9275 | } | | |
| 9276 | } | | |
| 9277 | return array_ptr; | | |
| 9278 | } else if (operand_ty.zigTypeTag(zcu) == .array and inst_ty.zigTypeTag(zcu) == .vector) { | | |
| 9279 | const elem_ty = operand_ty.childType(zcu); | | |
| 9280 | const llvm_vector_ty = try o.lowerType(pt, inst_ty); | | |
| 9281 | if (!operand_is_ref) return self.ng.todo("implement bitcast non-ref array to vector", .{}); | | |
| 9282 | | | |
| 9283 | const bitcast_ok = elem_ty.bitSize(zcu) == elem_ty.abiSize(zcu) * 8; | | |
| 9284 | if (bitcast_ok) { | | |
| 9285 | // The array is aligned to the element's alignment, while the vector might have a completely | | |
| 9286 | // different alignment. This means we need to enforce the alignment of this load. | | |
| 9287 | const alignment = elem_ty.abiAlignment(zcu).toLlvm(); | | |
| 9288 | return self.wip.load(.normal, llvm_vector_ty, operand, alignment, ""); | | |
| 9289 | } else { | | |
| 9290 | // If the ABI size of the element type is not evenly divisible by size in bits; | | |
| 9291 | // a simple bitcast will not work, and we fall back to extractelement. | | |
| 9292 | const array_llvm_ty = try o.lowerType(pt, operand_ty); | | |
| 9293 | const elem_llvm_ty = try o.lowerType(pt, elem_ty); | | |
| 9294 | const llvm_usize = try o.lowerType(pt, Type.usize); | | |
| 9295 | const usize_zero = try o.builder.intValue(llvm_usize, 0); | | |
| 9296 | const vector_len = operand_ty.arrayLen(zcu); | | |
| 9297 | var vector = try o.builder.poisonValue(llvm_vector_ty); | | |
| 9298 | var i: u64 = 0; | | |
| 9299 | while (i < vector_len) : (i += 1) { | | |
| 9300 | const elem_ptr = try self.wip.gep(.inbounds, array_llvm_ty, operand, &.{ | | |
| 9301 | usize_zero, try o.builder.intValue(llvm_usize, i), | | |
| 9302 | }, ""); | | |
| 9303 | const elem = try self.wip.load(.normal, elem_llvm_ty, elem_ptr, .default, ""); | | |
| 9304 | vector = | | |
| 9305 | try self.wip.insertElement(vector, elem, try o.builder.intValue(.i32, i), ""); | | |
| 9306 | } | | |
| 9307 | return vector; | | |
| 9308 | } | | |
| 9309 | } | | |
| 9310 | | | |
| 9311 | if (operand_is_ref) { | | |
| 9312 | const alignment = operand_ty.abiAlignment(zcu).toLlvm(); | | |
| 9313 | return self.wip.load(.normal, llvm_dest_ty, operand, alignment, ""); | | |
| 9314 | } | | |
| 9315 | | | |
| 9316 | if (result_is_ref) { | | |
| 9317 | const alignment = operand_ty.abiAlignment(zcu).max(inst_ty.abiAlignment(zcu)).toLlvm(); | | |
| 9318 | const result_ptr = try self.buildAlloca(llvm_dest_ty, alignment); | | |
| 9319 | _ = try self.wip.store(.normal, operand, result_ptr, alignment); | | |
| 9320 | return result_ptr; | | |
| 9321 | } | | |
| 9322 | | | |
| 9323 | if (llvm_dest_ty.isStruct(&o.builder) or | | |
| 9324 | ((operand_ty.zigTypeTag(zcu) == .vector or inst_ty.zigTypeTag(zcu) == .vector) and | | |
| 9325 | operand_ty.bitSize(zcu) != inst_ty.bitSize(zcu))) | | |
| 9326 | { | | |
| 9327 | // Both our operand and our result are values, not pointers, | | |
| 9328 | // but LLVM won't let us bitcast struct values or vectors with padding bits. | | |
| 9329 | // Therefore, we store operand to alloca, then load for result. | | |
| 9330 | const alignment = operand_ty.abiAlignment(zcu).max(inst_ty.abiAlignment(zcu)).toLlvm(); | | |
| 9331 | const result_ptr = try self.buildAlloca(llvm_dest_ty, alignment); | | |
| 9332 | _ = try self.wip.store(.normal, operand, result_ptr, alignment); | | |
| 9333 | return self.wip.load(.normal, llvm_dest_ty, result_ptr, alignment, ""); | | |
| 9334 | } | | |
| 9335 | | | |
| 9336 | return self.wip.cast(.bitcast, operand, llvm_dest_ty, ""); | | |
| 9337 | } | | |
| 9338 | | | |
| 9339 | fn airArg(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 9340 | const o = self.ng.object; | | |
| 9341 | const pt = self.ng.pt; | | |
| 9342 | const zcu = pt.zcu; | | |
| 9343 | const arg_val = self.args[self.arg_index]; | | |
| 9344 | self.arg_index += 1; | | |
| 9345 | | | |
| 9346 | // llvm does not support debug info for naked function arguments | | |
| 9347 | if (self.is_naked) return arg_val; | | |
| 9348 | | | |
| 9349 | const inst_ty = self.typeOfIndex(inst); | | |
| 9350 | | | |
| 9351 | const func = zcu.funcInfo(zcu.navValue(self.ng.nav_index).toIntern()); | | |
| 9352 | const func_zir = func.zir_body_inst.resolveFull(&zcu.intern_pool).?; | | |
| 9353 | const file = zcu.fileByIndex(func_zir.file); | | |
| 9354 | | | |
| 9355 | const mod = file.mod.?; | | |
| 9356 | if (mod.strip) return arg_val; | | |
| 9357 | const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg; | | |
| 9358 | const zir = &file.zir.?; | | |
| 9359 | const name = zir.nullTerminatedString(zir.getParamName(zir.getParamBody(func_zir.inst)[arg.zir_param_index]).?); | | |
| 9360 | | | |
| 9361 | const lbrace_line = zcu.navSrcLine(func.owner_nav) + func.lbrace_line + 1; | | |
| 9362 | const lbrace_col = func.lbrace_column + 1; | | |
| 9363 | | | |
| 9364 | const debug_parameter = try o.builder.debugParameter( | | |
| 9365 | if (name.len > 0) try o.builder.metadataString(name) else null, | | |
| 9366 | self.file, | | |
| 9367 | self.scope, | | |
| 9368 | lbrace_line, | | |
| 9369 | try o.getDebugType(pt, inst_ty), | | |
| 9370 | self.arg_index, | | |
| 9371 | ); | | |
| 9372 | | | |
| 9373 | const old_location = self.wip.debug_location; | | |
| 9374 | self.wip.debug_location = .{ .location = .{ | | |
| 9375 | .line = lbrace_line, | | |
| 9376 | .column = lbrace_col, | | |
| 9377 | .scope = self.scope.toOptional(), | | |
| 9378 | .inlined_at = .none, | | |
| 9379 | } }; | | |
| 9380 | | | |
| 9381 | if (isByRef(inst_ty, zcu)) { | | |
| 9382 | _ = try self.wip.callIntrinsic( | | |
| 9383 | .normal, | | |
| 9384 | .none, | | |
| 9385 | .@"dbg.declare", | | |
| 9386 | &.{}, | | |
| 9387 | &.{ | | |
| 9388 | (try self.wip.debugValue(arg_val)).toValue(), | | |
| 9389 | debug_parameter.toValue(), | | |
| 9390 | (try o.builder.debugExpression(&.{})).toValue(), | | |
| 9391 | }, | | |
| 9392 | "", | | |
| 9393 | ); | | |
| 9394 | } else if (mod.optimize_mode == .Debug) { | | |
| 9395 | const alignment = inst_ty.abiAlignment(zcu).toLlvm(); | | |
| 9396 | const alloca = try self.buildAlloca(arg_val.typeOfWip(&self.wip), alignment); | | |
| 9397 | _ = try self.wip.store(.normal, arg_val, alloca, alignment); | | |
| 9398 | _ = try self.wip.callIntrinsic( | | |
| 9399 | .normal, | | |
| 9400 | .none, | | |
| 9401 | .@"dbg.declare", | | |
| 9402 | &.{}, | | |
| 9403 | &.{ | | |
| 9404 | (try self.wip.debugValue(alloca)).toValue(), | | |
| 9405 | debug_parameter.toValue(), | | |
| 9406 | (try o.builder.debugExpression(&.{})).toValue(), | | |
| 9407 | }, | | |
| 9408 | "", | | |
| 9409 | ); | | |
| 9410 | } else { | | |
| 9411 | _ = try self.wip.callIntrinsic( | | |
| 9412 | .normal, | | |
| 9413 | .none, | | |
| 9414 | .@"dbg.value", | | |
| 9415 | &.{}, | | |
| 9416 | &.{ | | |
| 9417 | (try self.wip.debugValue(arg_val)).toValue(), | | |
| 9418 | debug_parameter.toValue(), | | |
| 9419 | (try o.builder.debugExpression(&.{})).toValue(), | | |
| 9420 | }, | | |
| 9421 | "", | | |
| 9422 | ); | | |
| 9423 | } | | |
| 9424 | | | |
| 9425 | self.wip.debug_location = old_location; | | |
| 9426 | return arg_val; | | |
| 9427 | } | | |
| 9428 | | | |
| 9429 | fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 9430 | const o = self.ng.object; | | |
| 9431 | const pt = self.ng.pt; | | |
| 9432 | const zcu = pt.zcu; | | |
| 9433 | const ptr_ty = self.typeOfIndex(inst); | | |
| 9434 | const pointee_type = ptr_ty.childType(zcu); | | |
| 9435 | if (!pointee_type.hasRuntimeBits(zcu)) | | |
| 9436 | return (try o.lowerPtrToVoid(pt, ptr_ty)).toValue(); | | |
| 9437 | | | |
| 9438 | const pointee_llvm_ty = try o.lowerType(pt, pointee_type); | | |
| 9439 | const alignment = ptr_ty.ptrAlignment(zcu).toLlvm(); | | |
| 9440 | return self.buildAlloca(pointee_llvm_ty, alignment); | | |
| 9441 | } | | |
| 9442 | | | |
| 9443 | fn airRetPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 9444 | const o = self.ng.object; | | |
| 9445 | const pt = self.ng.pt; | | |
| 9446 | const zcu = pt.zcu; | | |
| 9447 | const ptr_ty = self.typeOfIndex(inst); | | |
| 9448 | const ret_ty = ptr_ty.childType(zcu); | | |
| 9449 | if (!ret_ty.hasRuntimeBits(zcu)) | | |
| 9450 | return (try o.lowerPtrToVoid(pt, ptr_ty)).toValue(); | | |
| 9451 | if (self.ret_ptr != .none) return self.ret_ptr; | | |
| 9452 | const ret_llvm_ty = try o.lowerType(pt, ret_ty); | | |
| 9453 | const alignment = ptr_ty.ptrAlignment(zcu).toLlvm(); | | |
| 9454 | return self.buildAlloca(ret_llvm_ty, alignment); | | |
| 9455 | } | | |
| 9456 | | | |
| 9457 | /// Use this instead of builder.buildAlloca, because this function makes sure to | | |
| 9458 | /// put the alloca instruction at the top of the function! | | |
| 9459 | fn buildAlloca( | | |
| 9460 | self: *FuncGen, | | |
| 9461 | llvm_ty: Builder.Type, | | |
| 9462 | alignment: Builder.Alignment, | | |
| 9463 | ) Allocator.Error!Builder.Value { | | |
| 9464 | const target = self.ng.pt.zcu.getTarget(); | | |
| 9465 | return buildAllocaInner(&self.wip, llvm_ty, alignment, target); | | |
| 9466 | } | | |
| 9467 | | | |
| 9468 | fn airStore(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !Builder.Value { | | |
| 9469 | const o = self.ng.object; | | |
| 9470 | const pt = self.ng.pt; | | |
| 9471 | const zcu = pt.zcu; | | |
| 9472 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 9473 | const dest_ptr = try self.resolveInst(bin_op.lhs); | | |
| 9474 | const ptr_ty = self.typeOf(bin_op.lhs); | | |
| 9475 | const operand_ty = ptr_ty.childType(zcu); | | |
| 9476 | | | |
| 9477 | const val_is_undef = if (try self.air.value(bin_op.rhs, pt)) |val| val.isUndef(zcu) else false; | | |
| 9478 | if (val_is_undef) { | | |
| 9479 | const owner_mod = self.ng.ownerModule(); | | |
| 9480 | | | |
| 9481 | // Even if safety is disabled, we still emit a memset to undefined since it conveys | | |
| 9482 | // extra information to LLVM, and LLVM will optimize it out. Safety makes the difference | | |
| 9483 | // between using 0xaa or actual undefined for the fill byte. | | |
| 9484 | // | | |
| 9485 | // However, for Debug builds specifically, we avoid emitting the memset because LLVM | | |
| 9486 | // will neither use the information nor get rid of the memset, thus leaving an | | |
| 9487 | // unexpected call in the user's code. This is problematic if the code in question is | | |
| 9488 | // not ready to correctly make calls yet, such as in our early PIE startup code, or in | | |
| 9489 | // the early stages of a dynamic linker, etc. | | |
| 9490 | if (!safety and owner_mod.optimize_mode == .Debug) { | | |
| 9491 | return .none; | | |
| 9492 | } | | |
| 9493 | | | |
| 9494 | const ptr_info = ptr_ty.ptrInfo(zcu); | | |
| 9495 | const needs_bitmask = (ptr_info.packed_offset.host_size != 0); | | |
| 9496 | if (needs_bitmask) { | | |
| 9497 | // TODO: only some bits are to be undef, we cannot write with a simple memset. | | |
| 9498 | // meanwhile, ignore the write rather than stomping over valid bits. | | |
| 9499 | // https://github.com/ziglang/zig/issues/15337 | | |
| 9500 | return .none; | | |
| 9501 | } | | |
| 9502 | | | |
| 9503 | self.maybeMarkAllowZeroAccess(ptr_info); | | |
| 9504 | | | |
| 9505 | const len = try o.builder.intValue(try o.lowerType(pt, Type.usize), operand_ty.abiSize(zcu)); | | |
| 9506 | _ = try self.wip.callMemSet( | | |
| 9507 | dest_ptr, | | |
| 9508 | ptr_ty.ptrAlignment(zcu).toLlvm(), | | |
| 9509 | if (safety) try o.builder.intValue(.i8, 0xaa) else try o.builder.undefValue(.i8), | | |
| 9510 | len, | | |
| 9511 | if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal, | | |
| 9512 | self.disable_intrinsics, | | |
| 9513 | ); | | |
| 9514 | if (safety and owner_mod.valgrind) { | | |
| 9515 | try self.valgrindMarkUndef(dest_ptr, len); | | |
| 9516 | } | | |
| 9517 | return .none; | | |
| 9518 | } | | |
| 9519 | | | |
| 9520 | self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu)); | | |
| 9521 | | | |
| 9522 | const src_operand = try self.resolveInst(bin_op.rhs); | | |
| 9523 | try self.store(dest_ptr, ptr_ty, src_operand, .none); | | |
| 9524 | return .none; | | |
| 9525 | } | | |
| 9526 | | | |
| 9527 | fn airLoad(fg: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 9528 | const pt = fg.ng.pt; | | |
| 9529 | const zcu = pt.zcu; | | |
| 9530 | const ty_op = fg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 9531 | const ptr_ty = fg.typeOf(ty_op.operand); | | |
| 9532 | const ptr_info = ptr_ty.ptrInfo(zcu); | | |
| 9533 | const ptr = try fg.resolveInst(ty_op.operand); | | |
| 9534 | fg.maybeMarkAllowZeroAccess(ptr_info); | | |
| 9535 | return fg.load(ptr, ptr_ty); | | |
| 9536 | } | | |
| 9537 | | | |
| 9538 | fn airTrap(self: *FuncGen, inst: Air.Inst.Index) !void { | | |
| 9539 | _ = inst; | | |
| 9540 | const target = self.ng.object.target; | | |
| 9541 | if ((target.cpu.arch == .mips or target.cpu.arch == .mipsel) and | | |
| 9542 | target.cpu.has(.mips, .notraps)) | | |
| 9543 | { | | |
| 9544 | // Emit a MIPS `break` instruction followed by an infinite loop (to fulfill the noreturn) | | |
| 9545 | // since this CPU does not support trap instructions. | | |
| 9546 | const o = self.ng.object; | | |
| 9547 | _ = try self.wip.callAsm( | | |
| 9548 | .none, | | |
| 9549 | try o.builder.fnType(.void, &.{}, .normal), | | |
| 9550 | .{ .sideeffect = true }, | | |
| 9551 | try o.builder.string("break\n0:\nj 0b\nnop"), | | |
| 9552 | try o.builder.string("~{memory}"), | | |
| 9553 | &.{}, | | |
| 9554 | "", | | |
| 9555 | ); | | |
| 9556 | } else { | | |
| 9557 | _ = try self.wip.callIntrinsic(.normal, .none, .trap, &.{}, &.{}, ""); | | |
| 9558 | } | | |
| 9559 | _ = try self.wip.@"unreachable"(); | | |
| 9560 | } | | |
| 9561 | | | |
| 9562 | fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 9563 | _ = inst; | | |
| 9564 | _ = try self.wip.callIntrinsic(.normal, .none, .debugtrap, &.{}, &.{}, ""); | | |
| 9565 | return .none; | | |
| 9566 | } | | |
| 9567 | | | |
| 9568 | fn airRetAddr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 9569 | _ = inst; | | |
| 9570 | const o = self.ng.object; | | |
| 9571 | const pt = self.ng.pt; | | |
| 9572 | const llvm_usize = try o.lowerType(pt, Type.usize); | | |
| 9573 | if (!target_util.supportsReturnAddress(self.ng.pt.zcu.getTarget(), self.ng.ownerModule().optimize_mode)) { | | |
| 9574 | // https://github.com/ziglang/zig/issues/11946 | | |
| 9575 | return o.builder.intValue(llvm_usize, 0); | | |
| 9576 | } | | |
| 9577 | const result = try self.wip.callIntrinsic(.normal, .none, .returnaddress, &.{}, &.{.@"0"}, ""); | | |
| 9578 | return self.wip.cast(.ptrtoint, result, llvm_usize, ""); | | |
| 9579 | } | | |
| 9580 | | | |
| 9581 | fn airFrameAddress(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 9582 | _ = inst; | | |
| 9583 | const o = self.ng.object; | | |
| 9584 | const pt = self.ng.pt; | | |
| 9585 | const result = try self.wip.callIntrinsic(.normal, .none, .frameaddress, &.{.ptr}, &.{.@"0"}, ""); | | |
| 9586 | return self.wip.cast(.ptrtoint, result, try o.lowerType(pt, Type.usize), ""); | | |
| 9587 | } | | |
| 9588 | | | |
| 9589 | fn airCmpxchg( | | |
| 9590 | self: *FuncGen, | | |
| 9591 | inst: Air.Inst.Index, | | |
| 9592 | kind: Builder.Function.Instruction.CmpXchg.Kind, | | |
| 9593 | ) !Builder.Value { | | |
| 9594 | const o = self.ng.object; | | |
| 9595 | const pt = self.ng.pt; | | |
| 9596 | const zcu = pt.zcu; | | |
| 9597 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | | |
| 9598 | const extra = self.air.extraData(Air.Cmpxchg, ty_pl.payload).data; | | |
| 9599 | const ptr = try self.resolveInst(extra.ptr); | | |
| 9600 | const ptr_ty = self.typeOf(extra.ptr); | | |
| 9601 | var expected_value = try self.resolveInst(extra.expected_value); | | |
| 9602 | var new_value = try self.resolveInst(extra.new_value); | | |
| 9603 | const operand_ty = ptr_ty.childType(zcu); | | |
| 9604 | const llvm_operand_ty = try o.lowerType(pt, operand_ty); | | |
| 9605 | const llvm_abi_ty = try o.getAtomicAbiType(pt, operand_ty, false); | | |
| 9606 | if (llvm_abi_ty != .none) { | | |
| 9607 | // operand needs widening and truncating | | |
| 9608 | const signedness: Builder.Function.Instruction.Cast.Signedness = | | |
| 9609 | if (operand_ty.isSignedInt(zcu)) .signed else .unsigned; | | |
| 9610 | expected_value = try self.wip.conv(signedness, expected_value, llvm_abi_ty, ""); | | |
| 9611 | new_value = try self.wip.conv(signedness, new_value, llvm_abi_ty, ""); | | |
| 9612 | } | | |
| 9613 | | | |
| 9614 | self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu)); | | |
| 9615 | | | |
| 9616 | const result = try self.wip.cmpxchg( | | |
| 9617 | kind, | | |
| 9618 | if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal, | | |
| 9619 | ptr, | | |
| 9620 | expected_value, | | |
| 9621 | new_value, | | |
| 9622 | self.sync_scope, | | |
| 9623 | toLlvmAtomicOrdering(extra.successOrder()), | | |
| 9624 | toLlvmAtomicOrdering(extra.failureOrder()), | | |
| 9625 | ptr_ty.ptrAlignment(zcu).toLlvm(), | | |
| 9626 | "", | | |
| 9627 | ); | | |
| 9628 | | | |
| 9629 | const optional_ty = self.typeOfIndex(inst); | | |
| 9630 | | | |
| 9631 | var payload = try self.wip.extractValue(result, &.{0}, ""); | | |
| 9632 | if (llvm_abi_ty != .none) payload = try self.wip.cast(.trunc, payload, llvm_operand_ty, ""); | | |
| 9633 | const success_bit = try self.wip.extractValue(result, &.{1}, ""); | | |
| 9634 | | | |
| 9635 | if (optional_ty.optionalReprIsPayload(zcu)) { | | |
| 9636 | const zero = try o.builder.zeroInitValue(payload.typeOfWip(&self.wip)); | | |
| 9637 | return self.wip.select(.normal, success_bit, zero, payload, ""); | | |
| 9638 | } | | |
| 9639 | | | |
| 9640 | comptime assert(optional_layout_version == 3); | | |
| 9641 | | | |
| 9642 | const non_null_bit = try self.wip.not(success_bit, ""); | | |
| 9643 | return buildOptional(self, optional_ty, payload, non_null_bit); | | |
| 9644 | } | | |
| 9645 | | | |
| 9646 | fn airAtomicRmw(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 9647 | const o = self.ng.object; | | |
| 9648 | const pt = self.ng.pt; | | |
| 9649 | const zcu = pt.zcu; | | |
| 9650 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | | |
| 9651 | const extra = self.air.extraData(Air.AtomicRmw, pl_op.payload).data; | | |
| 9652 | const ptr = try self.resolveInst(pl_op.operand); | | |
| 9653 | const ptr_ty = self.typeOf(pl_op.operand); | | |
| 9654 | const operand_ty = ptr_ty.childType(zcu); | | |
| 9655 | const operand = try self.resolveInst(extra.operand); | | |
| 9656 | const is_signed_int = operand_ty.isSignedInt(zcu); | | |
| 9657 | const is_float = operand_ty.isRuntimeFloat(); | | |
| 9658 | const op = toLlvmAtomicRmwBinOp(extra.op(), is_signed_int, is_float); | | |
| 9659 | const ordering = toLlvmAtomicOrdering(extra.ordering()); | | |
| 9660 | const llvm_abi_ty = try o.getAtomicAbiType(pt, operand_ty, op == .xchg); | | |
| 9661 | const llvm_operand_ty = try o.lowerType(pt, operand_ty); | | |
| 9662 | | | |
| 9663 | const access_kind: Builder.MemoryAccessKind = | | |
| 9664 | if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; | | |
| 9665 | const ptr_alignment = ptr_ty.ptrAlignment(zcu).toLlvm(); | | |
| 9666 | | | |
| 9667 | self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu)); | | |
| 9668 | | | |
| 9669 | if (llvm_abi_ty != .none) { | | |
| 9670 | // operand needs widening and truncating or bitcasting. | | |
| 9671 | return self.wip.cast(if (is_float) .bitcast else .trunc, try self.wip.atomicrmw( | | |
| 9672 | access_kind, | | |
| 9673 | op, | | |
| 9674 | ptr, | | |
| 9675 | try self.wip.cast( | | |
| 9676 | if (is_float) .bitcast else if (is_signed_int) .sext else .zext, | | |
| 9677 | operand, | | |
| 9678 | llvm_abi_ty, | | |
| 9679 | "", | | |
| 9680 | ), | | |
| 9681 | self.sync_scope, | | |
| 9682 | ordering, | | |
| 9683 | ptr_alignment, | | |
| 9684 | "", | | |
| 9685 | ), llvm_operand_ty, ""); | | |
| 9686 | } | | |
| 9687 | | | |
| 9688 | if (!llvm_operand_ty.isPointer(&o.builder)) return self.wip.atomicrmw( | | |
| 9689 | access_kind, | | |
| 9690 | op, | | |
| 9691 | ptr, | | |
| 9692 | operand, | | |
| 9693 | self.sync_scope, | | |
| 9694 | ordering, | | |
| 9695 | ptr_alignment, | | |
| 9696 | "", | | |
| 9697 | ); | | |
| 9698 | | | |
| 9699 | // It's a pointer but we need to treat it as an int. | | |
| 9700 | return self.wip.cast(.inttoptr, try self.wip.atomicrmw( | | |
| 9701 | access_kind, | | |
| 9702 | op, | | |
| 9703 | ptr, | | |
| 9704 | try self.wip.cast(.ptrtoint, operand, try o.lowerType(pt, Type.usize), ""), | | |
| 9705 | self.sync_scope, | | |
| 9706 | ordering, | | |
| 9707 | ptr_alignment, | | |
| 9708 | "", | | |
| 9709 | ), llvm_operand_ty, ""); | | |
| 9710 | } | | |
| 9711 | | | |
| 9712 | fn airAtomicLoad(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 9713 | const o = self.ng.object; | | |
| 9714 | const pt = self.ng.pt; | | |
| 9715 | const zcu = pt.zcu; | | |
| 9716 | const atomic_load = self.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load; | | |
| 9717 | const ptr = try self.resolveInst(atomic_load.ptr); | | |
| 9718 | const ptr_ty = self.typeOf(atomic_load.ptr); | | |
| 9719 | const info = ptr_ty.ptrInfo(zcu); | | |
| 9720 | const elem_ty = Type.fromInterned(info.child); | | |
| 9721 | if (!elem_ty.hasRuntimeBits(zcu)) return .none; | | |
| 9722 | const ordering = toLlvmAtomicOrdering(atomic_load.order); | | |
| 9723 | const llvm_abi_ty = try o.getAtomicAbiType(pt, elem_ty, false); | | |
| 9724 | const ptr_alignment = (if (info.flags.alignment != .none) | | |
| 9725 | @as(InternPool.Alignment, info.flags.alignment) | | |
| 9726 | else | | |
| 9727 | Type.fromInterned(info.child).abiAlignment(zcu)).toLlvm(); | | |
| 9728 | const access_kind: Builder.MemoryAccessKind = | | |
| 9729 | if (info.flags.is_volatile) .@"volatile" else .normal; | | |
| 9730 | const elem_llvm_ty = try o.lowerType(pt, elem_ty); | | |
| 9731 | | | |
| 9732 | self.maybeMarkAllowZeroAccess(info); | | |
| 9733 | | | |
| 9734 | if (llvm_abi_ty != .none) { | | |
| 9735 | // operand needs widening and truncating | | |
| 9736 | const loaded = try self.wip.loadAtomic( | | |
| 9737 | access_kind, | | |
| 9738 | llvm_abi_ty, | | |
| 9739 | ptr, | | |
| 9740 | self.sync_scope, | | |
| 9741 | ordering, | | |
| 9742 | ptr_alignment, | | |
| 9743 | "", | | |
| 9744 | ); | | |
| 9745 | return self.wip.cast(.trunc, loaded, elem_llvm_ty, ""); | | |
| 9746 | } | | |
| 9747 | return self.wip.loadAtomic( | | |
| 9748 | access_kind, | | |
| 9749 | elem_llvm_ty, | | |
| 9750 | ptr, | | |
| 9751 | self.sync_scope, | | |
| 9752 | ordering, | | |
| 9753 | ptr_alignment, | | |
| 9754 | "", | | |
| 9755 | ); | | |
| 9756 | } | | |
| 9757 | | | |
| 9758 | fn airAtomicStore( | | |
| 9759 | self: *FuncGen, | | |
| 9760 | inst: Air.Inst.Index, | | |
| 9761 | ordering: Builder.AtomicOrdering, | | |
| 9762 | ) !Builder.Value { | | |
| 9763 | const o = self.ng.object; | | |
| 9764 | const pt = self.ng.pt; | | |
| 9765 | const zcu = pt.zcu; | | |
| 9766 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 9767 | const ptr_ty = self.typeOf(bin_op.lhs); | | |
| 9768 | const operand_ty = ptr_ty.childType(zcu); | | |
| 9769 | if (!operand_ty.hasRuntimeBits(zcu)) return .none; | | |
| 9770 | const ptr = try self.resolveInst(bin_op.lhs); | | |
| 9771 | var element = try self.resolveInst(bin_op.rhs); | | |
| 9772 | const llvm_abi_ty = try o.getAtomicAbiType(pt, operand_ty, false); | | |
| 9773 | | | |
| 9774 | if (llvm_abi_ty != .none) { | | |
| 9775 | // operand needs widening | | |
| 9776 | element = try self.wip.conv( | | |
| 9777 | if (operand_ty.isSignedInt(zcu)) .signed else .unsigned, | | |
| 9778 | element, | | |
| 9779 | llvm_abi_ty, | | |
| 9780 | "", | | |
| 9781 | ); | | |
| 9782 | } | | |
| 9783 | | | |
| 9784 | self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu)); | | |
| 9785 | | | |
| 9786 | try self.store(ptr, ptr_ty, element, ordering); | | |
| 9787 | return .none; | | |
| 9788 | } | | |
| 9789 | | | |
| 9790 | fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !Builder.Value { | | |
| 9791 | const o = self.ng.object; | | |
| 9792 | const pt = self.ng.pt; | | |
| 9793 | const zcu = pt.zcu; | | |
| 9794 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 9795 | const dest_slice = try self.resolveInst(bin_op.lhs); | | |
| 9796 | const ptr_ty = self.typeOf(bin_op.lhs); | | |
| 9797 | const elem_ty = self.typeOf(bin_op.rhs); | | |
| 9798 | const dest_ptr_align = ptr_ty.ptrAlignment(zcu).toLlvm(); | | |
| 9799 | const dest_ptr = try self.sliceOrArrayPtr(dest_slice, ptr_ty); | | |
| 9800 | const access_kind: Builder.MemoryAccessKind = | | |
| 9801 | if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; | | |
| 9802 | | | |
| 9803 | self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu)); | | |
| 9804 | | | |
| 9805 | if (try self.air.value(bin_op.rhs, pt)) |elem_val| { | | |
| 9806 | if (elem_val.isUndef(zcu)) { | | |
| 9807 | // Even if safety is disabled, we still emit a memset to undefined since it conveys | | |
| 9808 | // extra information to LLVM. However, safety makes the difference between using | | |
| 9809 | // 0xaa or actual undefined for the fill byte. | | |
| 9810 | const fill_byte = if (safety) | | |
| 9811 | try o.builder.intValue(.i8, 0xaa) | | |
| 9812 | else | | |
| 9813 | try o.builder.undefValue(.i8); | | |
| 9814 | const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); | | |
| 9815 | _ = try self.wip.callMemSet( | | |
| 9816 | dest_ptr, | | |
| 9817 | dest_ptr_align, | | |
| 9818 | fill_byte, | | |
| 9819 | len, | | |
| 9820 | access_kind, | | |
| 9821 | self.disable_intrinsics, | | |
| 9822 | ); | | |
| 9823 | const owner_mod = self.ng.ownerModule(); | | |
| 9824 | if (safety and owner_mod.valgrind) { | | |
| 9825 | try self.valgrindMarkUndef(dest_ptr, len); | | |
| 9826 | } | | |
| 9827 | return .none; | | |
| 9828 | } | | |
| 9829 | | | |
| 9830 | // Test if the element value is compile-time known to be a | | |
| 9831 | // repeating byte pattern, for example, `@as(u64, 0)` has a | | |
| 9832 | // repeating byte pattern of 0 bytes. In such case, the memset | | |
| 9833 | // intrinsic can be used. | | |
| 9834 | if (try elem_val.hasRepeatedByteRepr(pt)) |byte_val| { | | |
| 9835 | const fill_byte = try o.builder.intValue(.i8, byte_val); | | |
| 9836 | const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); | | |
| 9837 | _ = try self.wip.callMemSet( | | |
| 9838 | dest_ptr, | | |
| 9839 | dest_ptr_align, | | |
| 9840 | fill_byte, | | |
| 9841 | len, | | |
| 9842 | access_kind, | | |
| 9843 | self.disable_intrinsics, | | |
| 9844 | ); | | |
| 9845 | return .none; | | |
| 9846 | } | | |
| 9847 | } | | |
| 9848 | | | |
| 9849 | const value = try self.resolveInst(bin_op.rhs); | | |
| 9850 | const elem_abi_size = elem_ty.abiSize(zcu); | | |
| 9851 | | | |
| 9852 | if (elem_abi_size == 1) { | | |
| 9853 | // In this case we can take advantage of LLVM's intrinsic. | | |
| 9854 | const fill_byte = try self.bitCast(value, elem_ty, Type.u8); | | |
| 9855 | const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); | | |
| 9856 | | | |
| 9857 | _ = try self.wip.callMemSet( | | |
| 9858 | dest_ptr, | | |
| 9859 | dest_ptr_align, | | |
| 9860 | fill_byte, | | |
| 9861 | len, | | |
| 9862 | access_kind, | | |
| 9863 | self.disable_intrinsics, | | |
| 9864 | ); | | |
| 9865 | return .none; | | |
| 9866 | } | | |
| 9867 | | | |
| 9868 | // non-byte-sized element. lower with a loop. something like this: | | |
| 9869 | | | |
| 9870 | // entry: | | |
| 9871 | // ... | | |
| 9872 | // %end_ptr = getelementptr %ptr, %len | | |
| 9873 | // br %loop | | |
| 9874 | // loop: | | |
| 9875 | // %it_ptr = phi body %next_ptr, entry %ptr | | |
| 9876 | // %end = cmp eq %it_ptr, %end_ptr | | |
| 9877 | // br %end, %body, %end | | |
| 9878 | // body: | | |
| 9879 | // store %it_ptr, %value | | |
| 9880 | // %next_ptr = getelementptr %it_ptr, 1 | | |
| 9881 | // br %loop | | |
| 9882 | // end: | | |
| 9883 | // ... | | |
| 9884 | const entry_block = self.wip.cursor.block; | | |
| 9885 | const loop_block = try self.wip.block(2, "InlineMemsetLoop"); | | |
| 9886 | const body_block = try self.wip.block(1, "InlineMemsetBody"); | | |
| 9887 | const end_block = try self.wip.block(1, "InlineMemsetEnd"); | | |
| 9888 | | | |
| 9889 | const llvm_usize_ty = try o.lowerType(pt, Type.usize); | | |
| 9890 | const len = switch (ptr_ty.ptrSize(zcu)) { | | |
| 9891 | .slice => try self.wip.extractValue(dest_slice, &.{1}, ""), | | |
| 9892 | .one => try o.builder.intValue(llvm_usize_ty, ptr_ty.childType(zcu).arrayLen(zcu)), | | |
| 9893 | .many, .c => unreachable, | | |
| 9894 | }; | | |
| 9895 | const elem_llvm_ty = try o.lowerType(pt, elem_ty); | | |
| 9896 | const end_ptr = try self.wip.gep(.inbounds, elem_llvm_ty, dest_ptr, &.{len}, ""); | | |
| 9897 | _ = try self.wip.br(loop_block); | | |
| 9898 | | | |
| 9899 | self.wip.cursor = .{ .block = loop_block }; | | |
| 9900 | const it_ptr = try self.wip.phi(.ptr, ""); | | |
| 9901 | const end = try self.wip.icmp(.ne, it_ptr.toValue(), end_ptr, ""); | | |
| 9902 | _ = try self.wip.brCond(end, body_block, end_block, .none); | | |
| 9903 | | | |
| 9904 | self.wip.cursor = .{ .block = body_block }; | | |
| 9905 | const elem_abi_align = elem_ty.abiAlignment(zcu); | | |
| 9906 | const it_ptr_align = InternPool.Alignment.fromLlvm(dest_ptr_align).min(elem_abi_align).toLlvm(); | | |
| 9907 | if (isByRef(elem_ty, zcu)) { | | |
| 9908 | _ = try self.wip.callMemCpy( | | |
| 9909 | it_ptr.toValue(), | | |
| 9910 | it_ptr_align, | | |
| 9911 | value, | | |
| 9912 | elem_abi_align.toLlvm(), | | |
| 9913 | try o.builder.intValue(llvm_usize_ty, elem_abi_size), | | |
| 9914 | access_kind, | | |
| 9915 | self.disable_intrinsics, | | |
| 9916 | ); | | |
| 9917 | } else _ = try self.wip.store(access_kind, value, it_ptr.toValue(), it_ptr_align); | | |
| 9918 | const next_ptr = try self.wip.gep(.inbounds, elem_llvm_ty, it_ptr.toValue(), &.{ | | |
| 9919 | try o.builder.intValue(llvm_usize_ty, 1), | | |
| 9920 | }, ""); | | |
| 9921 | _ = try self.wip.br(loop_block); | | |
| 9922 | | | |
| 9923 | self.wip.cursor = .{ .block = end_block }; | | |
| 9924 | it_ptr.finish(&.{ next_ptr, dest_ptr }, &.{ body_block, entry_block }, &self.wip); | | |
| 9925 | return .none; | | |
| 9926 | } | | |
| 9927 | | | |
| 9928 | fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 9929 | const pt = self.ng.pt; | | |
| 9930 | const zcu = pt.zcu; | | |
| 9931 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 9932 | const dest_slice = try self.resolveInst(bin_op.lhs); | | |
| 9933 | const dest_ptr_ty = self.typeOf(bin_op.lhs); | | |
| 9934 | const src_slice = try self.resolveInst(bin_op.rhs); | | |
| 9935 | const src_ptr_ty = self.typeOf(bin_op.rhs); | | |
| 9936 | const src_ptr = try self.sliceOrArrayPtr(src_slice, src_ptr_ty); | | |
| 9937 | const len = try self.sliceOrArrayLenInBytes(dest_slice, dest_ptr_ty); | | |
| 9938 | const dest_ptr = try self.sliceOrArrayPtr(dest_slice, dest_ptr_ty); | | |
| 9939 | const access_kind: Builder.MemoryAccessKind = if (src_ptr_ty.isVolatilePtr(zcu) or | | |
| 9940 | dest_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; | | |
| 9941 | | | |
| 9942 | self.maybeMarkAllowZeroAccess(dest_ptr_ty.ptrInfo(zcu)); | | |
| 9943 | self.maybeMarkAllowZeroAccess(src_ptr_ty.ptrInfo(zcu)); | | |
| 9944 | | | |
| 9945 | _ = try self.wip.callMemCpy( | | |
| 9946 | dest_ptr, | | |
| 9947 | dest_ptr_ty.ptrAlignment(zcu).toLlvm(), | | |
| 9948 | src_ptr, | | |
| 9949 | src_ptr_ty.ptrAlignment(zcu).toLlvm(), | | |
| 9950 | len, | | |
| 9951 | access_kind, | | |
| 9952 | self.disable_intrinsics, | | |
| 9953 | ); | | |
| 9954 | return .none; | | |
| 9955 | } | | |
| 9956 | | | |
| 9957 | fn airMemmove(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 9958 | const pt = self.ng.pt; | | |
| 9959 | const zcu = pt.zcu; | | |
| 9960 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 9961 | const dest_slice = try self.resolveInst(bin_op.lhs); | | |
| 9962 | const dest_ptr_ty = self.typeOf(bin_op.lhs); | | |
| 9963 | const src_slice = try self.resolveInst(bin_op.rhs); | | |
| 9964 | const src_ptr_ty = self.typeOf(bin_op.rhs); | | |
| 9965 | const src_ptr = try self.sliceOrArrayPtr(src_slice, src_ptr_ty); | | |
| 9966 | const len = try self.sliceOrArrayLenInBytes(dest_slice, dest_ptr_ty); | | |
| 9967 | const dest_ptr = try self.sliceOrArrayPtr(dest_slice, dest_ptr_ty); | | |
| 9968 | const access_kind: Builder.MemoryAccessKind = if (src_ptr_ty.isVolatilePtr(zcu) or | | |
| 9969 | dest_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; | | |
| 9970 | | | |
| 9971 | _ = try self.wip.callMemMove( | | |
| 9972 | dest_ptr, | | |
| 9973 | dest_ptr_ty.ptrAlignment(zcu).toLlvm(), | | |
| 9974 | src_ptr, | | |
| 9975 | src_ptr_ty.ptrAlignment(zcu).toLlvm(), | | |
| 9976 | len, | | |
| 9977 | access_kind, | | |
| 9978 | ); | | |
| 9979 | return .none; | | |
| 9980 | } | | |
| 9981 | | | |
| 9982 | fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 9983 | const o = self.ng.object; | | |
| 9984 | const pt = self.ng.pt; | | |
| 9985 | const zcu = pt.zcu; | | |
| 9986 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 9987 | const un_ptr_ty = self.typeOf(bin_op.lhs); | | |
| 9988 | const un_ty = un_ptr_ty.childType(zcu); | | |
| 9989 | const layout = un_ty.unionGetLayout(zcu); | | |
| 9990 | if (layout.tag_size == 0) return .none; | | |
| 9991 | | | |
| 9992 | const access_kind: Builder.MemoryAccessKind = | | |
| 9993 | if (un_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; | | |
| 9994 | | | |
| 9995 | self.maybeMarkAllowZeroAccess(un_ptr_ty.ptrInfo(zcu)); | | |
| 9996 | | | |
| 9997 | const union_ptr = try self.resolveInst(bin_op.lhs); | | |
| 9998 | const new_tag = try self.resolveInst(bin_op.rhs); | | |
| 9999 | const union_ptr_align = un_ptr_ty.ptrAlignment(zcu); | | |
| 10000 | if (layout.payload_size == 0) { | | |
| 10001 | _ = try self.wip.store(access_kind, new_tag, union_ptr, union_ptr_align.toLlvm()); | | |
| 10002 | return .none; | | |
| 10003 | } | | |
| 10004 | const tag_index = @intFromBool(layout.tag_align.compare(.lt, layout.payload_align)); | | |
| 10005 | const tag_field_ptr = try self.wip.gepStruct(try o.lowerType(pt, un_ty), union_ptr, tag_index, ""); | | |
| 10006 | const tag_ptr_align: InternPool.Alignment = switch (layout.tagOffset()) { | | |
| 10007 | 0 => union_ptr_align, | | |
| 10008 | else => |off| .minStrict(union_ptr_align, .fromLog2Units(@ctz(off))), | | |
| 10009 | }; | | |
| 10010 | _ = try self.wip.store(access_kind, new_tag, tag_field_ptr, tag_ptr_align.toLlvm()); | | |
| 10011 | return .none; | | |
| 10012 | } | | |
| 10013 | | | |
| 10014 | fn airGetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10015 | const o = self.ng.object; | | |
| 10016 | const pt = self.ng.pt; | | |
| 10017 | const zcu = pt.zcu; | | |
| 10018 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 10019 | const un_ty = self.typeOf(ty_op.operand); | | |
| 10020 | const layout = un_ty.unionGetLayout(zcu); | | |
| 10021 | if (layout.tag_size == 0) return .none; | | |
| 10022 | const union_handle = try self.resolveInst(ty_op.operand); | | |
| 10023 | if (isByRef(un_ty, zcu)) { | | |
| 10024 | const llvm_un_ty = try o.lowerType(pt, un_ty); | | |
| 10025 | if (layout.payload_size == 0) | | |
| 10026 | return self.wip.load(.normal, llvm_un_ty, union_handle, .default, ""); | | |
| 10027 | const tag_index = @intFromBool(layout.tag_align.compare(.lt, layout.payload_align)); | | |
| 10028 | const tag_field_ptr = try self.wip.gepStruct(llvm_un_ty, union_handle, tag_index, ""); | | |
| 10029 | const llvm_tag_ty = llvm_un_ty.structFields(&o.builder)[tag_index]; | | |
| 10030 | return self.wip.load(.normal, llvm_tag_ty, tag_field_ptr, .default, ""); | | |
| 10031 | } else { | | |
| 10032 | if (layout.payload_size == 0) return union_handle; | | |
| 10033 | const tag_index = @intFromBool(layout.tag_align.compare(.lt, layout.payload_align)); | | |
| 10034 | return self.wip.extractValue(union_handle, &.{tag_index}, ""); | | |
| 10035 | } | | |
| 10036 | } | | |
| 10037 | | | |
| 10038 | fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, comptime op: FloatOp) !Builder.Value { | | |
| 10039 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | | |
| 10040 | const operand = try self.resolveInst(un_op); | | |
| 10041 | const operand_ty = self.typeOf(un_op); | | |
| 10042 | | | |
| 10043 | return self.buildFloatOp(op, .normal, operand_ty, 1, .{operand}); | | |
| 10044 | } | | |
| 10045 | | | |
| 10046 | fn airNeg(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) !Builder.Value { | | |
| 10047 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | | |
| 10048 | const operand = try self.resolveInst(un_op); | | |
| 10049 | const operand_ty = self.typeOf(un_op); | | |
| 10050 | | | |
| 10051 | return self.buildFloatOp(.neg, fast, operand_ty, 1, .{operand}); | | |
| 10052 | } | | |
| 10053 | | | |
| 10054 | fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, intrinsic: Builder.Intrinsic) !Builder.Value { | | |
| 10055 | const o = self.ng.object; | | |
| 10056 | const pt = self.ng.pt; | | |
| 10057 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 10058 | const inst_ty = self.typeOfIndex(inst); | | |
| 10059 | const operand_ty = self.typeOf(ty_op.operand); | | |
| 10060 | const operand = try self.resolveInst(ty_op.operand); | | |
| 10061 | | | |
| 10062 | const result = try self.wip.callIntrinsic( | | |
| 10063 | .normal, | | |
| 10064 | .none, | | |
| 10065 | intrinsic, | | |
| 10066 | &.{try o.lowerType(pt, operand_ty)}, | | |
| 10067 | &.{ operand, .false }, | | |
| 10068 | "", | | |
| 10069 | ); | | |
| 10070 | return self.wip.conv(.unsigned, result, try o.lowerType(pt, inst_ty), ""); | | |
| 10071 | } | | |
| 10072 | | | |
| 10073 | fn airBitOp(self: *FuncGen, inst: Air.Inst.Index, intrinsic: Builder.Intrinsic) !Builder.Value { | | |
| 10074 | const o = self.ng.object; | | |
| 10075 | const pt = self.ng.pt; | | |
| 10076 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 10077 | const inst_ty = self.typeOfIndex(inst); | | |
| 10078 | const operand_ty = self.typeOf(ty_op.operand); | | |
| 10079 | const operand = try self.resolveInst(ty_op.operand); | | |
| 10080 | | | |
| 10081 | const result = try self.wip.callIntrinsic( | | |
| 10082 | .normal, | | |
| 10083 | .none, | | |
| 10084 | intrinsic, | | |
| 10085 | &.{try o.lowerType(pt, operand_ty)}, | | |
| 10086 | &.{operand}, | | |
| 10087 | "", | | |
| 10088 | ); | | |
| 10089 | return self.wip.conv(.unsigned, result, try o.lowerType(pt, inst_ty), ""); | | |
| 10090 | } | | |
| 10091 | | | |
| 10092 | fn airByteSwap(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10093 | const o = self.ng.object; | | |
| 10094 | const pt = self.ng.pt; | | |
| 10095 | const zcu = pt.zcu; | | |
| 10096 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 10097 | const operand_ty = self.typeOf(ty_op.operand); | | |
| 10098 | var bits = operand_ty.intInfo(zcu).bits; | | |
| 10099 | assert(bits % 8 == 0); | | |
| 10100 | | | |
| 10101 | const inst_ty = self.typeOfIndex(inst); | | |
| 10102 | var operand = try self.resolveInst(ty_op.operand); | | |
| 10103 | var llvm_operand_ty = try o.lowerType(pt, operand_ty); | | |
| 10104 | | | |
| 10105 | if (bits % 16 == 8) { | | |
| 10106 | // If not an even byte-multiple, we need zero-extend + shift-left 1 byte | | |
| 10107 | // The truncated result at the end will be the correct bswap | | |
| 10108 | const scalar_ty = try o.builder.intType(@intCast(bits + 8)); | | |
| 10109 | if (operand_ty.zigTypeTag(zcu) == .vector) { | | |
| 10110 | const vec_len = operand_ty.vectorLen(zcu); | | |
| 10111 | llvm_operand_ty = try o.builder.vectorType(.normal, vec_len, scalar_ty); | | |
| 10112 | } else llvm_operand_ty = scalar_ty; | | |
| 10113 | | | |
| 10114 | const shift_amt = | | |
| 10115 | try o.builder.splatValue(llvm_operand_ty, try o.builder.intConst(scalar_ty, 8)); | | |
| 10116 | const extended = try self.wip.cast(.zext, operand, llvm_operand_ty, ""); | | |
| 10117 | operand = try self.wip.bin(.shl, extended, shift_amt, ""); | | |
| 10118 | | | |
| 10119 | bits = bits + 8; | | |
| 10120 | } | | |
| 10121 | | | |
| 10122 | const result = | | |
| 10123 | try self.wip.callIntrinsic(.normal, .none, .bswap, &.{llvm_operand_ty}, &.{operand}, ""); | | |
| 10124 | return self.wip.conv(.unsigned, result, try o.lowerType(pt, inst_ty), ""); | | |
| 10125 | } | | |
| 10126 | | | |
| 10127 | fn airErrorSetHasValue(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10128 | const o = self.ng.object; | | |
| 10129 | const pt = self.ng.pt; | | |
| 10130 | const zcu = pt.zcu; | | |
| 10131 | const ip = &zcu.intern_pool; | | |
| 10132 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 10133 | const operand = try self.resolveInst(ty_op.operand); | | |
| 10134 | const error_set_ty = ty_op.ty.toType(); | | |
| 10135 | | | |
| 10136 | const names = error_set_ty.errorSetNames(zcu); | | |
| 10137 | const valid_block = try self.wip.block(@intCast(names.len), "Valid"); | | |
| 10138 | const invalid_block = try self.wip.block(1, "Invalid"); | | |
| 10139 | const end_block = try self.wip.block(2, "End"); | | |
| 10140 | var wip_switch = try self.wip.@"switch"(operand, invalid_block, @intCast(names.len), .none); | | |
| 10141 | defer wip_switch.finish(&self.wip); | | |
| 10142 | | | |
| 10143 | for (0..names.len) |name_index| { | | |
| 10144 | const err_int = ip.getErrorValueIfExists(names.get(ip)[name_index]).?; | | |
| 10145 | const this_tag_int_value = try o.builder.intConst(try o.errorIntType(pt), err_int); | | |
| 10146 | try wip_switch.addCase(this_tag_int_value, valid_block, &self.wip); | | |
| 10147 | } | | |
| 10148 | self.wip.cursor = .{ .block = valid_block }; | | |
| 10149 | _ = try self.wip.br(end_block); | | |
| 10150 | | | |
| 10151 | self.wip.cursor = .{ .block = invalid_block }; | | |
| 10152 | _ = try self.wip.br(end_block); | | |
| 10153 | | | |
| 10154 | self.wip.cursor = .{ .block = end_block }; | | |
| 10155 | const phi = try self.wip.phi(.i1, ""); | | |
| 10156 | phi.finish(&.{ .true, .false }, &.{ valid_block, invalid_block }, &self.wip); | | |
| 10157 | return phi.toValue(); | | |
| 10158 | } | | |
| 10159 | | | |
| 10160 | fn airIsNamedEnumValue(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10161 | const o = self.ng.object; | | |
| 10162 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | | |
| 10163 | const operand = try self.resolveInst(un_op); | | |
| 10164 | const enum_ty = self.typeOf(un_op); | | |
| 10165 | | | |
| 10166 | const llvm_fn = try self.getIsNamedEnumValueFunction(enum_ty); | | |
| 10167 | return self.wip.call( | | |
| 10168 | .normal, | | |
| 10169 | .fastcc, | | |
| 10170 | .none, | | |
| 10171 | llvm_fn.typeOf(&o.builder), | | |
| 10172 | llvm_fn.toValue(&o.builder), | | |
| 10173 | &.{operand}, | | |
| 10174 | "", | | |
| 10175 | ); | | |
| 10176 | } | | |
| 10177 | | | |
| 10178 | fn getIsNamedEnumValueFunction(self: *FuncGen, enum_ty: Type) !Builder.Function.Index { | | |
| 10179 | const o = self.ng.object; | | |
| 10180 | const pt = self.ng.pt; | | |
| 10181 | const zcu = pt.zcu; | | |
| 10182 | const ip = &zcu.intern_pool; | | |
| 10183 | | | |
| 10184 | const gop = try o.named_enum_map.getOrPut(o.gpa, enum_ty.toIntern()); | | |
| 10185 | if (gop.found_existing) return gop.value_ptr.*; | | |
| 10186 | errdefer assert(o.named_enum_map.remove(enum_ty.toIntern())); | | |
| 10187 | const function_index = try o.builder.addFunction( | | |
| 10188 | // Dummy function type; `updateIsNamedEnumValue` will replace it with the correct type. | | |
| 10189 | // TODO: change the builder API so we don't need to do this. | | |
| 10190 | try o.builder.fnType(.void, &.{}, .normal), | | |
| 10191 | try o.builder.strtabStringFmt("__zig_is_named_enum_value_{f}", .{enum_ty.containerTypeName(ip).fmt(ip)}), | | |
| 10192 | toLlvmAddressSpace(.generic, zcu.getTarget()), | | |
| 10193 | ); | | |
| 10194 | gop.value_ptr.* = function_index; | | |
| 10195 | try o.updateIsNamedEnumValueFunction(pt, enum_ty, function_index); | | |
| 10196 | return function_index; | | |
| 10197 | } | | |
| 10198 | | | |
| 10199 | fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10200 | const o = self.ng.object; | | |
| 10201 | const pt = self.ng.pt; | | |
| 10202 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | | |
| 10203 | const operand = try self.resolveInst(un_op); | | |
| 10204 | const enum_ty = self.typeOf(un_op); | | |
| 10205 | | | |
| 10206 | const llvm_fn = try o.getEnumTagNameFunction(pt, enum_ty); | | |
| 10207 | return self.wip.call( | | |
| 10208 | .normal, | | |
| 10209 | .fastcc, | | |
| 10210 | .none, | | |
| 10211 | llvm_fn.typeOf(&o.builder), | | |
| 10212 | llvm_fn.toValue(&o.builder), | | |
| 10213 | &.{operand}, | | |
| 10214 | "", | | |
| 10215 | ); | | |
| 10216 | } | | |
| 10217 | | | |
| 10218 | fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10219 | const o = self.ng.object; | | |
| 10220 | const pt = self.ng.pt; | | |
| 10221 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | | |
| 10222 | const operand = try self.resolveInst(un_op); | | |
| 10223 | const slice_ty = self.typeOfIndex(inst); | | |
| 10224 | const slice_llvm_ty = try o.lowerType(pt, slice_ty); | | |
| 10225 | | | |
| 10226 | // If operand is small (e.g. `u8`), then signedness becomes a problem -- GEP always treats the index as signed. | | |
| 10227 | const extended_operand = try self.wip.conv(.unsigned, operand, try o.lowerType(pt, .usize), ""); | | |
| 10228 | | | |
| 10229 | const error_name_table_ptr = try self.getErrorNameTable(); | | |
| 10230 | const error_name_table = | | |
| 10231 | try self.wip.load(.normal, .ptr, error_name_table_ptr.toValue(&o.builder), .default, ""); | | |
| 10232 | const error_name_ptr = | | |
| 10233 | try self.wip.gep(.inbounds, slice_llvm_ty, error_name_table, &.{extended_operand}, ""); | | |
| 10234 | return self.wip.load(.normal, slice_llvm_ty, error_name_ptr, .default, ""); | | |
| 10235 | } | | |
| 10236 | | | |
| 10237 | fn airSplat(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10238 | const o = self.ng.object; | | |
| 10239 | const pt = self.ng.pt; | | |
| 10240 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 10241 | const scalar = try self.resolveInst(ty_op.operand); | | |
| 10242 | const vector_ty = self.typeOfIndex(inst); | | |
| 10243 | return self.wip.splatVector(try o.lowerType(pt, vector_ty), scalar, ""); | | |
| 10244 | } | | |
| 10245 | | | |
| 10246 | fn airSelect(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10247 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | | |
| 10248 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; | | |
| 10249 | const pred = try self.resolveInst(pl_op.operand); | | |
| 10250 | const a = try self.resolveInst(extra.lhs); | | |
| 10251 | const b = try self.resolveInst(extra.rhs); | | |
| 10252 | | | |
| 10253 | return self.wip.select(.normal, pred, a, b, ""); | | |
| 10254 | } | | |
| 10255 | | | |
| 10256 | fn airShuffleOne(fg: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10257 | const o = fg.ng.object; | | |
| 10258 | const pt = fg.ng.pt; | | |
| 10259 | const zcu = pt.zcu; | | |
| 10260 | const gpa = zcu.gpa; | | |
| 10261 | | | |
| 10262 | const unwrapped = fg.air.unwrapShuffleOne(zcu, inst); | | |
| 10263 | | | |
| 10264 | const operand = try fg.resolveInst(unwrapped.operand); | | |
| 10265 | const mask = unwrapped.mask; | | |
| 10266 | const operand_ty = fg.typeOf(unwrapped.operand); | | |
| 10267 | const llvm_operand_ty = try o.lowerType(pt, operand_ty); | | |
| 10268 | const llvm_result_ty = try o.lowerType(pt, unwrapped.result_ty); | | |
| 10269 | const llvm_elem_ty = try o.lowerType(pt, unwrapped.result_ty.childType(zcu)); | | |
| 10270 | const llvm_poison_elem = try o.builder.poisonConst(llvm_elem_ty); | | |
| 10271 | const llvm_poison_mask_elem = try o.builder.poisonConst(.i32); | | |
| 10272 | const llvm_mask_ty = try o.builder.vectorType(.normal, @intCast(mask.len), .i32); | | |
| 10273 | | | |
| 10274 | // LLVM requires that the two input vectors have the same length, so lowering isn't trivial. | | |
| 10275 | // And, in the words of jacobly0: "llvm sucks at shuffles so we do have to hold its hand at | | |
| 10276 | // least a bit". So, there are two cases here. | | |
| 10277 | // | | |
| 10278 | // If the operand length equals the mask length, we do just the one `shufflevector`, where | | |
| 10279 | // the second operand is a constant vector with comptime-known elements at the right indices | | |
| 10280 | // and poison values elsewhere (in the indices which won't be selected). | | |
| 10281 | // | | |
| 10282 | // Otherwise, we lower to *two* `shufflevector` instructions. The first shuffles the runtime | | |
| 10283 | // operand with an all-poison vector to extract and correctly position all of the runtime | | |
| 10284 | // elements. We also make a constant vector with all of the comptime elements correctly | | |
| 10285 | // positioned. Then, our second instruction selects elements from those "runtime-or-poison" | | |
| 10286 | // and "comptime-or-poison" vectors to compute the result. | | |
| 10287 | | | |
| 10288 | // This buffer is used primarily for the mask constants. | | |
| 10289 | const llvm_elem_buf = try gpa.alloc(Builder.Constant, mask.len); | | |
| 10290 | defer gpa.free(llvm_elem_buf); | | |
| 10291 | | | |
| 10292 | // ...but first, we'll collect all of the comptime-known values. | | |
| 10293 | var any_defined_comptime_value = false; | | |
| 10294 | for (mask, llvm_elem_buf) |mask_elem, *llvm_elem| { | | |
| 10295 | llvm_elem.* = switch (mask_elem.unwrap()) { | | |
| 10296 | .elem => llvm_poison_elem, | | |
| 10297 | .value => |val| if (!Value.fromInterned(val).isUndef(zcu)) elem: { | | |
| 10298 | any_defined_comptime_value = true; | | |
| 10299 | break :elem try o.lowerValue(pt, val); | | |
| 10300 | } else llvm_poison_elem, | | |
| 10301 | }; | | |
| 10302 | } | | |
| 10303 | // This vector is like the result, but runtime elements are replaced with poison. | | |
| 10304 | const comptime_and_poison: Builder.Value = if (any_defined_comptime_value) vec: { | | |
| 10305 | break :vec try o.builder.vectorValue(llvm_result_ty, llvm_elem_buf); | | |
| 10306 | } else try o.builder.poisonValue(llvm_result_ty); | | |
| 10307 | | | |
| 10308 | if (operand_ty.vectorLen(zcu) == mask.len) { | | |
| 10309 | // input length equals mask/output length, so we lower to one instruction | | |
| 10310 | for (mask, llvm_elem_buf, 0..) |mask_elem, *llvm_elem, elem_idx| { | | |
| 10311 | llvm_elem.* = switch (mask_elem.unwrap()) { | | |
| 10312 | .elem => |idx| try o.builder.intConst(.i32, idx), | | |
| 10313 | .value => |val| if (!Value.fromInterned(val).isUndef(zcu)) mask_val: { | | |
| 10314 | break :mask_val try o.builder.intConst(.i32, mask.len + elem_idx); | | |
| 10315 | } else llvm_poison_mask_elem, | | |
| 10316 | }; | | |
| 10317 | } | | |
| 10318 | return fg.wip.shuffleVector( | | |
| 10319 | operand, | | |
| 10320 | comptime_and_poison, | | |
| 10321 | try o.builder.vectorValue(llvm_mask_ty, llvm_elem_buf), | | |
| 10322 | "", | | |
| 10323 | ); | | |
| 10324 | } | | |
| 10325 | | | |
| 10326 | for (mask, llvm_elem_buf) |mask_elem, *llvm_elem| { | | |
| 10327 | llvm_elem.* = switch (mask_elem.unwrap()) { | | |
| 10328 | .elem => |idx| try o.builder.intConst(.i32, idx), | | |
| 10329 | .value => llvm_poison_mask_elem, | | |
| 10330 | }; | | |
| 10331 | } | | |
| 10332 | // This vector is like our result, but all comptime-known elements are poison. | | |
| 10333 | const runtime_and_poison = try fg.wip.shuffleVector( | | |
| 10334 | operand, | | |
| 10335 | try o.builder.poisonValue(llvm_operand_ty), | | |
| 10336 | try o.builder.vectorValue(llvm_mask_ty, llvm_elem_buf), | | |
| 10337 | "", | | |
| 10338 | ); | | |
| 10339 | | | |
| 10340 | if (!any_defined_comptime_value) { | | |
| 10341 | // `comptime_and_poison` is just poison; a second shuffle would be a nop. | | |
| 10342 | return runtime_and_poison; | | |
| 10343 | } | | |
| 10344 | | | |
| 10345 | // In this second shuffle, the inputs, the mask, and the output all have the same length. | | |
| 10346 | for (mask, llvm_elem_buf, 0..) |mask_elem, *llvm_elem, elem_idx| { | | |
| 10347 | llvm_elem.* = switch (mask_elem.unwrap()) { | | |
| 10348 | .elem => try o.builder.intConst(.i32, elem_idx), | | |
| 10349 | .value => |val| if (!Value.fromInterned(val).isUndef(zcu)) mask_val: { | | |
| 10350 | break :mask_val try o.builder.intConst(.i32, mask.len + elem_idx); | | |
| 10351 | } else llvm_poison_mask_elem, | | |
| 10352 | }; | | |
| 10353 | } | | |
| 10354 | // Merge the runtime and comptime elements with the mask we just built. | | |
| 10355 | return fg.wip.shuffleVector( | | |
| 10356 | runtime_and_poison, | | |
| 10357 | comptime_and_poison, | | |
| 10358 | try o.builder.vectorValue(llvm_mask_ty, llvm_elem_buf), | | |
| 10359 | "", | | |
| 10360 | ); | | |
| 10361 | } | | |
| 10362 | | | |
| 10363 | fn airShuffleTwo(fg: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10364 | const o = fg.ng.object; | | |
| 10365 | const pt = fg.ng.pt; | | |
| 10366 | const zcu = pt.zcu; | | |
| 10367 | const gpa = zcu.gpa; | | |
| 10368 | | | |
| 10369 | const unwrapped = fg.air.unwrapShuffleTwo(zcu, inst); | | |
| 10370 | | | |
| 10371 | const mask = unwrapped.mask; | | |
| 10372 | const llvm_elem_ty = try o.lowerType(pt, unwrapped.result_ty.childType(zcu)); | | |
| 10373 | const llvm_mask_ty = try o.builder.vectorType(.normal, @intCast(mask.len), .i32); | | |
| 10374 | const llvm_poison_mask_elem = try o.builder.poisonConst(.i32); | | |
| 10375 | | | |
| 10376 | // This is kind of simpler than in `airShuffleOne`. We extend the shorter vector to the | | |
| 10377 | // length of the longer one with an initial `shufflevector` if necessary, and then do the | | |
| 10378 | // actual computation with a second `shufflevector`. | | |
| 10379 | | | |
| 10380 | const operand_a_len = fg.typeOf(unwrapped.operand_a).vectorLen(zcu); | | |
| 10381 | const operand_b_len = fg.typeOf(unwrapped.operand_b).vectorLen(zcu); | | |
| 10382 | const operand_len: u32 = @max(operand_a_len, operand_b_len); | | |
| 10383 | | | |
| 10384 | // If we need to extend an operand, this is the type that mask will have. | | |
| 10385 | const llvm_operand_mask_ty = try o.builder.vectorType(.normal, operand_len, .i32); | | |
| 10386 | | | |
| 10387 | const llvm_elem_buf = try gpa.alloc(Builder.Constant, @max(mask.len, operand_len)); | | |
| 10388 | defer gpa.free(llvm_elem_buf); | | |
| 10389 | | | |
| 10390 | const operand_a: Builder.Value = extend: { | | |
| 10391 | const raw = try fg.resolveInst(unwrapped.operand_a); | | |
| 10392 | if (operand_a_len == operand_len) break :extend raw; | | |
| 10393 | // Extend with a `shufflevector`, with a mask `<0, 1, ..., n, poison, poison, ..., poison>` | | |
| 10394 | const mask_elems = llvm_elem_buf[0..operand_len]; | | |
| 10395 | for (mask_elems[0..operand_a_len], 0..) |*llvm_elem, elem_idx| { | | |
| 10396 | llvm_elem.* = try o.builder.intConst(.i32, elem_idx); | | |
| 10397 | } | | |
| 10398 | @memset(mask_elems[operand_a_len..], llvm_poison_mask_elem); | | |
| 10399 | const llvm_this_operand_ty = try o.builder.vectorType(.normal, operand_a_len, llvm_elem_ty); | | |
| 10400 | break :extend try fg.wip.shuffleVector( | | |
| 10401 | raw, | | |
| 10402 | try o.builder.poisonValue(llvm_this_operand_ty), | | |
| 10403 | try o.builder.vectorValue(llvm_operand_mask_ty, mask_elems), | | |
| 10404 | "", | | |
| 10405 | ); | | |
| 10406 | }; | | |
| 10407 | const operand_b: Builder.Value = extend: { | | |
| 10408 | const raw = try fg.resolveInst(unwrapped.operand_b); | | |
| 10409 | if (operand_b_len == operand_len) break :extend raw; | | |
| 10410 | // Extend with a `shufflevector`, with a mask `<0, 1, ..., n, poison, poison, ..., poison>` | | |
| 10411 | const mask_elems = llvm_elem_buf[0..operand_len]; | | |
| 10412 | for (mask_elems[0..operand_b_len], 0..) |*llvm_elem, elem_idx| { | | |
| 10413 | llvm_elem.* = try o.builder.intConst(.i32, elem_idx); | | |
| 10414 | } | | |
| 10415 | @memset(mask_elems[operand_b_len..], llvm_poison_mask_elem); | | |
| 10416 | const llvm_this_operand_ty = try o.builder.vectorType(.normal, operand_b_len, llvm_elem_ty); | | |
| 10417 | break :extend try fg.wip.shuffleVector( | | |
| 10418 | raw, | | |
| 10419 | try o.builder.poisonValue(llvm_this_operand_ty), | | |
| 10420 | try o.builder.vectorValue(llvm_operand_mask_ty, mask_elems), | | |
| 10421 | "", | | |
| 10422 | ); | | |
| 10423 | }; | | |
| 10424 | | | |
| 10425 | // `operand_a` and `operand_b` now have the same length (we've extended the shorter one with | | |
| 10426 | // an initial shuffle if necessary). Now for the easy bit. | | |
| 10427 | | | |
| 10428 | const mask_elems = llvm_elem_buf[0..mask.len]; | | |
| 10429 | for (mask, mask_elems) |mask_elem, *llvm_mask_elem| { | | |
| 10430 | llvm_mask_elem.* = switch (mask_elem.unwrap()) { | | |
| 10431 | .a_elem => |idx| try o.builder.intConst(.i32, idx), | | |
| 10432 | .b_elem => |idx| try o.builder.intConst(.i32, operand_len + idx), | | |
| 10433 | .undef => llvm_poison_mask_elem, | | |
| 10434 | }; | | |
| 10435 | } | | |
| 10436 | return fg.wip.shuffleVector( | | |
| 10437 | operand_a, | | |
| 10438 | operand_b, | | |
| 10439 | try o.builder.vectorValue(llvm_mask_ty, mask_elems), | | |
| 10440 | "", | | |
| 10441 | ); | | |
| 10442 | } | | |
| 10443 | | | |
| 10444 | /// Reduce a vector by repeatedly applying `llvm_fn` to produce an accumulated result. | | |
| 10445 | /// | | |
| 10446 | /// Equivalent to: | | |
| 10447 | /// reduce: { | | |
| 10448 | /// var i: usize = 0; | | |
| 10449 | /// var accum: T = init; | | |
| 10450 | /// while (i < vec.len) : (i += 1) { | | |
| 10451 | /// accum = llvm_fn(accum, vec[i]); | | |
| 10452 | /// } | | |
| 10453 | /// break :reduce accum; | | |
| 10454 | /// } | | |
| 10455 | /// | | |
| 10456 | fn buildReducedCall( | | |
| 10457 | self: *FuncGen, | | |
| 10458 | llvm_fn: Builder.Function.Index, | | |
| 10459 | operand_vector: Builder.Value, | | |
| 10460 | vector_len: usize, | | |
| 10461 | accum_init: Builder.Value, | | |
| 10462 | ) !Builder.Value { | | |
| 10463 | const o = self.ng.object; | | |
| 10464 | const pt = self.ng.pt; | | |
| 10465 | const usize_ty = try o.lowerType(pt, Type.usize); | | |
| 10466 | const llvm_vector_len = try o.builder.intValue(usize_ty, vector_len); | | |
| 10467 | const llvm_result_ty = accum_init.typeOfWip(&self.wip); | | |
| 10468 | | | |
| 10469 | // Allocate and initialize our mutable variables | | |
| 10470 | const i_ptr = try self.buildAlloca(usize_ty, .default); | | |
| 10471 | _ = try self.wip.store(.normal, try o.builder.intValue(usize_ty, 0), i_ptr, .default); | | |
| 10472 | const accum_ptr = try self.buildAlloca(llvm_result_ty, .default); | | |
| 10473 | _ = try self.wip.store(.normal, accum_init, accum_ptr, .default); | | |
| 10474 | | | |
| 10475 | // Setup the loop | | |
| 10476 | const loop = try self.wip.block(2, "ReduceLoop"); | | |
| 10477 | const loop_exit = try self.wip.block(1, "AfterReduce"); | | |
| 10478 | _ = try self.wip.br(loop); | | |
| 10479 | { | | |
| 10480 | self.wip.cursor = .{ .block = loop }; | | |
| 10481 | | | |
| 10482 | // while (i < vec.len) | | |
| 10483 | const i = try self.wip.load(.normal, usize_ty, i_ptr, .default, ""); | | |
| 10484 | const cond = try self.wip.icmp(.ult, i, llvm_vector_len, ""); | | |
| 10485 | const loop_then = try self.wip.block(1, "ReduceLoopThen"); | | |
| 10486 | | | |
| 10487 | _ = try self.wip.brCond(cond, loop_then, loop_exit, .none); | | |
| 10488 | | | |
| 10489 | { | | |
| 10490 | self.wip.cursor = .{ .block = loop_then }; | | |
| 10491 | | | |
| 10492 | // accum = f(accum, vec[i]); | | |
| 10493 | const accum = try self.wip.load(.normal, llvm_result_ty, accum_ptr, .default, ""); | | |
| 10494 | const element = try self.wip.extractElement(operand_vector, i, ""); | | |
| 10495 | const new_accum = try self.wip.call( | | |
| 10496 | .normal, | | |
| 10497 | .ccc, | | |
| 10498 | .none, | | |
| 10499 | llvm_fn.typeOf(&o.builder), | | |
| 10500 | llvm_fn.toValue(&o.builder), | | |
| 10501 | &.{ accum, element }, | | |
| 10502 | "", | | |
| 10503 | ); | | |
| 10504 | _ = try self.wip.store(.normal, new_accum, accum_ptr, .default); | | |
| 10505 | | | |
| 10506 | // i += 1 | | |
| 10507 | const new_i = try self.wip.bin(.add, i, try o.builder.intValue(usize_ty, 1), ""); | | |
| 10508 | _ = try self.wip.store(.normal, new_i, i_ptr, .default); | | |
| 10509 | _ = try self.wip.br(loop); | | |
| 10510 | } | | |
| 10511 | } | | |
| 10512 | | | |
| 10513 | self.wip.cursor = .{ .block = loop_exit }; | | |
| 10514 | return self.wip.load(.normal, llvm_result_ty, accum_ptr, .default, ""); | | |
| 10515 | } | | |
| 10516 | | | |
| 10517 | fn airReduce(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) !Builder.Value { | | |
| 10518 | const o = self.ng.object; | | |
| 10519 | const pt = self.ng.pt; | | |
| 10520 | const zcu = pt.zcu; | | |
| 10521 | const target = zcu.getTarget(); | | |
| 10522 | | | |
| 10523 | const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce; | | |
| 10524 | const operand = try self.resolveInst(reduce.operand); | | |
| 10525 | const operand_ty = self.typeOf(reduce.operand); | | |
| 10526 | const llvm_operand_ty = try o.lowerType(pt, operand_ty); | | |
| 10527 | const scalar_ty = self.typeOfIndex(inst); | | |
| 10528 | const llvm_scalar_ty = try o.lowerType(pt, scalar_ty); | | |
| 10529 | | | |
| 10530 | switch (reduce.operation) { | | |
| 10531 | .And, .Or, .Xor => return self.wip.callIntrinsic(.normal, .none, switch (reduce.operation) { | | |
| 10532 | .And => .@"vector.reduce.and", | | |
| 10533 | .Or => .@"vector.reduce.or", | | |
| 10534 | .Xor => .@"vector.reduce.xor", | | |
| 10535 | else => unreachable, | | |
| 10536 | }, &.{llvm_operand_ty}, &.{operand}, ""), | | |
| 10537 | .Min, .Max => switch (scalar_ty.zigTypeTag(zcu)) { | | |
| 10538 | .int => return self.wip.callIntrinsic(.normal, .none, switch (reduce.operation) { | | |
| 10539 | .Min => if (scalar_ty.isSignedInt(zcu)) | | |
| 10540 | .@"vector.reduce.smin" | | |
| 10541 | else | | |
| 10542 | .@"vector.reduce.umin", | | |
| 10543 | .Max => if (scalar_ty.isSignedInt(zcu)) | | |
| 10544 | .@"vector.reduce.smax" | | |
| 10545 | else | | |
| 10546 | .@"vector.reduce.umax", | | |
| 10547 | else => unreachable, | | |
| 10548 | }, &.{llvm_operand_ty}, &.{operand}, ""), | | |
| 10549 | .float => if (intrinsicsAllowed(scalar_ty, target)) | | |
| 10550 | return self.wip.callIntrinsic(fast, .none, switch (reduce.operation) { | | |
| 10551 | .Min => .@"vector.reduce.fmin", | | |
| 10552 | .Max => .@"vector.reduce.fmax", | | |
| 10553 | else => unreachable, | | |
| 10554 | }, &.{llvm_operand_ty}, &.{operand}, ""), | | |
| 10555 | else => unreachable, | | |
| 10556 | }, | | |
| 10557 | .Add, .Mul => switch (scalar_ty.zigTypeTag(zcu)) { | | |
| 10558 | .int => return self.wip.callIntrinsic(.normal, .none, switch (reduce.operation) { | | |
| 10559 | .Add => .@"vector.reduce.add", | | |
| 10560 | .Mul => .@"vector.reduce.mul", | | |
| 10561 | else => unreachable, | | |
| 10562 | }, &.{llvm_operand_ty}, &.{operand}, ""), | | |
| 10563 | .float => if (intrinsicsAllowed(scalar_ty, target)) | | |
| 10564 | return self.wip.callIntrinsic(fast, .none, switch (reduce.operation) { | | |
| 10565 | .Add => .@"vector.reduce.fadd", | | |
| 10566 | .Mul => .@"vector.reduce.fmul", | | |
| 10567 | else => unreachable, | | |
| 10568 | }, &.{llvm_operand_ty}, &.{ switch (reduce.operation) { | | |
| 10569 | .Add => try o.builder.fpValue(llvm_scalar_ty, -0.0), | | |
| 10570 | .Mul => try o.builder.fpValue(llvm_scalar_ty, 1.0), | | |
| 10571 | else => unreachable, | | |
| 10572 | }, operand }, ""), | | |
| 10573 | else => unreachable, | | |
| 10574 | }, | | |
| 10575 | } | | |
| 10576 | | | |
| 10577 | // Reduction could not be performed with intrinsics. | | |
| 10578 | // Use a manual loop over a softfloat call instead. | | |
| 10579 | const float_bits = scalar_ty.floatBits(target); | | |
| 10580 | const fn_name = switch (reduce.operation) { | | |
| 10581 | .Min => try o.builder.strtabStringFmt("{s}fmin{s}", .{ | | |
| 10582 | libcFloatPrefix(float_bits), libcFloatSuffix(float_bits), | | |
| 10583 | }), | | |
| 10584 | .Max => try o.builder.strtabStringFmt("{s}fmax{s}", .{ | | |
| 10585 | libcFloatPrefix(float_bits), libcFloatSuffix(float_bits), | | |
| 10586 | }), | | |
| 10587 | .Add => try o.builder.strtabStringFmt("__add{s}f3", .{ | | |
| 10588 | compilerRtFloatAbbrev(float_bits), | | |
| 10589 | }), | | |
| 10590 | .Mul => try o.builder.strtabStringFmt("__mul{s}f3", .{ | | |
| 10591 | compilerRtFloatAbbrev(float_bits), | | |
| 10592 | }), | | |
| 10593 | else => unreachable, | | |
| 10594 | }; | | |
| 10595 | | | |
| 10596 | const libc_fn = | | |
| 10597 | try self.getLibcFunction(fn_name, &.{ llvm_scalar_ty, llvm_scalar_ty }, llvm_scalar_ty); | | |
| 10598 | const init_val = switch (llvm_scalar_ty) { | | |
| 10599 | .i16 => try o.builder.intValue(.i16, @as(i16, @bitCast( | | |
| 10600 | @as(f16, switch (reduce.operation) { | | |
| 10601 | .Min, .Max => std.math.nan(f16), | | |
| 10602 | .Add => -0.0, | | |
| 10603 | .Mul => 1.0, | | |
| 10604 | else => unreachable, | | |
| 10605 | }), | | |
| 10606 | ))), | | |
| 10607 | .i80 => try o.builder.intValue(.i80, @as(i80, @bitCast( | | |
| 10608 | @as(f80, switch (reduce.operation) { | | |
| 10609 | .Min, .Max => std.math.nan(f80), | | |
| 10610 | .Add => -0.0, | | |
| 10611 | .Mul => 1.0, | | |
| 10612 | else => unreachable, | | |
| 10613 | }), | | |
| 10614 | ))), | | |
| 10615 | .i128 => try o.builder.intValue(.i128, @as(i128, @bitCast( | | |
| 10616 | @as(f128, switch (reduce.operation) { | | |
| 10617 | .Min, .Max => std.math.nan(f128), | | |
| 10618 | .Add => -0.0, | | |
| 10619 | .Mul => 1.0, | | |
| 10620 | else => unreachable, | | |
| 10621 | }), | | |
| 10622 | ))), | | |
| 10623 | else => unreachable, | | |
| 10624 | }; | | |
| 10625 | return self.buildReducedCall(libc_fn, operand, operand_ty.vectorLen(zcu), init_val); | | |
| 10626 | } | | |
| 10627 | | | |
| 10628 | fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10629 | const o = self.ng.object; | | |
| 10630 | const pt = self.ng.pt; | | |
| 10631 | const zcu = pt.zcu; | | |
| 10632 | const ip = &zcu.intern_pool; | | |
| 10633 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | | |
| 10634 | const result_ty = self.typeOfIndex(inst); | | |
| 10635 | const len: usize = @intCast(result_ty.arrayLen(zcu)); | | |
| 10636 | const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[ty_pl.payload..][0..len]); | | |
| 10637 | const llvm_result_ty = try o.lowerType(pt, result_ty); | | |
| 10638 | | | |
| 10639 | switch (result_ty.zigTypeTag(zcu)) { | | |
| 10640 | .vector => { | | |
| 10641 | var vector = try o.builder.poisonValue(llvm_result_ty); | | |
| 10642 | for (elements, 0..) |elem, i| { | | |
| 10643 | const index_u32 = try o.builder.intValue(.i32, i); | | |
| 10644 | const llvm_elem = try self.resolveInst(elem); | | |
| 10645 | vector = try self.wip.insertElement(vector, llvm_elem, index_u32, ""); | | |
| 10646 | } | | |
| 10647 | return vector; | | |
| 10648 | }, | | |
| 10649 | .@"struct" => { | | |
| 10650 | if (zcu.typeToPackedStruct(result_ty)) |struct_type| { | | |
| 10651 | const backing_int_ty: Type = .fromInterned(struct_type.packed_backing_int_type); | | |
| 10652 | const big_bits = backing_int_ty.bitSize(zcu); | | |
| 10653 | const int_ty = try o.builder.intType(@intCast(big_bits)); | | |
| 10654 | comptime assert(Type.packed_struct_layout_version == 2); | | |
| 10655 | var running_int = try o.builder.intValue(int_ty, 0); | | |
| 10656 | var running_bits: u16 = 0; | | |
| 10657 | for (elements, struct_type.field_types.get(ip)) |elem, field_ty| { | | |
| 10658 | if (!Type.fromInterned(field_ty).hasRuntimeBits(zcu)) continue; | | |
| 10659 | | | |
| 10660 | const non_int_val = try self.resolveInst(elem); | | |
| 10661 | const ty_bit_size: u16 = @intCast(Type.fromInterned(field_ty).bitSize(zcu)); | | |
| 10662 | const small_int_ty = try o.builder.intType(ty_bit_size); | | |
| 10663 | const small_int_val = if (Type.fromInterned(field_ty).isPtrAtRuntime(zcu)) | | |
| 10664 | try self.wip.cast(.ptrtoint, non_int_val, small_int_ty, "") | | |
| 10665 | else | | |
| 10666 | try self.wip.cast(.bitcast, non_int_val, small_int_ty, ""); | | |
| 10667 | const shift_rhs = try o.builder.intValue(int_ty, running_bits); | | |
| 10668 | const extended_int_val = | | |
| 10669 | try self.wip.conv(.unsigned, small_int_val, int_ty, ""); | | |
| 10670 | const shifted = try self.wip.bin(.shl, extended_int_val, shift_rhs, ""); | | |
| 10671 | running_int = try self.wip.bin(.@"or", running_int, shifted, ""); | | |
| 10672 | running_bits += ty_bit_size; | | |
| 10673 | } | | |
| 10674 | return running_int; | | |
| 10675 | } | | |
| 10676 | | | |
| 10677 | assert(result_ty.containerLayout(zcu) != .@"packed"); | | |
| 10678 | | | |
| 10679 | if (isByRef(result_ty, zcu)) { | | |
| 10680 | // TODO in debug builds init to undef so that the padding will be 0xaa | | |
| 10681 | // even if we fully populate the fields. | | |
| 10682 | const alignment = result_ty.abiAlignment(zcu).toLlvm(); | | |
| 10683 | const alloca_inst = try self.buildAlloca(llvm_result_ty, alignment); | | |
| 10684 | | | |
| 10685 | for (elements, 0..) |elem, i| { | | |
| 10686 | if ((try result_ty.structFieldValueComptime(pt, i)) != null) continue; | | |
| 10687 | | | |
| 10688 | const llvm_elem = try self.resolveInst(elem); | | |
| 10689 | const llvm_i = o.llvmFieldIndex(result_ty, i).?; | | |
| 10690 | const field_ptr = try self.wip.gepStruct(llvm_result_ty, alloca_inst, llvm_i, ""); | | |
| 10691 | | | |
| 10692 | const field_ptr_ty = try pt.ptrType(.{ | | |
| 10693 | .child = self.typeOf(elem).toIntern(), | | |
| 10694 | .flags = .{ | | |
| 10695 | .alignment = result_ty.explicitFieldAlignment(i, zcu), | | |
| 10696 | }, | | |
| 10697 | }); | | |
| 10698 | try self.store(field_ptr, field_ptr_ty, llvm_elem, .none); | | |
| 10699 | } | | |
| 10700 | | | |
| 10701 | return alloca_inst; | | |
| 10702 | } else { | | |
| 10703 | var result = try o.builder.poisonValue(llvm_result_ty); | | |
| 10704 | for (elements, 0..) |elem, i| { | | |
| 10705 | if ((try result_ty.structFieldValueComptime(pt, i)) != null) continue; | | |
| 10706 | | | |
| 10707 | const llvm_elem = try self.resolveInst(elem); | | |
| 10708 | const llvm_i = o.llvmFieldIndex(result_ty, i).?; | | |
| 10709 | result = try self.wip.insertValue(result, llvm_elem, &.{llvm_i}, ""); | | |
| 10710 | } | | |
| 10711 | return result; | | |
| 10712 | } | | |
| 10713 | }, | | |
| 10714 | .array => { | | |
| 10715 | assert(isByRef(result_ty, zcu)); | | |
| 10716 | | | |
| 10717 | const llvm_usize = try o.lowerType(pt, Type.usize); | | |
| 10718 | const usize_zero = try o.builder.intValue(llvm_usize, 0); | | |
| 10719 | const alignment = result_ty.abiAlignment(zcu).toLlvm(); | | |
| 10720 | const alloca_inst = try self.buildAlloca(llvm_result_ty, alignment); | | |
| 10721 | | | |
| 10722 | const array_info = result_ty.arrayInfo(zcu); | | |
| 10723 | const elem_ptr_ty = try pt.ptrType(.{ | | |
| 10724 | .child = array_info.elem_type.toIntern(), | | |
| 10725 | }); | | |
| 10726 | | | |
| 10727 | for (elements, 0..) |elem, i| { | | |
| 10728 | const elem_ptr = try self.wip.gep(.inbounds, llvm_result_ty, alloca_inst, &.{ | | |
| 10729 | usize_zero, try o.builder.intValue(llvm_usize, i), | | |
| 10730 | }, ""); | | |
| 10731 | const llvm_elem = try self.resolveInst(elem); | | |
| 10732 | try self.store(elem_ptr, elem_ptr_ty, llvm_elem, .none); | | |
| 10733 | } | | |
| 10734 | if (array_info.sentinel) |sent_val| { | | |
| 10735 | const elem_ptr = try self.wip.gep(.inbounds, llvm_result_ty, alloca_inst, &.{ | | |
| 10736 | usize_zero, try o.builder.intValue(llvm_usize, array_info.len), | | |
| 10737 | }, ""); | | |
| 10738 | const llvm_elem = try self.resolveValue(sent_val); | | |
| 10739 | try self.store(elem_ptr, elem_ptr_ty, llvm_elem.toValue(), .none); | | |
| 10740 | } | | |
| 10741 | | | |
| 10742 | return alloca_inst; | | |
| 10743 | }, | | |
| 10744 | else => unreachable, | | |
| 10745 | } | | |
| 10746 | } | | |
| 10747 | | | |
| 10748 | fn airUnionInit(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10749 | const o = self.ng.object; | | |
| 10750 | const pt = self.ng.pt; | | |
| 10751 | const zcu = pt.zcu; | | |
| 10752 | const ip = &zcu.intern_pool; | | |
| 10753 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | | |
| 10754 | const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; | | |
| 10755 | const union_ty = self.typeOfIndex(inst); | | |
| 10756 | const union_llvm_ty = try o.lowerType(pt, union_ty); | | |
| 10757 | const union_obj = zcu.typeToUnion(union_ty).?; | | |
| 10758 | | | |
| 10759 | assert(union_obj.layout != .@"packed"); | | |
| 10760 | | | |
| 10761 | const layout = Type.getUnionLayout(union_obj, zcu); | | |
| 10762 | | | |
| 10763 | const tag_int_val = blk: { | | |
| 10764 | const tag_ty = union_ty.unionTagTypeHypothetical(zcu); | | |
| 10765 | const tag_val = try pt.enumValueFieldIndex(tag_ty, extra.field_index); | | |
| 10766 | break :blk tag_val.intFromEnum(zcu); | | |
| 10767 | }; | | |
| 10768 | if (layout.payload_size == 0) { | | |
| 10769 | if (layout.tag_size == 0) { | | |
| 10770 | return .none; | | |
| 10771 | } | | |
| 10772 | assert(!isByRef(union_ty, zcu)); | | |
| 10773 | var big_int_space: Value.BigIntSpace = undefined; | | |
| 10774 | const tag_big_int = tag_int_val.toBigInt(&big_int_space, zcu); | | |
| 10775 | return try o.builder.bigIntValue(union_llvm_ty, tag_big_int); | | |
| 10776 | } | | |
| 10777 | assert(isByRef(union_ty, zcu)); | | |
| 10778 | // The llvm type of the alloca will be the named LLVM union type, and will not | | |
| 10779 | // necessarily match the format that we need, depending on which tag is active. | | |
| 10780 | // We must construct the correct unnamed struct type here, in order to then set | | |
| 10781 | // the fields appropriately. | | |
| 10782 | const alignment = layout.abi_align.toLlvm(); | | |
| 10783 | const result_ptr = try self.buildAlloca(union_llvm_ty, alignment); | | |
| 10784 | const llvm_payload = try self.resolveInst(extra.init); | | |
| 10785 | const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]); | | |
| 10786 | const field_llvm_ty = try o.lowerType(pt, field_ty); | | |
| 10787 | const field_size = field_ty.abiSize(zcu); | | |
| 10788 | const field_align = union_ty.explicitFieldAlignment(extra.field_index, zcu); | | |
| 10789 | const llvm_usize = try o.lowerType(pt, Type.usize); | | |
| 10790 | const usize_zero = try o.builder.intValue(llvm_usize, 0); | | |
| 10791 | | | |
| 10792 | assert(field_ty.hasRuntimeBits(zcu)); | | |
| 10793 | | | |
| 10794 | const llvm_union_ty = t: { | | |
| 10795 | const payload_ty = p: { | | |
| 10796 | if (field_size == layout.payload_size) { | | |
| 10797 | break :p field_llvm_ty; | | |
| 10798 | } | | |
| 10799 | const padding_len = layout.payload_size - field_size; | | |
| 10800 | break :p try o.builder.structType(.@"packed", &.{ | | |
| 10801 | field_llvm_ty, try o.builder.arrayType(padding_len, .i8), | | |
| 10802 | }); | | |
| 10803 | }; | | |
| 10804 | if (layout.tag_size == 0) break :t try o.builder.structType(.normal, &.{payload_ty}); | | |
| 10805 | const tag_ty = try o.lowerType(pt, .fromInterned(union_obj.enum_tag_type)); | | |
| 10806 | var fields: [3]Builder.Type = undefined; | | |
| 10807 | var fields_len: usize = 2; | | |
| 10808 | if (layout.tag_align.compare(.gte, layout.payload_align)) { | | |
| 10809 | fields = .{ tag_ty, payload_ty, undefined }; | | |
| 10810 | } else { | | |
| 10811 | fields = .{ payload_ty, tag_ty, undefined }; | | |
| 10812 | } | | |
| 10813 | if (layout.padding != 0) { | | |
| 10814 | fields[fields_len] = try o.builder.arrayType(layout.padding, .i8); | | |
| 10815 | fields_len += 1; | | |
| 10816 | } | | |
| 10817 | break :t try o.builder.structType(.normal, fields[0..fields_len]); | | |
| 10818 | }; | | |
| 10819 | | | |
| 10820 | // Now we follow the layout as expressed above with GEP instructions to set the | | |
| 10821 | // tag and the payload. | | |
| 10822 | const field_ptr_ty = try pt.ptrType(.{ | | |
| 10823 | .child = field_ty.toIntern(), | | |
| 10824 | .flags = .{ .alignment = field_align }, | | |
| 10825 | }); | | |
| 10826 | if (layout.tag_size == 0) { | | |
| 10827 | const indices = [3]Builder.Value{ usize_zero, .@"0", .@"0" }; | | |
| 10828 | const len: usize = if (field_size == layout.payload_size) 2 else 3; | | |
| 10829 | const field_ptr = | | |
| 10830 | try self.wip.gep(.inbounds, llvm_union_ty, result_ptr, indices[0..len], ""); | | |
| 10831 | try self.store(field_ptr, field_ptr_ty, llvm_payload, .none); | | |
| 10832 | return result_ptr; | | |
| 10833 | } | | |
| 10834 | | | |
| 10835 | { | | |
| 10836 | const payload_index = @intFromBool(layout.tag_align.compare(.gte, layout.payload_align)); | | |
| 10837 | const indices: [3]Builder.Value = .{ usize_zero, try o.builder.intValue(.i32, payload_index), .@"0" }; | | |
| 10838 | const len: usize = if (field_size == layout.payload_size) 2 else 3; | | |
| 10839 | const field_ptr = try self.wip.gep(.inbounds, llvm_union_ty, result_ptr, indices[0..len], ""); | | |
| 10840 | try self.store(field_ptr, field_ptr_ty, llvm_payload, .none); | | |
| 10841 | } | | |
| 10842 | { | | |
| 10843 | const tag_index = @intFromBool(layout.tag_align.compare(.lt, layout.payload_align)); | | |
| 10844 | const indices: [2]Builder.Value = .{ usize_zero, try o.builder.intValue(.i32, tag_index) }; | | |
| 10845 | const field_ptr = try self.wip.gep(.inbounds, llvm_union_ty, result_ptr, &indices, ""); | | |
| 10846 | const tag_ty = try o.lowerType(pt, .fromInterned(union_obj.enum_tag_type)); | | |
| 10847 | var big_int_space: Value.BigIntSpace = undefined; | | |
| 10848 | const tag_big_int = tag_int_val.toBigInt(&big_int_space, zcu); | | |
| 10849 | const llvm_tag = try o.builder.bigIntValue(tag_ty, tag_big_int); | | |
| 10850 | const tag_alignment = Type.fromInterned(union_obj.enum_tag_type).abiAlignment(zcu).toLlvm(); | | |
| 10851 | _ = try self.wip.store(.normal, llvm_tag, field_ptr, tag_alignment); | | |
| 10852 | } | | |
| 10853 | | | |
| 10854 | return result_ptr; | | |
| 10855 | } | | |
| 10856 | | | |
| 10857 | fn airPrefetch(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10858 | const o = self.ng.object; | | |
| 10859 | const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch; | | |
| 10860 | | | |
| 10861 | comptime assert(@intFromEnum(std.builtin.PrefetchOptions.Rw.read) == 0); | | |
| 10862 | comptime assert(@intFromEnum(std.builtin.PrefetchOptions.Rw.write) == 1); | | |
| 10863 | | | |
| 10864 | comptime assert(prefetch.locality >= 0); | | |
| 10865 | comptime assert(prefetch.locality <= 3); | | |
| 10866 | | | |
| 10867 | comptime assert(@intFromEnum(std.builtin.PrefetchOptions.Cache.instruction) == 0); | | |
| 10868 | comptime assert(@intFromEnum(std.builtin.PrefetchOptions.Cache.data) == 1); | | |
| 10869 | | | |
| 10870 | // LLVM fails during codegen of instruction cache prefetchs for these architectures. | | |
| 10871 | // This is an LLVM bug as the prefetch intrinsic should be a noop if not supported | | |
| 10872 | // by the target. | | |
| 10873 | // To work around this, don't emit llvm.prefetch in this case. | | |
| 10874 | // See https://bugs.llvm.org/show_bug.cgi?id=21037 | | |
| 10875 | const zcu = self.ng.pt.zcu; | | |
| 10876 | const target = zcu.getTarget(); | | |
| 10877 | switch (prefetch.cache) { | | |
| 10878 | .instruction => switch (target.cpu.arch) { | | |
| 10879 | .x86_64, | | |
| 10880 | .x86, | | |
| 10881 | .powerpc, | | |
| 10882 | .powerpcle, | | |
| 10883 | .powerpc64, | | |
| 10884 | .powerpc64le, | | |
| 10885 | => return .none, | | |
| 10886 | .arm, .armeb, .thumb, .thumbeb => { | | |
| 10887 | switch (prefetch.rw) { | | |
| 10888 | .write => return .none, | | |
| 10889 | else => {}, | | |
| 10890 | } | | |
| 10891 | }, | | |
| 10892 | else => {}, | | |
| 10893 | }, | | |
| 10894 | .data => {}, | | |
| 10895 | } | | |
| 10896 | | | |
| 10897 | _ = try self.wip.callIntrinsic(.normal, .none, .prefetch, &.{.ptr}, &.{ | | |
| 10898 | try self.sliceOrArrayPtr(try self.resolveInst(prefetch.ptr), self.typeOf(prefetch.ptr)), | | |
| 10899 | try o.builder.intValue(.i32, prefetch.rw), | | |
| 10900 | try o.builder.intValue(.i32, prefetch.locality), | | |
| 10901 | try o.builder.intValue(.i32, prefetch.cache), | | |
| 10902 | }, ""); | | |
| 10903 | return .none; | | |
| 10904 | } | | |
| 10905 | | | |
| 10906 | fn airAddrSpaceCast(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10907 | const o = self.ng.object; | | |
| 10908 | const pt = self.ng.pt; | | |
| 10909 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | | |
| 10910 | const inst_ty = self.typeOfIndex(inst); | | |
| 10911 | const operand = try self.resolveInst(ty_op.operand); | | |
| 10912 | | | |
| 10913 | return self.wip.cast(.addrspacecast, operand, try o.lowerType(pt, inst_ty), ""); | | |
| 10914 | } | | |
| 10915 | | | |
| 10916 | fn workIntrinsic( | | |
| 10917 | self: *FuncGen, | | |
| 10918 | dimension: u32, | | |
| 10919 | default: u32, | | |
| 10920 | comptime basename: []const u8, | | |
| 10921 | ) !Builder.Value { | | |
| 10922 | return self.wip.callIntrinsic(.normal, .none, switch (dimension) { | | |
| 10923 | 0 => @field(Builder.Intrinsic, basename ++ ".x"), | | |
| 10924 | 1 => @field(Builder.Intrinsic, basename ++ ".y"), | | |
| 10925 | 2 => @field(Builder.Intrinsic, basename ++ ".z"), | | |
| 10926 | else => return self.ng.object.builder.intValue(.i32, default), | | |
| 10927 | }, &.{}, &.{}, ""); | | |
| 10928 | } | | |
| 10929 | | | |
| 10930 | fn airWorkItemId(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10931 | const target = self.ng.pt.zcu.getTarget(); | | |
| 10932 | | | |
| 10933 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | | |
| 10934 | const dimension = pl_op.payload; | | |
| 10935 | | | |
| 10936 | return switch (target.cpu.arch) { | | |
| 10937 | .amdgcn => self.workIntrinsic(dimension, 0, "amdgcn.workitem.id"), | | |
| 10938 | .nvptx, .nvptx64 => self.workIntrinsic(dimension, 0, "nvvm.read.ptx.sreg.tid"), | | |
| 10939 | else => unreachable, | | |
| 10940 | }; | | |
| 10941 | } | | |
| 10942 | | | |
| 10943 | fn airWorkGroupSize(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10944 | const o = self.ng.object; | | |
| 10945 | const pt = self.ng.pt; | | |
| 10946 | const target = pt.zcu.getTarget(); | | |
| 10947 | | | |
| 10948 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | | |
| 10949 | const dimension = pl_op.payload; | | |
| 10950 | | | |
| 10951 | switch (target.cpu.arch) { | | |
| 10952 | .amdgcn => { | | |
| 10953 | if (dimension >= 3) return .@"1"; | | |
| 10954 | | | |
| 10955 | // Fetch the dispatch pointer, which points to this structure: | | |
| 10956 | // https://github.com/RadeonOpenCompute/ROCR-Runtime/blob/adae6c61e10d371f7cbc3d0e94ae2c070cab18a4/src/inc/hsa.h#L2913 | | |
| 10957 | const dispatch_ptr = | | |
| 10958 | try self.wip.callIntrinsic(.normal, .none, .@"amdgcn.dispatch.ptr", &.{}, &.{}, ""); | | |
| 10959 | | | |
| 10960 | // Load the work_group_* member from the struct as u16. | | |
| 10961 | // Just treat the dispatch pointer as an array of u16 to keep things simple. | | |
| 10962 | const workgroup_size_ptr = try self.wip.gep(.inbounds, .i16, dispatch_ptr, &.{ | | |
| 10963 | try o.builder.intValue(try o.lowerType(pt, Type.usize), 2 + dimension), | | |
| 10964 | }, ""); | | |
| 10965 | const workgroup_size_alignment = comptime Builder.Alignment.fromByteUnits(2); | | |
| 10966 | return self.wip.load(.normal, .i16, workgroup_size_ptr, workgroup_size_alignment, ""); | | |
| 10967 | }, | | |
| 10968 | .nvptx, .nvptx64 => { | | |
| 10969 | return self.workIntrinsic(dimension, 1, "nvvm.read.ptx.sreg.ntid"); | | |
| 10970 | }, | | |
| 10971 | else => unreachable, | | |
| 10972 | } | | |
| 10973 | } | | |
| 10974 | | | |
| 10975 | fn airWorkGroupId(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | | |
| 10976 | const target = self.ng.pt.zcu.getTarget(); | | |
| 10977 | | | |
| 10978 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | | |
| 10979 | const dimension = pl_op.payload; | | |
| 10980 | | | |
| 10981 | return switch (target.cpu.arch) { | | |
| 10982 | .amdgcn => self.workIntrinsic(dimension, 0, "amdgcn.workgroup.id"), | | |
| 10983 | .nvptx, .nvptx64 => self.workIntrinsic(dimension, 0, "nvvm.read.ptx.sreg.ctaid"), | | |
| 10984 | else => unreachable, | | |
| 10985 | }; | | |
| 10986 | } | | |
| 10987 | | | |
| 10988 | fn getErrorNameTable(self: *FuncGen) Allocator.Error!Builder.Variable.Index { | | |
| 10989 | const o = self.ng.object; | | |
| 10990 | const pt = self.ng.pt; | | |
| 10991 | | | |
| 10992 | const table = o.error_name_table; | | |
| 10993 | if (table != .none) return table; | | |
| 10994 | | | |
| 10995 | // TODO: Address space | | |
| 10996 | const variable_index = | | |
| 10997 | try o.builder.addVariable(try o.builder.strtabString("__zig_err_name_table"), .ptr, .default); | | |
| 10998 | variable_index.setLinkage(.private, &o.builder); | | |
| 10999 | variable_index.setMutability(.constant, &o.builder); | | |
| 11000 | variable_index.setUnnamedAddr(.unnamed_addr, &o.builder); | | |
| 11001 | variable_index.setAlignment( | | |
| 11002 | Type.slice_const_u8_sentinel_0.abiAlignment(pt.zcu).toLlvm(), | | |
| 11003 | &o.builder, | | |
| 11004 | ); | | |
| 11005 | | | |
| 11006 | o.error_name_table = variable_index; | | |
| 11007 | return variable_index; | | |
| 11008 | } | | |
| 11009 | | | |
| 11010 | /// Assumes the optional is not pointer-like and payload has bits. | | |
| 11011 | fn optCmpNull( | | |
| 11012 | self: *FuncGen, | | |
| 11013 | cond: Builder.IntegerCondition, | | |
| 11014 | opt_llvm_ty: Builder.Type, | | |
| 11015 | opt_handle: Builder.Value, | | |
| 11016 | is_by_ref: bool, | | |
| 11017 | access_kind: Builder.MemoryAccessKind, | | |
| 11018 | ) Allocator.Error!Builder.Value { | | |
| 11019 | const o = self.ng.object; | | |
| 11020 | const field = b: { | | |
| 11021 | if (is_by_ref) { | | |
| 11022 | const field_ptr = try self.wip.gepStruct(opt_llvm_ty, opt_handle, 1, ""); | | |
| 11023 | break :b try self.wip.load(access_kind, .i8, field_ptr, .default, ""); | | |
| 11024 | } | | |
| 11025 | break :b try self.wip.extractValue(opt_handle, &.{1}, ""); | | |
| 11026 | }; | | |
| 11027 | comptime assert(optional_layout_version == 3); | | |
| 11028 | | | |
| 11029 | return self.wip.icmp(cond, field, try o.builder.intValue(.i8, 0), ""); | | |
| 11030 | } | | |
| 11031 | | | |
| 11032 | /// Assumes the optional is not pointer-like and payload has bits. | | |
| 11033 | fn optPayloadHandle( | | |
| 11034 | fg: *FuncGen, | | |
| 11035 | opt_llvm_ty: Builder.Type, | | |
| 11036 | opt_handle: Builder.Value, | | |
| 11037 | opt_ty: Type, | | |
| 11038 | can_elide_load: bool, | | |
| 11039 | ) !Builder.Value { | | |
| 11040 | const pt = fg.ng.pt; | | |
| 11041 | const zcu = pt.zcu; | | |
| 11042 | const payload_ty = opt_ty.optionalChild(zcu); | | |
| 11043 | | | |
| 11044 | if (isByRef(opt_ty, zcu)) { | | |
| 11045 | // We have a pointer and we need to return a pointer to the first field. | | |
| 11046 | const payload_ptr = try fg.wip.gepStruct(opt_llvm_ty, opt_handle, 0, ""); | | |
| 11047 | | | |
| 11048 | const payload_alignment = payload_ty.abiAlignment(zcu).toLlvm(); | | |
| 11049 | if (isByRef(payload_ty, zcu)) { | | |
| 11050 | if (can_elide_load) | | |
| 11051 | return payload_ptr; | | |
| 11052 | | | |
| 11053 | return fg.loadByRef(payload_ptr, payload_ty, payload_alignment, .normal); | | |
| 11054 | } | | |
| 11055 | return fg.loadTruncate(.normal, payload_ty, payload_ptr, payload_alignment); | | |
| 11056 | } | | |
| 11057 | | | |
| 11058 | assert(!isByRef(payload_ty, zcu)); | | |
| 11059 | return fg.wip.extractValue(opt_handle, &.{0}, ""); | | |
| 11060 | } | | |
| 11061 | | | |
| 11062 | fn buildOptional( | | |
| 11063 | self: *FuncGen, | | |
| 11064 | optional_ty: Type, | | |
| 11065 | payload: Builder.Value, | | |
| 11066 | non_null_bit: Builder.Value, | | |
| 11067 | ) !Builder.Value { | | |
| 11068 | const o = self.ng.object; | | |
| 11069 | const pt = self.ng.pt; | | |
| 11070 | const zcu = pt.zcu; | | |
| 11071 | const optional_llvm_ty = try o.lowerType(pt, optional_ty); | | |
| 11072 | const non_null_field = try self.wip.cast(.zext, non_null_bit, .i8, ""); | | |
| 11073 | | | |
| 11074 | if (isByRef(optional_ty, zcu)) { | | |
| 11075 | const payload_alignment = optional_ty.abiAlignment(pt.zcu).toLlvm(); | | |
| 11076 | const alloca_inst = try self.buildAlloca(optional_llvm_ty, payload_alignment); | | |
| 11077 | | | |
| 11078 | { | | |
| 11079 | const field_ptr = try self.wip.gepStruct(optional_llvm_ty, alloca_inst, 0, ""); | | |
| 11080 | _ = try self.wip.store(.normal, payload, field_ptr, payload_alignment); | | |
| 11081 | } | | |
| 11082 | { | | |
| 11083 | const non_null_alignment = comptime Builder.Alignment.fromByteUnits(1); | | |
| 11084 | const field_ptr = try self.wip.gepStruct(optional_llvm_ty, alloca_inst, 1, ""); | | |
| 11085 | _ = try self.wip.store(.normal, non_null_field, field_ptr, non_null_alignment); | | |
| 11086 | } | | |
| 11087 | | | |
| 11088 | return alloca_inst; | | |
| 11089 | } | | |
| 11090 | | | |
| 11091 | return self.wip.buildAggregate(optional_llvm_ty, &.{ payload, non_null_field }, ""); | | |
| 11092 | } | | |
| 11093 | | | |
| 11094 | fn fieldPtr( | | |
| 11095 | self: *FuncGen, | | |
| 11096 | aggregate_ptr: Builder.Value, | | |
| 11097 | aggregate_ptr_ty: Type, | | |
| 11098 | field_index: u32, | | |
| 11099 | ) !Builder.Value { | | |
| 11100 | const o = self.ng.object; | | |
| 11101 | const pt = self.ng.pt; | | |
| 11102 | const zcu = pt.zcu; | | |
| 11103 | const aggregate_ty = aggregate_ptr_ty.childType(zcu); | | |
| 11104 | if (aggregate_ty.containerLayout(zcu) == .@"packed") { | | |
| 11105 | // A pointer to a bitpack field is equivalent to a pointer to the whole bitpack; the | | |
| 11106 | // bit offset is represented in the pointer *type*. | | |
| 11107 | return aggregate_ptr; | | |
| 11108 | } | | |
| 11109 | switch (aggregate_ty.zigTypeTag(zcu)) { | | |
| 11110 | .@"struct" => { | | |
| 11111 | if (!aggregate_ty.hasRuntimeBits(zcu)) { | | |
| 11112 | return aggregate_ptr; | | |
| 11113 | } | | |
| 11114 | const struct_llvm_ty = try o.lowerType(pt, aggregate_ty); | | |
| 11115 | if (o.llvmFieldIndex(aggregate_ty, field_index)) |llvm_field_index| { | | |
| 11116 | return self.wip.gepStruct(struct_llvm_ty, aggregate_ptr, llvm_field_index, ""); | | |
| 11117 | } else { | | |
| 11118 | // If we found no index then this means this is a zero sized field at the | | |
| 11119 | // end of the struct. Treat our struct pointer as an array of two and get | | |
| 11120 | // the index to the element at index `1` to get a pointer to the end of | | |
| 11121 | // the struct. | | |
| 11122 | const llvm_index = try o.builder.intValue( | | |
| 11123 | try o.lowerType(pt, Type.usize), | | |
| 11124 | @intFromBool(aggregate_ty.hasRuntimeBits(zcu)), | | |
| 11125 | ); | | |
| 11126 | return self.wip.gep(.inbounds, struct_llvm_ty, aggregate_ptr, &.{llvm_index}, ""); | | |
| 11127 | } | | |
| 11128 | }, | | |
| 11129 | .@"union" => { | | |
| 11130 | const layout = aggregate_ty.unionGetLayout(zcu); | | |
| 11131 | if (layout.payload_size == 0) return aggregate_ptr; | | |
| 11132 | const payload_index = @intFromBool(layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align)); | | |
| 11133 | const union_llvm_ty = try o.lowerType(pt, aggregate_ty); | | |
| 11134 | return self.wip.gepStruct(union_llvm_ty, aggregate_ptr, payload_index, ""); | | |
| 11135 | }, | | |
| 11136 | else => unreachable, | | |
| 11137 | } | | |
| 11138 | } | | |
| 11139 | | | |
| 11140 | /// Load a value and, if needed, mask out padding bits for non byte-sized integer values. | | |
| 11141 | fn loadTruncate( | | |
| 11142 | fg: *FuncGen, | | |
| 11143 | access_kind: Builder.MemoryAccessKind, | | |
| 11144 | payload_ty: Type, | | |
| 11145 | payload_ptr: Builder.Value, | | |
| 11146 | payload_alignment: Builder.Alignment, | | |
| 11147 | ) !Builder.Value { | | |
| 11148 | // from https://llvm.org/docs/LangRef.html#load-instruction : | | |
| 11149 | // "When loading a value of a type like i20 with a size that is not an integral number of bytes, the result is undefined if the value was not originally written using a store of the same type. " | | |
| 11150 | // => so load the byte aligned value and trunc the unwanted bits. | | |
| 11151 | | | |
| 11152 | const o = fg.ng.object; | | |
| 11153 | const pt = fg.ng.pt; | | |
| 11154 | const zcu = pt.zcu; | | |
| 11155 | const payload_llvm_ty = try o.lowerType(pt, payload_ty); | | |
| 11156 | const abi_size = payload_ty.abiSize(zcu); | | |
| 11157 | | | |
| 11158 | const load_llvm_ty = if (payload_ty.isAbiInt(zcu)) | | |
| 11159 | try o.builder.intType(@intCast(abi_size * 8)) | | |
| 11160 | else | | |
| 11161 | payload_llvm_ty; | | |
| 11162 | const loaded = try fg.wip.load(access_kind, load_llvm_ty, payload_ptr, payload_alignment, ""); | | |
| 11163 | const shifted = if (payload_llvm_ty != load_llvm_ty and o.target.cpu.arch.endian() == .big) | | |
| 11164 | try fg.wip.bin(.lshr, loaded, try o.builder.intValue( | | |
| 11165 | load_llvm_ty, | | |
| 11166 | (payload_ty.abiSize(zcu) - (std.math.divCeil(u64, payload_ty.bitSize(zcu), 8) catch unreachable)) * 8, | | |
| 11167 | ), "") | | |
| 11168 | else | | |
| 11169 | loaded; | | |
| 11170 | | | |
| 11171 | return fg.wip.conv(.unneeded, shifted, payload_llvm_ty, ""); | | |
| 11172 | } | | |
| 11173 | | | |
| 11174 | /// Load a by-ref type by constructing a new alloca and performing a memcpy. | | |
| 11175 | fn loadByRef( | | |
| 11176 | fg: *FuncGen, | | |
| 11177 | ptr: Builder.Value, | | |
| 11178 | pointee_type: Type, | | |
| 11179 | ptr_alignment: Builder.Alignment, | | |
| 11180 | access_kind: Builder.MemoryAccessKind, | | |
| 11181 | ) !Builder.Value { | | |
| 11182 | const o = fg.ng.object; | | |
| 11183 | const pt = fg.ng.pt; | | |
| 11184 | const pointee_llvm_ty = try o.lowerType(pt, pointee_type); | | |
| 11185 | const result_align = InternPool.Alignment.fromLlvm(ptr_alignment) | | |
| 11186 | .max(pointee_type.abiAlignment(pt.zcu)).toLlvm(); | | |
| 11187 | const result_ptr = try fg.buildAlloca(pointee_llvm_ty, result_align); | | |
| 11188 | const size_bytes = pointee_type.abiSize(pt.zcu); | | |
| 11189 | _ = try fg.wip.callMemCpy( | | |
| 11190 | result_ptr, | | |
| 11191 | result_align, | | |
| 11192 | ptr, | | |
| 11193 | ptr_alignment, | | |
| 11194 | try o.builder.intValue(try o.lowerType(pt, Type.usize), size_bytes), | | |
| 11195 | access_kind, | | |
| 11196 | fg.disable_intrinsics, | | |
| 11197 | ); | | |
| 11198 | return result_ptr; | | |
| 11199 | } | | |
| 11200 | | | |
| 11201 | /// This function always performs a copy. For isByRef=true types, it creates a new | | |
| 11202 | /// alloca and copies the value into it, then returns the alloca instruction. | | |
| 11203 | /// For isByRef=false types, it creates a load instruction and returns it. | | |
| 11204 | fn load(self: *FuncGen, ptr: Builder.Value, ptr_ty: Type) !Builder.Value { | | |
| 11205 | const o = self.ng.object; | | |
| 11206 | const pt = self.ng.pt; | | |
| 11207 | const zcu = pt.zcu; | | |
| 11208 | const info = ptr_ty.ptrInfo(zcu); | | |
| 11209 | const elem_ty = Type.fromInterned(info.child); | | |
| 11210 | if (!elem_ty.hasRuntimeBits(zcu)) return .none; | | |
| 11211 | | | |
| 11212 | const ptr_alignment = (if (info.flags.alignment != .none) | | |
| 11213 | @as(InternPool.Alignment, info.flags.alignment) | | |
| 11214 | else | | |
| 11215 | elem_ty.abiAlignment(zcu)).toLlvm(); | | |
| 11216 | | | |
| 11217 | const access_kind: Builder.MemoryAccessKind = | | |
| 11218 | if (info.flags.is_volatile) .@"volatile" else .normal; | | |
| 11219 | | | |
| 11220 | if (info.flags.vector_index != .none) { | | |
| 11221 | const index_u32 = try o.builder.intValue(.i32, info.flags.vector_index); | | |
| 11222 | const vec_elem_ty = try o.lowerType(pt, elem_ty); | | |
| 11223 | const vec_ty = try o.builder.vectorType(.normal, info.packed_offset.host_size, vec_elem_ty); | | |
| 11224 | | | |
| 11225 | const loaded_vector = try self.wip.load(access_kind, vec_ty, ptr, ptr_alignment, ""); | | |
| 11226 | return self.wip.extractElement(loaded_vector, index_u32, ""); | | |
| 11227 | } | | |
| 11228 | | | |
| 11229 | if (info.packed_offset.host_size == 0) { | | |
| 11230 | if (isByRef(elem_ty, zcu)) { | | |
| 11231 | return self.loadByRef(ptr, elem_ty, ptr_alignment, access_kind); | | |
| 11232 | } | | |
| 11233 | return self.loadTruncate(access_kind, elem_ty, ptr, ptr_alignment); | | |
| 11234 | } | | |
| 11235 | | | |
| 11236 | const containing_int_ty = try o.builder.intType(@intCast(info.packed_offset.host_size * 8)); | | |
| 11237 | const containing_int = | | |
| 11238 | try self.wip.load(access_kind, containing_int_ty, ptr, ptr_alignment, ""); | | |
| 11239 | | | |
| 11240 | const elem_bits = ptr_ty.childType(zcu).bitSize(zcu); | | |
| 11241 | const shift_amt = try o.builder.intValue(containing_int_ty, info.packed_offset.bit_offset); | | |
| 11242 | const shifted_value = try self.wip.bin(.lshr, containing_int, shift_amt, ""); | | |
| 11243 | const elem_llvm_ty = try o.lowerType(pt, elem_ty); | | |
| 11244 | | | |
| 11245 | if (isByRef(elem_ty, zcu)) { | | |
| 11246 | const result_align = elem_ty.abiAlignment(zcu).toLlvm(); | | |
| 11247 | const result_ptr = try self.buildAlloca(elem_llvm_ty, result_align); | | |
| 11248 | | | |
| 11249 | const same_size_int = try o.builder.intType(@intCast(elem_bits)); | | |
| 11250 | const truncated_int = try self.wip.cast(.trunc, shifted_value, same_size_int, ""); | | |
| 11251 | _ = try self.wip.store(.normal, truncated_int, result_ptr, result_align); | | |
| 11252 | return result_ptr; | | |
| 11253 | } | | |
| 11254 | | | |
| 11255 | if (elem_ty.zigTypeTag(zcu) == .float or elem_ty.zigTypeTag(zcu) == .vector) { | | |
| 11256 | const same_size_int = try o.builder.intType(@intCast(elem_bits)); | | |
| 11257 | const truncated_int = try self.wip.cast(.trunc, shifted_value, same_size_int, ""); | | |
| 11258 | return self.wip.cast(.bitcast, truncated_int, elem_llvm_ty, ""); | | |
| 11259 | } | | |
| 11260 | | | |
| 11261 | if (elem_ty.isPtrAtRuntime(zcu)) { | | |
| 11262 | const same_size_int = try o.builder.intType(@intCast(elem_bits)); | | |
| 11263 | const truncated_int = try self.wip.cast(.trunc, shifted_value, same_size_int, ""); | | |
| 11264 | return self.wip.cast(.inttoptr, truncated_int, elem_llvm_ty, ""); | | |
| 11265 | } | | |
| 11266 | | | |
| 11267 | return self.wip.cast(.trunc, shifted_value, elem_llvm_ty, ""); | | |
| 11268 | } | | |
| 11269 | | | |
| 11270 | fn store( | | |
| 11271 | self: *FuncGen, | | |
| 11272 | ptr: Builder.Value, | | |
| 11273 | ptr_ty: Type, | | |
| 11274 | elem: Builder.Value, | | |
| 11275 | ordering: Builder.AtomicOrdering, | | |
| 11276 | ) !void { | | |
| 11277 | const o = self.ng.object; | | |
| 11278 | const pt = self.ng.pt; | | |
| 11279 | const zcu = pt.zcu; | | |
| 11280 | const info = ptr_ty.ptrInfo(zcu); | | |
| 11281 | const elem_ty = Type.fromInterned(info.child); | | |
| 11282 | if (!elem_ty.hasRuntimeBits(zcu)) { | | |
| 11283 | return; | | |
| 11284 | } | | |
| 11285 | const ptr_alignment = ptr_ty.ptrAlignment(zcu).toLlvm(); | | |
| 11286 | const access_kind: Builder.MemoryAccessKind = | | |
| 11287 | if (info.flags.is_volatile) .@"volatile" else .normal; | | |
| 11288 | | | |
| 11289 | if (info.flags.vector_index != .none) { | | |
| 11290 | const index_u32 = try o.builder.intValue(.i32, info.flags.vector_index); | | |
| 11291 | const vec_elem_ty = try o.lowerType(pt, elem_ty); | | |
| 11292 | const vec_ty = try o.builder.vectorType(.normal, info.packed_offset.host_size, vec_elem_ty); | | |
| 11293 | | | |
| 11294 | const loaded_vector = try self.wip.load(.normal, vec_ty, ptr, ptr_alignment, ""); | | |
| 11295 | | | |
| 11296 | const modified_vector = try self.wip.insertElement(loaded_vector, elem, index_u32, ""); | | |
| 11297 | | | |
| 11298 | assert(ordering == .none); | | |
| 11299 | _ = try self.wip.store(access_kind, modified_vector, ptr, ptr_alignment); | | |
| 11300 | return; | | |
| 11301 | } | | |
| 11302 | | | |
| 11303 | if (info.packed_offset.host_size != 0) { | | |
| 11304 | const containing_int_ty = try o.builder.intType(@intCast(info.packed_offset.host_size * 8)); | | |
| 11305 | assert(ordering == .none); | | |
| 11306 | const containing_int = | | |
| 11307 | try self.wip.load(.normal, containing_int_ty, ptr, ptr_alignment, ""); | | |
| 11308 | const elem_bits = ptr_ty.childType(zcu).bitSize(zcu); | | |
| 11309 | const shift_amt = try o.builder.intConst(containing_int_ty, info.packed_offset.bit_offset); | | |
| 11310 | // Convert to equally-sized integer type in order to perform the bit | | |
| 11311 | // operations on the value to store | | |
| 11312 | const value_bits_type = try o.builder.intType(@intCast(elem_bits)); | | |
| 11313 | const value_bits = if (elem_ty.isPtrAtRuntime(zcu)) | | |
| 11314 | try self.wip.cast(.ptrtoint, elem, value_bits_type, "") | | |
| 11315 | else | | |
| 11316 | try self.wip.cast(.bitcast, elem, value_bits_type, ""); | | |
| 11317 | | | |
| 11318 | const mask_val = blk: { | | |
| 11319 | const zext = try self.wip.cast( | | |
| 11320 | .zext, | | |
| 11321 | try o.builder.intValue(value_bits_type, -1), | | |
| 11322 | containing_int_ty, | | |
| 11323 | "", | | |
| 11324 | ); | | |
| 11325 | const shl = try self.wip.bin(.shl, zext, shift_amt.toValue(), ""); | | |
| 11326 | break :blk try self.wip.bin( | | |
| 11327 | .xor, | | |
| 11328 | shl, | | |
| 11329 | try o.builder.intValue(containing_int_ty, -1), | | |
| 11330 | "", | | |
| 11331 | ); | | |
| 11332 | }; | | |
| 11333 | | | |
| 11334 | const anded_containing_int = try self.wip.bin(.@"and", containing_int, mask_val, ""); | | |
| 11335 | const extended_value = try self.wip.cast(.zext, value_bits, containing_int_ty, ""); | | |
| 11336 | const shifted_value = try self.wip.bin(.shl, extended_value, shift_amt.toValue(), ""); | | |
| 11337 | const ored_value = try self.wip.bin(.@"or", shifted_value, anded_containing_int, ""); | | |
| 11338 | | | |
| 11339 | assert(ordering == .none); | | |
| 11340 | _ = try self.wip.store(access_kind, ored_value, ptr, ptr_alignment); | | |
| 11341 | return; | | |
| 11342 | } | | |
| 11343 | if (!isByRef(elem_ty, zcu)) { | | |
| 11344 | _ = try self.wip.storeAtomic( | | |
| 11345 | access_kind, | | |
| 11346 | elem, | | |
| 11347 | ptr, | | |
| 11348 | self.sync_scope, | | |
| 11349 | ordering, | | |
| 11350 | ptr_alignment, | | |
| 11351 | ); | | |
| 11352 | return; | | |
| 11353 | } | | |
| 11354 | assert(ordering == .none); | | |
| 11355 | _ = try self.wip.callMemCpy( | | |
| 11356 | ptr, | | |
| 11357 | ptr_alignment, | | |
| 11358 | elem, | | |
| 11359 | elem_ty.abiAlignment(zcu).toLlvm(), | | |
| 11360 | try o.builder.intValue(try o.lowerType(pt, Type.usize), elem_ty.abiSize(zcu)), | | |
| 11361 | access_kind, | | |
| 11362 | self.disable_intrinsics, | | |
| 11363 | ); | | |
| 11364 | } | | |
| 11365 | | | |
| 11366 | fn valgrindMarkUndef(fg: *FuncGen, ptr: Builder.Value, len: Builder.Value) Allocator.Error!void { | | |
| 11367 | const VG_USERREQ__MAKE_MEM_UNDEFINED = 1296236545; | | |
| 11368 | const o = fg.ng.object; | | |
| 11369 | const pt = fg.ng.pt; | | |
| 11370 | const usize_ty = try o.lowerType(pt, Type.usize); | | |
| 11371 | const zero = try o.builder.intValue(usize_ty, 0); | | |
| 11372 | const req = try o.builder.intValue(usize_ty, VG_USERREQ__MAKE_MEM_UNDEFINED); | | |
| 11373 | const ptr_as_usize = try fg.wip.cast(.ptrtoint, ptr, usize_ty, ""); | | |
| 11374 | _ = try valgrindClientRequest(fg, zero, req, ptr_as_usize, len, zero, zero, zero); | | |
| 11375 | } | | |
| 11376 | | | |
| 11377 | fn valgrindClientRequest( | | |
| 11378 | fg: *FuncGen, | | |
| 11379 | default_value: Builder.Value, | | |
| 11380 | request: Builder.Value, | | |
| 11381 | a1: Builder.Value, | | |
| 11382 | a2: Builder.Value, | | |
| 11383 | a3: Builder.Value, | | |
| 11384 | a4: Builder.Value, | | |
| 11385 | a5: Builder.Value, | | |
| 11386 | ) Allocator.Error!Builder.Value { | | |
| 11387 | const o = fg.ng.object; | | |
| 11388 | const pt = fg.ng.pt; | | |
| 11389 | const zcu = pt.zcu; | | |
| 11390 | const target = zcu.getTarget(); | | |
| 11391 | if (!target_util.hasValgrindSupport(target, .stage2_llvm)) return default_value; | | |
| 11392 | | | |
| 11393 | const llvm_usize = try o.lowerType(pt, Type.usize); | | |
| 11394 | const usize_alignment = Type.usize.abiAlignment(zcu).toLlvm(); | | |
| 11395 | | | |
| 11396 | const array_llvm_ty = try o.builder.arrayType(6, llvm_usize); | | |
| 11397 | const array_ptr = if (fg.valgrind_client_request_array == .none) a: { | | |
| 11398 | const array_ptr = try fg.buildAlloca(array_llvm_ty, usize_alignment); | | |
| 11399 | fg.valgrind_client_request_array = array_ptr; | | |
| 11400 | break :a array_ptr; | | |
| 11401 | } else fg.valgrind_client_request_array; | | |
| 11402 | const array_elements = [_]Builder.Value{ request, a1, a2, a3, a4, a5 }; | | |
| 11403 | const zero = try o.builder.intValue(llvm_usize, 0); | | |
| 11404 | for (array_elements, 0..) |elem, i| { | | |
| 11405 | const elem_ptr = try fg.wip.gep(.inbounds, array_llvm_ty, array_ptr, &.{ | | |
| 11406 | zero, try o.builder.intValue(llvm_usize, i), | | |
| 11407 | }, ""); | | |
| 11408 | _ = try fg.wip.store(.normal, elem, elem_ptr, usize_alignment); | | |
| 11409 | } | | |
| 11410 | | | |
| 11411 | const arch_specific: struct { | | |
| 11412 | template: [:0]const u8, | | |
| 11413 | constraints: [:0]const u8, | | |
| 11414 | } = switch (target.cpu.arch) { | | |
| 11415 | .arm, .armeb, .thumb, .thumbeb => .{ | | |
| 11416 | .template = | | |
| 11417 | \\ mov r12, r12, ror #3 ; mov r12, r12, ror #13 | | |
| 11418 | \\ mov r12, r12, ror #29 ; mov r12, r12, ror #19 | | |
| 11419 | \\ orr r10, r10, r10 | | |
| 11420 | , | | |
| 11421 | .constraints = "={r3},{r4},{r3},~{cc},~{memory}", | | |
| 11422 | }, | | |
| 11423 | .aarch64, .aarch64_be => .{ | | |
| 11424 | .template = | | |
| 11425 | \\ ror x12, x12, #3 ; ror x12, x12, #13 | | |
| 11426 | \\ ror x12, x12, #51 ; ror x12, x12, #61 | | |
| 11427 | \\ orr x10, x10, x10 | | |
| 11428 | , | | |
| 11429 | .constraints = "={x3},{x4},{x3},~{cc},~{memory}", | | |
| 11430 | }, | | |
| 11431 | .mips, .mipsel => .{ | | |
| 11432 | .template = | | |
| 11433 | \\ srl $$0, $$0, 13 | | |
| 11434 | \\ srl $$0, $$0, 29 | | |
| 11435 | \\ srl $$0, $$0, 3 | | |
| 11436 | \\ srl $$0, $$0, 19 | | |
| 11437 | \\ or $$13, $$13, $$13 | | |
| 11438 | , | | |
| 11439 | .constraints = "={$11},{$12},{$11},~{memory},~{$1}", | | |
| 11440 | }, | | |
| 11441 | .mips64, .mips64el => .{ | | |
| 11442 | .template = | | |
| 11443 | \\ dsll $$0, $$0, 3 ; dsll $$0, $$0, 13 | | |
| 11444 | \\ dsll $$0, $$0, 29 ; dsll $$0, $$0, 19 | | |
| 11445 | \\ or $$13, $$13, $$13 | | |
| 11446 | , | | |
| 11447 | .constraints = "={$11},{$12},{$11},~{memory},~{$1}", | | |
| 11448 | }, | | |
| 11449 | .powerpc, .powerpcle => .{ | | |
| 11450 | .template = | | |
| 11451 | \\ rlwinm 0, 0, 3, 0, 31 ; rlwinm 0, 0, 13, 0, 31 | | |
| 11452 | \\ rlwinm 0, 0, 29, 0, 31 ; rlwinm 0, 0, 19, 0, 31 | | |
| 11453 | \\ or 1, 1, 1 | | |
| 11454 | , | | |
| 11455 | .constraints = "={r3},{r4},{r3},~{cc},~{memory}", | | |
| 11456 | }, | | |
| 11457 | .powerpc64, .powerpc64le => .{ | | |
| 11458 | .template = | | |
| 11459 | \\ rotldi 0, 0, 3 ; rotldi 0, 0, 13 | | |
| 11460 | \\ rotldi 0, 0, 61 ; rotldi 0, 0, 51 | | |
| 11461 | \\ or 1, 1, 1 | | |
| 11462 | , | | |
| 11463 | .constraints = "={r3},{r4},{r3},~{cc},~{memory}", | | |
| 11464 | }, | | |
| 11465 | .riscv64 => .{ | | |
| 11466 | .template = | | |
| 11467 | \\ .option push | | |
| 11468 | \\ .option norvc | | |
| 11469 | \\ srli zero, zero, 3 | | |
| 11470 | \\ srli zero, zero, 13 | | |
| 11471 | \\ srli zero, zero, 51 | | |
| 11472 | \\ srli zero, zero, 61 | | |
| 11473 | \\ or a0, a0, a0 | | |
| 11474 | \\ .option pop | | |
| 11475 | , | | |
| 11476 | .constraints = "={a3},{a4},{a3},~{cc},~{memory}", | | |
| 11477 | }, | | |
| 11478 | .s390x => .{ | | |
| 11479 | .template = | | |
| 11480 | \\ lr %r15, %r15 | | |
| 11481 | \\ lr %r1, %r1 | | |
| 11482 | \\ lr %r2, %r2 | | |
| 11483 | \\ lr %r3, %r3 | | |
| 11484 | \\ lr %r2, %r2 | | |
| 11485 | , | | |
| 11486 | .constraints = "={r3},{r2},{r3},~{cc},~{memory}", | | |
| 11487 | }, | | |
| 11488 | .x86 => .{ | | |
| 11489 | .template = | | |
| 11490 | \\ roll $$3, %edi ; roll $$13, %edi | | |
| 11491 | \\ roll $$61, %edi ; roll $$51, %edi | | |
| 11492 | \\ xchgl %ebx, %ebx | | |
| 11493 | , | | |
| 11494 | .constraints = "={edx},{eax},{edx},~{cc},~{memory},~{dirflag},~{fpsr},~{flags}", | | |
| 11495 | }, | | |
| 11496 | .x86_64 => .{ | | |
| 11497 | .template = | | |
| 11498 | \\ rolq $$3, %rdi ; rolq $$13, %rdi | | |
| 11499 | \\ rolq $$61, %rdi ; rolq $$51, %rdi | | |
| 11500 | \\ xchgq %rbx, %rbx | | |
| 11501 | , | | |
| 11502 | .constraints = "={rdx},{rax},{rdx},~{cc},~{memory},~{dirflag},~{fpsr},~{flags}", | | |
| 11503 | }, | | |
| 11504 | else => unreachable, | | |
| 11505 | }; | | |
| 11506 | | | |
| 11507 | return fg.wip.callAsm( | | |
| 11508 | .none, | | |
| 11509 | try o.builder.fnType(llvm_usize, &.{ llvm_usize, llvm_usize }, .normal), | | |
| 11510 | .{ .sideeffect = true }, | | |
| 11511 | try o.builder.string(arch_specific.template), | | |
| 11512 | try o.builder.string(arch_specific.constraints), | | |
| 11513 | &.{ try fg.wip.cast(.ptrtoint, array_ptr, llvm_usize, ""), default_value }, | | |
| 11514 | "", | | |
| 11515 | ); | | |
| 11516 | } | | |
| 11517 | | | |
| 11518 | fn typeOf(fg: *FuncGen, inst: Air.Inst.Ref) Type { | | |
| 11519 | const zcu = fg.ng.pt.zcu; | | |
| 11520 | return fg.air.typeOf(inst, &zcu.intern_pool); | | |
| 11521 | } | | |
| 11522 | | | |
| 11523 | fn typeOfIndex(fg: *FuncGen, inst: Air.Inst.Index) Type { | | |
| 11524 | const zcu = fg.ng.pt.zcu; | | |
| 11525 | return fg.air.typeOfIndex(inst, &zcu.intern_pool); | | |
| 11526 | } | | |
| 11527 | }; | | |
| 11528 | | | |
| 11529 | fn toLlvmAtomicOrdering(atomic_order: std.builtin.AtomicOrder) Builder.AtomicOrdering { | | |
| 11530 | return switch (atomic_order) { | | |
| 11531 | .unordered => .unordered, | | |
| 11532 | .monotonic => .monotonic, | | |
| 11533 | .acquire => .acquire, | | |
| 11534 | .release => .release, | | |
| 11535 | .acq_rel => .acq_rel, | | |
| 11536 | .seq_cst => .seq_cst, | | |
| 11537 | }; | | |
| 11538 | } | | |
| 11539 | | | |
| 11540 | fn toLlvmAtomicRmwBinOp( | | |
| 11541 | op: std.builtin.AtomicRmwOp, | | |
| 11542 | is_signed: bool, | | |
| 11543 | is_float: bool, | | |
| 11544 | ) Builder.Function.Instruction.AtomicRmw.Operation { | | |
| 11545 | return switch (op) { | | |
| 11546 | .Xchg => .xchg, | | |
| 11547 | .Add => if (is_float) .fadd else return .add, | | |
| 11548 | .Sub => if (is_float) .fsub else return .sub, | | |
| 11549 | .And => .@"and", | | |
| 11550 | .Nand => .nand, | | |
| 11551 | .Or => .@"or", | | |
| 11552 | .Xor => .xor, | | |
| 11553 | .Max => if (is_float) .fmax else if (is_signed) .max else return .umax, | | |
| 11554 | .Min => if (is_float) .fmin else if (is_signed) .min else return .umin, | | |
| 11555 | }; | | |
| 11556 | } | | |
| 11557 | | | |
| 11558 | const CallingConventionInfo = struct { | | |
| 11559 | /// The LLVM calling convention to use. | | |
| 11560 | llvm_cc: Builder.CallConv, | | |
| 11561 | /// Whether to use an `alignstack` attribute to forcibly re-align the stack pointer in the function's prologue. | | |
| 11562 | align_stack: bool, | | |
| 11563 | /// Whether the function needs a `naked` attribute. | | |
| 11564 | naked: bool, | | |
| 11565 | /// How many leading parameters to apply the `inreg` attribute to. | | |
| 11566 | inreg_param_count: u2 = 0, | | |
| 11567 | }; | | |
| 11568 | | | |
| 11569 | pub fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: *const std.Target) ?CallingConventionInfo { | | |
| 11570 | const llvm_cc = toLlvmCallConvTag(cc, target) orelse return null; | | |
| 11571 | const incoming_stack_alignment: ?u64, const register_params: u2 = switch (cc) { | | |
| 11572 | inline else => |pl| switch (@TypeOf(pl)) { | | |
| 11573 | void => .{ null, 0 }, | | |
| 11574 | std.builtin.CallingConvention.ArcInterruptOptions, | | |
| 11575 | std.builtin.CallingConvention.ArmInterruptOptions, | | |
| 11576 | std.builtin.CallingConvention.RiscvInterruptOptions, | | |
| 11577 | std.builtin.CallingConvention.ShInterruptOptions, | | |
| 11578 | std.builtin.CallingConvention.MicroblazeInterruptOptions, | | |
| 11579 | std.builtin.CallingConvention.MipsInterruptOptions, | | |
| 11580 | std.builtin.CallingConvention.CommonOptions, | | |
| 11581 | => .{ pl.incoming_stack_alignment, 0 }, | | |
| 11582 | std.builtin.CallingConvention.X86RegparmOptions => .{ pl.incoming_stack_alignment, pl.register_params }, | | |
| 11583 | else => @compileError("TODO: toLlvmCallConv" ++ @tagName(pl)), | | |
| 11584 | }, | | |
| 11585 | }; | | |
| 11586 | return .{ | | |
| 11587 | .llvm_cc = llvm_cc, | | |
| 11588 | .align_stack = if (incoming_stack_alignment) |a| need_align: { | | |
| 11589 | const normal_stack_align = target.stackAlignment(); | | |
| 11590 | break :need_align a < normal_stack_align; | | |
| 11591 | } else false, | | |
| 11592 | .naked = cc == .naked, | | |
| 11593 | .inreg_param_count = register_params, | | |
| 11594 | }; | | |
| 11595 | } | | |
| 11596 | fn toLlvmCallConvTag(cc_tag: std.builtin.CallingConvention.Tag, target: *const std.Target) ?Builder.CallConv { | | |
| 11597 | if (target.cCallingConvention()) |default_c| { | | |
| 11598 | if (cc_tag == default_c) { | | |
| 11599 | return .ccc; | | |
| 11600 | } | | |
| 11601 | } | | |
| 11602 | return switch (cc_tag) { | | |
| 11603 | .@"inline" => unreachable, | | |
| 11604 | .auto, .async => .fastcc, | | |
| 11605 | .naked => .ccc, | | |
| 11606 | .x86_64_sysv => .x86_64_sysvcc, | | |
| 11607 | .x86_64_win => .win64cc, | | |
| 11608 | .x86_64_regcall_v3_sysv => if (target.cpu.arch == .x86_64 and target.os.tag != .windows) | | |
| 11609 | .x86_regcallcc | | |
| 11610 | else | | |
| 11611 | null, | | |
| 11612 | .x86_64_regcall_v4_win => if (target.cpu.arch == .x86_64 and target.os.tag == .windows) | | |
| 11613 | .x86_regcallcc // we use the "RegCallv4" module flag to make this correct | | |
| 11614 | else | | |
| 11615 | null, | | |
| 11616 | .x86_64_vectorcall => .x86_vectorcallcc, | | |
| 11617 | .x86_64_interrupt => .x86_intrcc, | | |
| 11618 | .x86_stdcall => .x86_stdcallcc, | | |
| 11619 | .x86_fastcall => .x86_fastcallcc, | | |
| 11620 | .x86_thiscall => .x86_thiscallcc, | | |
| 11621 | .x86_regcall_v3 => if (target.cpu.arch == .x86 and target.os.tag != .windows) | | |
| 11622 | .x86_regcallcc | | |
| 11623 | else | | |
| 11624 | null, | | |
| 11625 | .x86_regcall_v4_win => if (target.cpu.arch == .x86 and target.os.tag == .windows) | | |
| 11626 | .x86_regcallcc // we use the "RegCallv4" module flag to make this correct | | |
| 11627 | else | | |
| 11628 | null, | | |
| 11629 | .x86_vectorcall => .x86_vectorcallcc, | | |
| 11630 | .x86_interrupt => .x86_intrcc, | | |
| 11631 | .aarch64_vfabi => .aarch64_vector_pcs, | | |
| 11632 | .aarch64_vfabi_sve => .aarch64_sve_vector_pcs, | | |
| 11633 | .arm_aapcs => .arm_aapcscc, | | |
| 11634 | .arm_aapcs_vfp => .arm_aapcs_vfpcc, | | |
| 11635 | .riscv64_lp64_v => .riscv_vectorcallcc, | | |
| 11636 | .riscv32_ilp32_v => .riscv_vectorcallcc, | | |
| 11637 | .avr_builtin => .avr_builtincc, | | |
| 11638 | .avr_signal => .avr_signalcc, | | |
| 11639 | .avr_interrupt => .avr_intrcc, | | |
| 11640 | .m68k_rtd => .m68k_rtdcc, | | |
| 11641 | .m68k_interrupt => .m68k_intrcc, | | |
| 11642 | .msp430_interrupt => .msp430_intrcc, | | |
| 11643 | .amdgcn_kernel => .amdgpu_kernel, | | |
| 11644 | .amdgcn_cs => .amdgpu_cs, | | |
| 11645 | .nvptx_device => .ptx_device, | | |
| 11646 | .nvptx_kernel => .ptx_kernel, | | |
| 11647 | | | |
| 11648 | // Calling conventions which LLVM uses function attributes for. | | |
| 11649 | .riscv64_interrupt, | | |
| 11650 | .riscv32_interrupt, | | |
| 11651 | .arm_interrupt, | | |
| 11652 | .mips64_interrupt, | | |
| 11653 | .mips_interrupt, | | |
| 11654 | .csky_interrupt, | | |
| 11655 | => .ccc, | | |
| 11656 | | 4605 | |
| 11657 | // All the calling conventions which LLVM does not have a general representation for. | 4606 | // All the calling conventions which LLVM does not have a general representation for. |
| 11658 | // Note that these are often still supported through the `cCallingConvention` path above via `ccc`. | 4607 | // Note that these are often still supported through the `cCallingConvention` path above via `ccc`. |
| ... | @@ -11724,7 +4673,7 @@ fn toLlvmCallConvTag(cc_tag: std.builtin.CallingConvention.Tag, target: *const s | ... | @@ -11724,7 +4673,7 @@ fn toLlvmCallConvTag(cc_tag: std.builtin.CallingConvention.Tag, target: *const s |
| 11724 | } | 4673 | } |
| 11725 | | 4674 | |
| 11726 | /// Convert a zig-address space to an llvm address space. | 4675 | /// Convert a zig-address space to an llvm address space. |
| 11727 | fn toLlvmAddressSpace(address_space: std.builtin.AddressSpace, target: *const std.Target) Builder.AddrSpace { | 4676 | pub fn toLlvmAddressSpace(address_space: std.builtin.AddressSpace, target: *const std.Target) Builder.AddrSpace { |
| 11728 | for (llvmAddrSpaceInfo(target)) |info| if (info.zig == address_space) return info.llvm; | 4677 | for (llvmAddrSpaceInfo(target)) |info| if (info.zig == address_space) return info.llvm; |
| 11729 | unreachable; | 4678 | unreachable; |
| 11730 | } | 4679 | } |
| ... | @@ -11811,20 +4760,6 @@ fn llvmAddrSpaceInfo(target: *const std.Target) []const AddrSpaceInfo { | ... | @@ -11811,20 +4760,6 @@ fn llvmAddrSpaceInfo(target: *const std.Target) []const AddrSpaceInfo { |
| 11811 | }; | 4760 | }; |
| 11812 | } | 4761 | } |
| 11813 | | 4762 | |
| 11814 | /// On some targets, local values that are in the generic address space must be generated into a | | |
| 11815 | /// different address, space and then cast back to the generic address space. | | |
| 11816 | /// For example, on GPUs local variable declarations must be generated into the local address space. | | |
| 11817 | /// This function returns the address space local values should be generated into. | | |
| 11818 | fn llvmAllocaAddressSpace(target: *const std.Target) Builder.AddrSpace { | | |
| 11819 | return switch (target.cpu.arch) { | | |
| 11820 | // On amdgcn, locals should be generated into the private address space. | | |
| 11821 | // To make Zig not impossible to use, these are then converted to addresses in the | | |
| 11822 | // generic address space and treates as regular pointers. This is the way that HIP also does it. | | |
| 11823 | .amdgcn => Builder.AddrSpace.amdgpu.private, | | |
| 11824 | else => .default, | | |
| 11825 | }; | | |
| 11826 | } | | |
| 11827 | | | |
| 11828 | /// On some targets, global values that are in the generic address space must be generated into a | 4763 | /// On some targets, global values that are in the generic address space must be generated into a |
| 11829 | /// different address space, and then cast back to the generic address space. | 4764 | /// different address space, and then cast back to the generic address space. |
| 11830 | fn llvmDefaultGlobalAddressSpace(target: *const std.Target) Builder.AddrSpace { | 4765 | fn llvmDefaultGlobalAddressSpace(target: *const std.Target) Builder.AddrSpace { |
| ... | @@ -11845,731 +4780,10 @@ fn toLlvmGlobalAddressSpace(wanted_address_space: std.builtin.AddressSpace, targ | ... | @@ -11845,731 +4780,10 @@ fn toLlvmGlobalAddressSpace(wanted_address_space: std.builtin.AddressSpace, targ |
| 11845 | }; | 4780 | }; |
| 11846 | } | 4781 | } |
| 11847 | | 4782 | |
| 11848 | fn returnTypeByRef(zcu: *Zcu, target: *const std.Target, ty: Type) bool { | | |
| 11849 | if (isByRef(ty, zcu)) { | | |
| 11850 | return true; | | |
| 11851 | } else if (target.cpu.arch.isX86() and | | |
| 11852 | !target.cpu.has(.x86, .evex512) and | | |
| 11853 | ty.totalVectorBits(zcu) >= 512) | | |
| 11854 | { | | |
| 11855 | // As of LLVM 18, passing a vector byval with fastcc that is 512 bits or more returns | | |
| 11856 | // "512-bit vector arguments require 'evex512' for AVX512" | | |
| 11857 | return true; | | |
| 11858 | } else { | | |
| 11859 | return false; | | |
| 11860 | } | | |
| 11861 | } | | |
| 11862 | | | |
| 11863 | fn firstParamSRet(fn_info: InternPool.Key.FuncType, zcu: *Zcu, target: *const std.Target) bool { | | |
| 11864 | const return_type = Type.fromInterned(fn_info.return_type); | | |
| 11865 | if (!return_type.hasRuntimeBits(zcu)) return false; | | |
| 11866 | | | |
| 11867 | return switch (fn_info.cc) { | | |
| 11868 | .auto => returnTypeByRef(zcu, target, return_type), | | |
| 11869 | .x86_64_sysv => firstParamSRetSystemV(return_type, zcu, target), | | |
| 11870 | .x86_64_win => x86_64_abi.classifyWindows(return_type, zcu, target, .ret) == .memory, | | |
| 11871 | .x86_sysv, .x86_win => isByRef(return_type, zcu), | | |
| 11872 | .x86_stdcall => !isScalar(zcu, return_type), | | |
| 11873 | .wasm_mvp => wasm_c_abi.classifyType(return_type, zcu) == .indirect, | | |
| 11874 | .aarch64_aapcs, | | |
| 11875 | .aarch64_aapcs_darwin, | | |
| 11876 | .aarch64_aapcs_win, | | |
| 11877 | => aarch64_c_abi.classifyType(return_type, zcu) == .memory, | | |
| 11878 | .arm_aapcs, .arm_aapcs_vfp => switch (arm_c_abi.classifyType(return_type, zcu, .ret)) { | | |
| 11879 | .memory, .i64_array => true, | | |
| 11880 | .i32_array => |size| size != 1, | | |
| 11881 | .byval => false, | | |
| 11882 | }, | | |
| 11883 | .riscv64_lp64, .riscv32_ilp32 => riscv_c_abi.classifyType(return_type, zcu) == .memory, | | |
| 11884 | .mips_o32 => switch (mips_c_abi.classifyType(return_type, zcu, .ret)) { | | |
| 11885 | .memory, .i32_array => true, | | |
| 11886 | .byval => false, | | |
| 11887 | }, | | |
| 11888 | else => false, // TODO: investigate other targets/callconvs | | |
| 11889 | }; | | |
| 11890 | } | | |
| 11891 | | | |
| 11892 | fn firstParamSRetSystemV(ty: Type, zcu: *Zcu, target: *const std.Target) bool { | | |
| 11893 | const class = x86_64_abi.classifySystemV(ty, zcu, target, .ret); | | |
| 11894 | if (class[0] == .memory) return true; | | |
| 11895 | if (class[0] == .x87 and class[2] != .none) return true; | | |
| 11896 | return false; | | |
| 11897 | } | | |
| 11898 | | | |
| 11899 | /// In order to support the C calling convention, some return types need to be lowered | | |
| 11900 | /// completely differently in the function prototype to honor the C ABI, and then | | |
| 11901 | /// be effectively bitcasted to the actual return type. | | |
| 11902 | fn lowerFnRetTy(o: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.FuncType) Allocator.Error!Builder.Type { | | |
| 11903 | const zcu = pt.zcu; | | |
| 11904 | const return_type = Type.fromInterned(fn_info.return_type); | | |
| 11905 | if (!return_type.hasRuntimeBits(zcu)) { | | |
| 11906 | assert(!return_type.isError(zcu)); | | |
| 11907 | return .void; | | |
| 11908 | } | | |
| 11909 | const target = zcu.getTarget(); | | |
| 11910 | switch (fn_info.cc) { | | |
| 11911 | .@"inline" => unreachable, | | |
| 11912 | .auto => return if (returnTypeByRef(zcu, target, return_type)) .void else o.lowerType(pt, return_type), | | |
| 11913 | | | |
| 11914 | .x86_64_sysv => return lowerSystemVFnRetTy(o, pt, fn_info), | | |
| 11915 | .x86_64_win => return lowerWin64FnRetTy(o, pt, fn_info), | | |
| 11916 | .x86_stdcall => return if (isScalar(zcu, return_type)) o.lowerType(pt, return_type) else .void, | | |
| 11917 | .x86_sysv, .x86_win => return if (isByRef(return_type, zcu)) .void else o.lowerType(pt, return_type), | | |
| 11918 | .aarch64_aapcs, .aarch64_aapcs_darwin, .aarch64_aapcs_win => switch (aarch64_c_abi.classifyType(return_type, zcu)) { | | |
| 11919 | .memory => return .void, | | |
| 11920 | .float_array => return o.lowerType(pt, return_type), | | |
| 11921 | .byval => return o.lowerType(pt, return_type), | | |
| 11922 | .integer => return o.builder.intType(@intCast(return_type.bitSize(zcu))), | | |
| 11923 | .double_integer => return o.builder.arrayType(2, .i64), | | |
| 11924 | }, | | |
| 11925 | .arm_aapcs, .arm_aapcs_vfp => switch (arm_c_abi.classifyType(return_type, zcu, .ret)) { | | |
| 11926 | .memory, .i64_array => return .void, | | |
| 11927 | .i32_array => |len| return if (len == 1) .i32 else .void, | | |
| 11928 | .byval => return o.lowerType(pt, return_type), | | |
| 11929 | }, | | |
| 11930 | .mips_o32 => switch (mips_c_abi.classifyType(return_type, zcu, .ret)) { | | |
| 11931 | .memory, .i32_array => return .void, | | |
| 11932 | .byval => return o.lowerType(pt, return_type), | | |
| 11933 | }, | | |
| 11934 | .riscv64_lp64, .riscv32_ilp32 => switch (riscv_c_abi.classifyType(return_type, zcu)) { | | |
| 11935 | .memory => return .void, | | |
| 11936 | .integer => return o.builder.intType(@intCast(return_type.bitSize(zcu))), | | |
| 11937 | .double_integer => { | | |
| 11938 | const integer: Builder.Type = switch (zcu.getTarget().cpu.arch) { | | |
| 11939 | .riscv64, .riscv64be => .i64, | | |
| 11940 | .riscv32, .riscv32be => .i32, | | |
| 11941 | else => unreachable, | | |
| 11942 | }; | | |
| 11943 | return o.builder.structType(.normal, &.{ integer, integer }); | | |
| 11944 | }, | | |
| 11945 | .byval => return o.lowerType(pt, return_type), | | |
| 11946 | .fields => { | | |
| 11947 | var types_len: usize = 0; | | |
| 11948 | var types: [8]Builder.Type = undefined; | | |
| 11949 | for (0..return_type.structFieldCount(zcu)) |field_index| { | | |
| 11950 | const field_ty = return_type.fieldType(field_index, zcu); | | |
| 11951 | if (!field_ty.hasRuntimeBits(zcu)) continue; | | |
| 11952 | types[types_len] = try o.lowerType(pt, field_ty); | | |
| 11953 | types_len += 1; | | |
| 11954 | } | | |
| 11955 | return o.builder.structType(.normal, types[0..types_len]); | | |
| 11956 | }, | | |
| 11957 | }, | | |
| 11958 | .wasm_mvp => switch (wasm_c_abi.classifyType(return_type, zcu)) { | | |
| 11959 | .direct => |scalar_ty| return o.lowerType(pt, scalar_ty), | | |
| 11960 | .indirect => return .void, | | |
| 11961 | }, | | |
| 11962 | // TODO investigate other callconvs | | |
| 11963 | else => return o.lowerType(pt, return_type), | | |
| 11964 | } | | |
| 11965 | } | | |
| 11966 | | | |
| 11967 | fn lowerWin64FnRetTy(o: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.FuncType) Allocator.Error!Builder.Type { | | |
| 11968 | const zcu = pt.zcu; | | |
| 11969 | const return_type = Type.fromInterned(fn_info.return_type); | | |
| 11970 | switch (x86_64_abi.classifyWindows(return_type, zcu, zcu.getTarget(), .ret)) { | | |
| 11971 | .integer => { | | |
| 11972 | if (isScalar(zcu, return_type)) { | | |
| 11973 | return o.lowerType(pt, return_type); | | |
| 11974 | } else { | | |
| 11975 | return o.builder.intType(@intCast(return_type.abiSize(zcu) * 8)); | | |
| 11976 | } | | |
| 11977 | }, | | |
| 11978 | .win_i128 => return o.builder.vectorType(.normal, 2, .i64), | | |
| 11979 | .memory => return .void, | | |
| 11980 | .sse => return o.lowerType(pt, return_type), | | |
| 11981 | else => unreachable, | | |
| 11982 | } | | |
| 11983 | } | | |
| 11984 | | | |
| 11985 | fn lowerSystemVFnRetTy(o: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.FuncType) Allocator.Error!Builder.Type { | | |
| 11986 | const zcu = pt.zcu; | | |
| 11987 | const ip = &zcu.intern_pool; | | |
| 11988 | const return_type = Type.fromInterned(fn_info.return_type); | | |
| 11989 | return_type.assertHasLayout(zcu); | | |
| 11990 | if (isScalar(zcu, return_type)) { | | |
| 11991 | return o.lowerType(pt, return_type); | | |
| 11992 | } | | |
| 11993 | const classes = x86_64_abi.classifySystemV(return_type, zcu, zcu.getTarget(), .ret); | | |
| 11994 | var types_index: u32 = 0; | | |
| 11995 | var types_buffer: [8]Builder.Type = undefined; | | |
| 11996 | for (classes) |class| { | | |
| 11997 | switch (class) { | | |
| 11998 | .integer => { | | |
| 11999 | types_buffer[types_index] = .i64; | | |
| 12000 | types_index += 1; | | |
| 12001 | }, | | |
| 12002 | .sse => { | | |
| 12003 | types_buffer[types_index] = .double; | | |
| 12004 | types_index += 1; | | |
| 12005 | }, | | |
| 12006 | .sseup => { | | |
| 12007 | if (types_buffer[types_index - 1] == .double) { | | |
| 12008 | types_buffer[types_index - 1] = .fp128; | | |
| 12009 | } else { | | |
| 12010 | types_buffer[types_index] = .double; | | |
| 12011 | types_index += 1; | | |
| 12012 | } | | |
| 12013 | }, | | |
| 12014 | .float => { | | |
| 12015 | types_buffer[types_index] = .float; | | |
| 12016 | types_index += 1; | | |
| 12017 | }, | | |
| 12018 | .float_combine => { | | |
| 12019 | types_buffer[types_index] = try o.builder.vectorType(.normal, 2, .float); | | |
| 12020 | types_index += 1; | | |
| 12021 | }, | | |
| 12022 | .x87 => { | | |
| 12023 | if (types_index != 0 or classes[2] != .none) return .void; | | |
| 12024 | types_buffer[types_index] = .x86_fp80; | | |
| 12025 | types_index += 1; | | |
| 12026 | }, | | |
| 12027 | .x87up => continue, | | |
| 12028 | .none => break, | | |
| 12029 | .memory, .integer_per_element => return .void, | | |
| 12030 | .win_i128 => unreachable, // windows only | | |
| 12031 | } | | |
| 12032 | } | | |
| 12033 | const first_non_integer = std.mem.indexOfNone(x86_64_abi.Class, &classes, &.{.integer}); | | |
| 12034 | if (first_non_integer == null or classes[first_non_integer.?] == .none) { | | |
| 12035 | assert(first_non_integer orelse classes.len == types_index); | | |
| 12036 | switch (ip.indexToKey(return_type.toIntern())) { | | |
| 12037 | .struct_type => { | | |
| 12038 | const size = return_type.abiSize(zcu); | | |
| 12039 | assert((std.math.divCeil(u64, size, 8) catch unreachable) == types_index); | | |
| 12040 | if (size % 8 > 0) { | | |
| 12041 | types_buffer[types_index - 1] = try o.builder.intType(@intCast(size % 8 * 8)); | | |
| 12042 | } | | |
| 12043 | }, | | |
| 12044 | else => {}, | | |
| 12045 | } | | |
| 12046 | if (types_index == 1) return types_buffer[0]; | | |
| 12047 | } | | |
| 12048 | return o.builder.structType(.normal, types_buffer[0..types_index]); | | |
| 12049 | } | | |
| 12050 | | | |
| 12051 | const ParamTypeIterator = struct { | | |
| 12052 | object: *Object, | | |
| 12053 | pt: Zcu.PerThread, | | |
| 12054 | fn_info: InternPool.Key.FuncType, | | |
| 12055 | zig_index: u32, | | |
| 12056 | llvm_index: u32, | | |
| 12057 | types_len: u32, | | |
| 12058 | types_buffer: [8]Builder.Type, | | |
| 12059 | byval_attr: bool, | | |
| 12060 | | | |
| 12061 | const Lowering = union(enum) { | | |
| 12062 | no_bits, | | |
| 12063 | byval, | | |
| 12064 | byref, | | |
| 12065 | byref_mut, | | |
| 12066 | abi_sized_int, | | |
| 12067 | multiple_llvm_types, | | |
| 12068 | slice, | | |
| 12069 | float_array: u8, | | |
| 12070 | i32_array: u8, | | |
| 12071 | i64_array: u8, | | |
| 12072 | }; | | |
| 12073 | | | |
| 12074 | fn next(it: *ParamTypeIterator) Allocator.Error!?Lowering { | | |
| 12075 | if (it.zig_index >= it.fn_info.param_types.len) return null; | | |
| 12076 | const ip = &it.pt.zcu.intern_pool; | | |
| 12077 | const ty = it.fn_info.param_types.get(ip)[it.zig_index]; | | |
| 12078 | it.byval_attr = false; | | |
| 12079 | return nextInner(it, Type.fromInterned(ty)); | | |
| 12080 | } | | |
| 12081 | | | |
| 12082 | /// `airCall` uses this instead of `next` so that it can take into account variadic functions. | | |
| 12083 | fn nextCall(it: *ParamTypeIterator, fg: *FuncGen, args: []const Air.Inst.Ref) Allocator.Error!?Lowering { | | |
| 12084 | assert(std.meta.eql(it.pt, fg.ng.pt)); | | |
| 12085 | const ip = &it.pt.zcu.intern_pool; | | |
| 12086 | if (it.zig_index >= it.fn_info.param_types.len) { | | |
| 12087 | if (it.zig_index >= args.len) { | | |
| 12088 | return null; | | |
| 12089 | } else { | | |
| 12090 | return nextInner(it, fg.typeOf(args[it.zig_index])); | | |
| 12091 | } | | |
| 12092 | } else { | | |
| 12093 | return nextInner(it, Type.fromInterned(it.fn_info.param_types.get(ip)[it.zig_index])); | | |
| 12094 | } | | |
| 12095 | } | | |
| 12096 | | | |
| 12097 | fn nextInner(it: *ParamTypeIterator, ty: Type) Allocator.Error!?Lowering { | | |
| 12098 | const pt = it.pt; | | |
| 12099 | const zcu = pt.zcu; | | |
| 12100 | const target = zcu.getTarget(); | | |
| 12101 | | | |
| 12102 | if (!ty.hasRuntimeBits(zcu)) { | | |
| 12103 | it.zig_index += 1; | | |
| 12104 | return .no_bits; | | |
| 12105 | } | | |
| 12106 | switch (it.fn_info.cc) { | | |
| 12107 | .@"inline" => unreachable, | | |
| 12108 | .auto => { | | |
| 12109 | it.zig_index += 1; | | |
| 12110 | it.llvm_index += 1; | | |
| 12111 | if (ty.isSlice(zcu) or | | |
| 12112 | (ty.zigTypeTag(zcu) == .optional and ty.optionalChild(zcu).isSlice(zcu) and !ty.ptrAllowsZero(zcu))) | | |
| 12113 | { | | |
| 12114 | it.llvm_index += 1; | | |
| 12115 | return .slice; | | |
| 12116 | } else if (isByRef(ty, zcu)) { | | |
| 12117 | return .byref; | | |
| 12118 | } else if (target.cpu.arch.isX86() and | | |
| 12119 | !target.cpu.has(.x86, .evex512) and | | |
| 12120 | ty.totalVectorBits(zcu) >= 512) | | |
| 12121 | { | | |
| 12122 | // As of LLVM 18, passing a vector byval with fastcc that is 512 bits or more returns | | |
| 12123 | // "512-bit vector arguments require 'evex512' for AVX512" | | |
| 12124 | return .byref; | | |
| 12125 | } else { | | |
| 12126 | return .byval; | | |
| 12127 | } | | |
| 12128 | }, | | |
| 12129 | .async => { | | |
| 12130 | @panic("TODO implement async function lowering in the LLVM backend"); | | |
| 12131 | }, | | |
| 12132 | .x86_64_sysv => return it.nextSystemV(ty), | | |
| 12133 | .x86_64_win => return it.nextWin64(ty), | | |
| 12134 | .x86_stdcall => { | | |
| 12135 | it.zig_index += 1; | | |
| 12136 | it.llvm_index += 1; | | |
| 12137 | | | |
| 12138 | if (isScalar(zcu, ty)) { | | |
| 12139 | return .byval; | | |
| 12140 | } else { | | |
| 12141 | it.byval_attr = true; | | |
| 12142 | return .byref; | | |
| 12143 | } | | |
| 12144 | }, | | |
| 12145 | .aarch64_aapcs, .aarch64_aapcs_darwin, .aarch64_aapcs_win => { | | |
| 12146 | it.zig_index += 1; | | |
| 12147 | it.llvm_index += 1; | | |
| 12148 | switch (aarch64_c_abi.classifyType(ty, zcu)) { | | |
| 12149 | .memory => return .byref_mut, | | |
| 12150 | .float_array => |len| return Lowering{ .float_array = len }, | | |
| 12151 | .byval => return .byval, | | |
| 12152 | .integer => { | | |
| 12153 | it.types_len = 1; | | |
| 12154 | it.types_buffer[0] = .i64; | | |
| 12155 | return .multiple_llvm_types; | | |
| 12156 | }, | | |
| 12157 | .double_integer => return Lowering{ .i64_array = 2 }, | | |
| 12158 | } | | |
| 12159 | }, | | |
| 12160 | .arm_aapcs, .arm_aapcs_vfp => { | | |
| 12161 | it.zig_index += 1; | | |
| 12162 | it.llvm_index += 1; | | |
| 12163 | switch (arm_c_abi.classifyType(ty, zcu, .arg)) { | | |
| 12164 | .memory => { | | |
| 12165 | it.byval_attr = true; | | |
| 12166 | return .byref; | | |
| 12167 | }, | | |
| 12168 | .byval => return .byval, | | |
| 12169 | .i32_array => |size| return Lowering{ .i32_array = size }, | | |
| 12170 | .i64_array => |size| return Lowering{ .i64_array = size }, | | |
| 12171 | } | | |
| 12172 | }, | | |
| 12173 | .mips_o32 => { | | |
| 12174 | it.zig_index += 1; | | |
| 12175 | it.llvm_index += 1; | | |
| 12176 | switch (mips_c_abi.classifyType(ty, zcu, .arg)) { | | |
| 12177 | .memory => { | | |
| 12178 | it.byval_attr = true; | | |
| 12179 | return .byref; | | |
| 12180 | }, | | |
| 12181 | .byval => return .byval, | | |
| 12182 | .i32_array => |size| return Lowering{ .i32_array = size }, | | |
| 12183 | } | | |
| 12184 | }, | | |
| 12185 | .riscv64_lp64, .riscv32_ilp32 => { | | |
| 12186 | it.zig_index += 1; | | |
| 12187 | it.llvm_index += 1; | | |
| 12188 | switch (riscv_c_abi.classifyType(ty, zcu)) { | | |
| 12189 | .memory => return .byref_mut, | | |
| 12190 | .byval => return .byval, | | |
| 12191 | .integer => return .abi_sized_int, | | |
| 12192 | .double_integer => return Lowering{ .i64_array = 2 }, | | |
| 12193 | .fields => { | | |
| 12194 | it.types_len = 0; | | |
| 12195 | for (0..ty.structFieldCount(zcu)) |field_index| { | | |
| 12196 | const field_ty = ty.fieldType(field_index, zcu); | | |
| 12197 | if (!field_ty.hasRuntimeBits(zcu)) continue; | | |
| 12198 | it.types_buffer[it.types_len] = try it.object.lowerType(pt, field_ty); | | |
| 12199 | it.types_len += 1; | | |
| 12200 | } | | |
| 12201 | it.llvm_index += it.types_len - 1; | | |
| 12202 | return .multiple_llvm_types; | | |
| 12203 | }, | | |
| 12204 | } | | |
| 12205 | }, | | |
| 12206 | .wasm_mvp => switch (wasm_c_abi.classifyType(ty, zcu)) { | | |
| 12207 | .direct => |scalar_ty| { | | |
| 12208 | if (isScalar(zcu, ty)) { | | |
| 12209 | it.zig_index += 1; | | |
| 12210 | it.llvm_index += 1; | | |
| 12211 | return .byval; | | |
| 12212 | } else { | | |
| 12213 | var types_buffer: [8]Builder.Type = undefined; | | |
| 12214 | types_buffer[0] = try it.object.lowerType(pt, scalar_ty); | | |
| 12215 | it.types_buffer = types_buffer; | | |
| 12216 | it.types_len = 1; | | |
| 12217 | it.llvm_index += 1; | | |
| 12218 | it.zig_index += 1; | | |
| 12219 | return .multiple_llvm_types; | | |
| 12220 | } | | |
| 12221 | }, | | |
| 12222 | .indirect => { | | |
| 12223 | it.zig_index += 1; | | |
| 12224 | it.llvm_index += 1; | | |
| 12225 | it.byval_attr = true; | | |
| 12226 | return .byref; | | |
| 12227 | }, | | |
| 12228 | }, | | |
| 12229 | // TODO investigate other callconvs | | |
| 12230 | else => { | | |
| 12231 | it.zig_index += 1; | | |
| 12232 | it.llvm_index += 1; | | |
| 12233 | return .byval; | | |
| 12234 | }, | | |
| 12235 | } | | |
| 12236 | } | | |
| 12237 | | | |
| 12238 | fn nextWin64(it: *ParamTypeIterator, ty: Type) ?Lowering { | | |
| 12239 | const zcu = it.pt.zcu; | | |
| 12240 | switch (x86_64_abi.classifyWindows(ty, zcu, zcu.getTarget(), .arg)) { | | |
| 12241 | .integer => { | | |
| 12242 | if (isScalar(zcu, ty)) { | | |
| 12243 | it.zig_index += 1; | | |
| 12244 | it.llvm_index += 1; | | |
| 12245 | return .byval; | | |
| 12246 | } else { | | |
| 12247 | it.zig_index += 1; | | |
| 12248 | it.llvm_index += 1; | | |
| 12249 | return .abi_sized_int; | | |
| 12250 | } | | |
| 12251 | }, | | |
| 12252 | .win_i128 => { | | |
| 12253 | it.zig_index += 1; | | |
| 12254 | it.llvm_index += 1; | | |
| 12255 | return .byref; | | |
| 12256 | }, | | |
| 12257 | .memory => { | | |
| 12258 | it.zig_index += 1; | | |
| 12259 | it.llvm_index += 1; | | |
| 12260 | return .byref_mut; | | |
| 12261 | }, | | |
| 12262 | .sse => { | | |
| 12263 | it.zig_index += 1; | | |
| 12264 | it.llvm_index += 1; | | |
| 12265 | return .byval; | | |
| 12266 | }, | | |
| 12267 | else => unreachable, | | |
| 12268 | } | | |
| 12269 | } | | |
| 12270 | | | |
| 12271 | fn nextSystemV(it: *ParamTypeIterator, ty: Type) Allocator.Error!?Lowering { | | |
| 12272 | const zcu = it.pt.zcu; | | |
| 12273 | const ip = &zcu.intern_pool; | | |
| 12274 | ty.assertHasLayout(zcu); | | |
| 12275 | const classes = x86_64_abi.classifySystemV(ty, zcu, zcu.getTarget(), .arg); | | |
| 12276 | if (classes[0] == .memory) { | | |
| 12277 | it.zig_index += 1; | | |
| 12278 | it.llvm_index += 1; | | |
| 12279 | it.byval_attr = true; | | |
| 12280 | return .byref; | | |
| 12281 | } | | |
| 12282 | if (isScalar(zcu, ty)) { | | |
| 12283 | it.zig_index += 1; | | |
| 12284 | it.llvm_index += 1; | | |
| 12285 | return .byval; | | |
| 12286 | } | | |
| 12287 | var types_index: u32 = 0; | | |
| 12288 | var types_buffer: [8]Builder.Type = undefined; | | |
| 12289 | for (classes) |class| { | | |
| 12290 | switch (class) { | | |
| 12291 | .integer => { | | |
| 12292 | types_buffer[types_index] = .i64; | | |
| 12293 | types_index += 1; | | |
| 12294 | }, | | |
| 12295 | .sse => { | | |
| 12296 | types_buffer[types_index] = .double; | | |
| 12297 | types_index += 1; | | |
| 12298 | }, | | |
| 12299 | .sseup => { | | |
| 12300 | if (types_buffer[types_index - 1] == .double) { | | |
| 12301 | types_buffer[types_index - 1] = .fp128; | | |
| 12302 | } else { | | |
| 12303 | types_buffer[types_index] = .double; | | |
| 12304 | types_index += 1; | | |
| 12305 | } | | |
| 12306 | }, | | |
| 12307 | .float => { | | |
| 12308 | types_buffer[types_index] = .float; | | |
| 12309 | types_index += 1; | | |
| 12310 | }, | | |
| 12311 | .float_combine => { | | |
| 12312 | types_buffer[types_index] = try it.object.builder.vectorType(.normal, 2, .float); | | |
| 12313 | types_index += 1; | | |
| 12314 | }, | | |
| 12315 | .x87 => { | | |
| 12316 | it.zig_index += 1; | | |
| 12317 | it.llvm_index += 1; | | |
| 12318 | it.byval_attr = true; | | |
| 12319 | return .byref; | | |
| 12320 | }, | | |
| 12321 | .x87up => unreachable, | | |
| 12322 | .none => break, | | |
| 12323 | .memory => unreachable, // handled above | | |
| 12324 | .win_i128 => unreachable, // windows only | | |
| 12325 | .integer_per_element => { | | |
| 12326 | @panic("TODO"); | | |
| 12327 | }, | | |
| 12328 | } | | |
| 12329 | } | | |
| 12330 | const first_non_integer = std.mem.indexOfNone(x86_64_abi.Class, &classes, &.{.integer}); | | |
| 12331 | if (first_non_integer == null or classes[first_non_integer.?] == .none) { | | |
| 12332 | assert(first_non_integer orelse classes.len == types_index); | | |
| 12333 | if (types_index == 1) { | | |
| 12334 | it.zig_index += 1; | | |
| 12335 | it.llvm_index += 1; | | |
| 12336 | return .abi_sized_int; | | |
| 12337 | } | | |
| 12338 | if (it.llvm_index + types_index > 6) { | | |
| 12339 | it.zig_index += 1; | | |
| 12340 | it.llvm_index += 1; | | |
| 12341 | it.byval_attr = true; | | |
| 12342 | return .byref; | | |
| 12343 | } | | |
| 12344 | switch (ip.indexToKey(ty.toIntern())) { | | |
| 12345 | .struct_type => { | | |
| 12346 | const size = ty.abiSize(zcu); | | |
| 12347 | assert((std.math.divCeil(u64, size, 8) catch unreachable) == types_index); | | |
| 12348 | if (size % 8 > 0) { | | |
| 12349 | types_buffer[types_index - 1] = | | |
| 12350 | try it.object.builder.intType(@intCast(size % 8 * 8)); | | |
| 12351 | } | | |
| 12352 | }, | | |
| 12353 | else => {}, | | |
| 12354 | } | | |
| 12355 | } | | |
| 12356 | it.types_len = types_index; | | |
| 12357 | it.types_buffer = types_buffer; | | |
| 12358 | it.llvm_index += types_index; | | |
| 12359 | it.zig_index += 1; | | |
| 12360 | return .multiple_llvm_types; | | |
| 12361 | } | | |
| 12362 | }; | | |
| 12363 | | | |
| 12364 | fn iterateParamTypes(object: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.FuncType) ParamTypeIterator { | | |
| 12365 | return .{ | | |
| 12366 | .object = object, | | |
| 12367 | .pt = pt, | | |
| 12368 | .fn_info = fn_info, | | |
| 12369 | .zig_index = 0, | | |
| 12370 | .llvm_index = 0, | | |
| 12371 | .types_len = 0, | | |
| 12372 | .types_buffer = undefined, | | |
| 12373 | .byval_attr = false, | | |
| 12374 | }; | | |
| 12375 | } | | |
| 12376 | | | |
| 12377 | /// This function deliberately does not handle `_BitInt` because it typically | | |
| 12378 | /// has different ABI than regular integer types, and there is no currently no | | |
| 12379 | /// way to determine whether a Zig integer type is meant to represent e.g. `int` | | |
| 12380 | /// or `_BitInt(32)`. | | |
| 12381 | fn ccAbiPromoteInt(cc: std.builtin.CallingConvention, zcu: *Zcu, ty: Type) ?std.builtin.Signedness { | | |
| 12382 | switch (cc) { | | |
| 12383 | .auto, .@"inline", .async => return null, | | |
| 12384 | else => {}, | | |
| 12385 | } | | |
| 12386 | | | |
| 12387 | const int_info = switch (ty.zigTypeTag(zcu)) { | | |
| 12388 | .bool => Type.u1.intInfo(zcu), | | |
| 12389 | else => if (ty.isAbiInt(zcu)) ty.intInfo(zcu) else return null, | | |
| 12390 | }; | | |
| 12391 | assert(int_info.bits >= 0); | | |
| 12392 | | | |
| 12393 | const target = zcu.getTarget(); | | |
| 12394 | return switch (target.cpu.arch) { | | |
| 12395 | .aarch64, | | |
| 12396 | .aarch64_be, | | |
| 12397 | => switch (target.os.tag) { | | |
| 12398 | .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => switch (int_info.bits) { | | |
| 12399 | 8, 16 => int_info.signedness, | | |
| 12400 | else => null, | | |
| 12401 | }, | | |
| 12402 | else => null, | | |
| 12403 | }, | | |
| 12404 | | | |
| 12405 | .avr, | | |
| 12406 | => switch (int_info.bits) { | | |
| 12407 | 8 => int_info.signedness, | | |
| 12408 | else => null, | | |
| 12409 | }, | | |
| 12410 | | | |
| 12411 | .lanai, | | |
| 12412 | => null, | | |
| 12413 | | | |
| 12414 | .loongarch64, | | |
| 12415 | .riscv64, | | |
| 12416 | .riscv64be, | | |
| 12417 | => switch (int_info.bits) { | | |
| 12418 | 8, 16 => int_info.signedness, | | |
| 12419 | 32 => .signed, | | |
| 12420 | else => null, | | |
| 12421 | }, | | |
| 12422 | | | |
| 12423 | .mips, | | |
| 12424 | .mipsel, | | |
| 12425 | .mips64, | | |
| 12426 | .mips64el, | | |
| 12427 | => switch (int_info.bits) { | | |
| 12428 | 8, 16, 64 => int_info.signedness, | | |
| 12429 | // https://github.com/llvm/llvm-project/issues/179088 | | |
| 12430 | // 32 => .signed, | | |
| 12431 | else => null, | | |
| 12432 | }, | | |
| 12433 | | | |
| 12434 | .powerpc64, | | |
| 12435 | .powerpc64le, | | |
| 12436 | .s390x, | | |
| 12437 | .sparc64, | | |
| 12438 | .ve, | | |
| 12439 | => switch (int_info.bits) { | | |
| 12440 | 8, 16, 32 => int_info.signedness, | | |
| 12441 | else => null, | | |
| 12442 | }, | | |
| 12443 | | | |
| 12444 | else => switch (int_info.bits) { | | |
| 12445 | 8, 16 => int_info.signedness, | | |
| 12446 | else => null, | | |
| 12447 | }, | | |
| 12448 | }; | | |
| 12449 | } | | |
| 12450 | | | |
| 12451 | /// This is the one source of truth for whether a type is passed around as an LLVM pointer, | | |
| 12452 | /// or as an LLVM value. | | |
| 12453 | fn isByRef(ty: Type, zcu: *Zcu) bool { | | |
| 12454 | // For tuples and structs, if there are more than this many non-void | | |
| 12455 | // fields, then we make it byref, otherwise byval. | | |
| 12456 | const max_fields_byval = 0; | | |
| 12457 | const ip = &zcu.intern_pool; | | |
| 12458 | | | |
| 12459 | switch (ty.zigTypeTag(zcu)) { | | |
| 12460 | .type, | | |
| 12461 | .comptime_int, | | |
| 12462 | .comptime_float, | | |
| 12463 | .enum_literal, | | |
| 12464 | .undefined, | | |
| 12465 | .null, | | |
| 12466 | .@"opaque", | | |
| 12467 | => unreachable, | | |
| 12468 | | | |
| 12469 | .noreturn, | | |
| 12470 | .void, | | |
| 12471 | .bool, | | |
| 12472 | .int, | | |
| 12473 | .float, | | |
| 12474 | .pointer, | | |
| 12475 | .error_set, | | |
| 12476 | .@"fn", | | |
| 12477 | .@"enum", | | |
| 12478 | .vector, | | |
| 12479 | .@"anyframe", | | |
| 12480 | => return false, | | |
| 12481 | | | |
| 12482 | .array, .frame => return ty.hasRuntimeBits(zcu), | | |
| 12483 | .@"struct" => { | | |
| 12484 | const struct_type = switch (ip.indexToKey(ty.toIntern())) { | | |
| 12485 | .tuple_type => |tuple| { | | |
| 12486 | var count: usize = 0; | | |
| 12487 | for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, field_val| { | | |
| 12488 | if (field_val != .none or !Type.fromInterned(field_ty).hasRuntimeBits(zcu)) continue; | | |
| 12489 | | | |
| 12490 | count += 1; | | |
| 12491 | if (count > max_fields_byval) return true; | | |
| 12492 | if (isByRef(Type.fromInterned(field_ty), zcu)) return true; | | |
| 12493 | } | | |
| 12494 | return false; | | |
| 12495 | }, | | |
| 12496 | .struct_type => ip.loadStructType(ty.toIntern()), | | |
| 12497 | else => unreachable, | | |
| 12498 | }; | | |
| 12499 | | | |
| 12500 | // Packed structs are represented to LLVM as integers. | | |
| 12501 | if (struct_type.layout == .@"packed") return false; | | |
| 12502 | | | |
| 12503 | const field_types = struct_type.field_types.get(ip); | | |
| 12504 | var it = struct_type.iterateRuntimeOrder(ip); | | |
| 12505 | var count: usize = 0; | | |
| 12506 | while (it.next()) |field_index| { | | |
| 12507 | count += 1; | | |
| 12508 | if (count > max_fields_byval) return true; | | |
| 12509 | const field_ty = Type.fromInterned(field_types[field_index]); | | |
| 12510 | if (isByRef(field_ty, zcu)) return true; | | |
| 12511 | } | | |
| 12512 | return false; | | |
| 12513 | }, | | |
| 12514 | .@"union" => switch (ty.containerLayout(zcu)) { | | |
| 12515 | .@"packed" => return false, | | |
| 12516 | else => return ty.hasRuntimeBits(zcu) and !ty.unionHasAllZeroBitFieldTypes(zcu), | | |
| 12517 | }, | | |
| 12518 | .error_union => { | | |
| 12519 | const payload_ty = ty.errorUnionPayload(zcu); | | |
| 12520 | if (!payload_ty.hasRuntimeBits(zcu)) { | | |
| 12521 | return false; | | |
| 12522 | } | | |
| 12523 | return true; | | |
| 12524 | }, | | |
| 12525 | .optional => { | | |
| 12526 | const payload_ty = ty.optionalChild(zcu); | | |
| 12527 | if (!payload_ty.hasRuntimeBits(zcu)) { | | |
| 12528 | return false; | | |
| 12529 | } | | |
| 12530 | if (ty.optionalReprIsPayload(zcu)) { | | |
| 12531 | return false; | | |
| 12532 | } | | |
| 12533 | return true; | | |
| 12534 | }, | | |
| 12535 | } | | |
| 12536 | } | | |
| 12537 | | | |
| 12538 | fn isScalar(zcu: *Zcu, ty: Type) bool { | | |
| 12539 | return switch (ty.zigTypeTag(zcu)) { | | |
| 12540 | .void, | | |
| 12541 | .bool, | | |
| 12542 | .noreturn, | | |
| 12543 | .int, | | |
| 12544 | .float, | | |
| 12545 | .pointer, | | |
| 12546 | .optional, | | |
| 12547 | .error_set, | | |
| 12548 | .@"enum", | | |
| 12549 | .@"anyframe", | | |
| 12550 | .vector, | | |
| 12551 | => true, | | |
| 12552 | | | |
| 12553 | .@"struct" => ty.containerLayout(zcu) == .@"packed", | | |
| 12554 | .@"union" => ty.containerLayout(zcu) == .@"packed", | | |
| 12555 | else => false, | | |
| 12556 | }; | | |
| 12557 | } | | |
| 12558 | | | |
| 12559 | /// This function returns true if we expect LLVM to lower x86_fp80 correctly | | |
| 12560 | /// and false if we expect LLVM to crash if it encounters an x86_fp80 type, | | |
| 12561 | /// or if it produces miscompilations. | | |
| 12562 | fn backendSupportsF80(target: *const std.Target) bool { | | |
| 12563 | return switch (target.cpu.arch) { | | |
| 12564 | .x86, .x86_64 => !target.cpu.has(.x86, .soft_float), | | |
| 12565 | else => false, | | |
| 12566 | }; | | |
| 12567 | } | | |
| 12568 | | | |
| 12569 | /// This function returns true if we expect LLVM to lower f16 correctly | 4783 | /// This function returns true if we expect LLVM to lower f16 correctly |
| 12570 | /// and false if we expect LLVM to crash if it encounters an f16 type, | 4784 | /// and false if we expect LLVM to crash if it encounters an f16 type, |
| 12571 | /// or if it produces miscompilations. | 4785 | /// or if it produces miscompilations. |
| 12572 | fn backendSupportsF16(target: *const std.Target) bool { | 4786 | pub fn backendSupportsF16(target: *const std.Target) bool { |
| 12573 | return switch (target.cpu.arch) { | 4787 | return switch (target.cpu.arch) { |
| 12574 | // https://github.com/llvm/llvm-project/issues/97981 | 4788 | // https://github.com/llvm/llvm-project/issues/97981 |
| 12575 | .csky, | 4789 | .csky, |
| ... | @@ -12594,10 +4808,20 @@ fn backendSupportsF16(target: *const std.Target) bool { | ... | @@ -12594,10 +4808,20 @@ fn backendSupportsF16(target: *const std.Target) bool { |
| 12594 | }; | 4808 | }; |
| 12595 | } | 4809 | } |
| 12596 | | 4810 | |
| | 4811 | /// This function returns true if we expect LLVM to lower x86_fp80 correctly |
| | 4812 | /// and false if we expect LLVM to crash if it encounters an x86_fp80 type, |
| | 4813 | /// or if it produces miscompilations. |
| | 4814 | pub fn backendSupportsF80(target: *const std.Target) bool { |
| | 4815 | return switch (target.cpu.arch) { |
| | 4816 | .x86, .x86_64 => !target.cpu.has(.x86, .soft_float), |
| | 4817 | else => false, |
| | 4818 | }; |
| | 4819 | } |
| | 4820 | |
| 12597 | /// This function returns true if we expect LLVM to lower f128 correctly, | 4821 | /// This function returns true if we expect LLVM to lower f128 correctly, |
| 12598 | /// and false if we expect LLVM to crash if it encounters an f128 type, | 4822 | /// and false if we expect LLVM to crash if it encounters an f128 type, |
| 12599 | /// or if it produces miscompilations. | 4823 | /// or if it produces miscompilations. |
| 12600 | fn backendSupportsF128(target: *const std.Target) bool { | 4824 | pub fn backendSupportsF128(target: *const std.Target) bool { |
| 12601 | return switch (target.cpu.arch) { | 4825 | return switch (target.cpu.arch) { |
| 12602 | // https://github.com/llvm/llvm-project/issues/121122 | 4826 | // https://github.com/llvm/llvm-project/issues/121122 |
| 12603 | .amdgcn, | 4827 | .amdgcn, |
| ... | @@ -12616,17 +4840,6 @@ fn backendSupportsF128(target: *const std.Target) bool { | ... | @@ -12616,17 +4840,6 @@ fn backendSupportsF128(target: *const std.Target) bool { |
| 12616 | }; | 4840 | }; |
| 12617 | } | 4841 | } |
| 12618 | | 4842 | |
| 12619 | /// LLVM does not support all relevant intrinsics for all targets, so we | | |
| 12620 | /// may need to manually generate a compiler-rt call. | | |
| 12621 | fn intrinsicsAllowed(scalar_ty: Type, target: *const std.Target) bool { | | |
| 12622 | return switch (scalar_ty.toIntern()) { | | |
| 12623 | .f16_type => backendSupportsF16(target), | | |
| 12624 | .f80_type => (target.cTypeBitSize(.longdouble) == 80) and backendSupportsF80(target), | | |
| 12625 | .f128_type => (target.cTypeBitSize(.longdouble) == 128) and backendSupportsF128(target), | | |
| 12626 | else => true, | | |
| 12627 | }; | | |
| 12628 | } | | |
| 12629 | | | |
| 12630 | /// We need to insert extra padding if LLVM's isn't enough. | 4843 | /// We need to insert extra padding if LLVM's isn't enough. |
| 12631 | /// However we don't want to ever call LLVMABIAlignmentOfType or | 4844 | /// However we don't want to ever call LLVMABIAlignmentOfType or |
| 12632 | /// LLVMABISizeOfType because these functions will trip assertions | 4845 | /// LLVMABISizeOfType because these functions will trip assertions |
| ... | @@ -12638,264 +4851,188 @@ const struct_layout_version = 2; | ... | @@ -12638,264 +4851,188 @@ const struct_layout_version = 2; |
| 12638 | | 4851 | |
| 12639 | // TODO: Restore the non_null field to i1 once | 4852 | // TODO: Restore the non_null field to i1 once |
| 12640 | // https://github.com/llvm/llvm-project/issues/56585/ is fixed | 4853 | // https://github.com/llvm/llvm-project/issues/56585/ is fixed |
| 12641 | const optional_layout_version = 3; | 4854 | pub const optional_layout_version = 3; |
| 12642 | | 4855 | |
| 12643 | const lt_errors_fn_name = "__zig_lt_errors_len"; | 4856 | const lt_errors_fn_name = "__zig_lt_errors_len"; |
| 12644 | | 4857 | |
| 12645 | fn compilerRtIntBits(bits: u16) ?u16 { | | |
| 12646 | inline for (.{ 32, 64, 128 }) |b| { | | |
| 12647 | if (bits <= b) { | | |
| 12648 | return b; | | |
| 12649 | } | | |
| 12650 | } | | |
| 12651 | return null; | | |
| 12652 | } | | |
| 12653 | | | |
| 12654 | fn buildAllocaInner( | | |
| 12655 | wip: *Builder.WipFunction, | | |
| 12656 | llvm_ty: Builder.Type, | | |
| 12657 | alignment: Builder.Alignment, | | |
| 12658 | target: *const std.Target, | | |
| 12659 | ) Allocator.Error!Builder.Value { | | |
| 12660 | const address_space = llvmAllocaAddressSpace(target); | | |
| 12661 | | | |
| 12662 | const alloca = blk: { | | |
| 12663 | const prev_cursor = wip.cursor; | | |
| 12664 | const prev_debug_location = wip.debug_location; | | |
| 12665 | defer { | | |
| 12666 | wip.cursor = prev_cursor; | | |
| 12667 | if (wip.cursor.block == .entry) wip.cursor.instruction += 1; | | |
| 12668 | wip.debug_location = prev_debug_location; | | |
| 12669 | } | | |
| 12670 | | | |
| 12671 | wip.cursor = .{ .block = .entry }; | | |
| 12672 | wip.debug_location = .no_location; | | |
| 12673 | break :blk try wip.alloca(.normal, llvm_ty, .none, alignment, address_space, ""); | | |
| 12674 | }; | | |
| 12675 | | | |
| 12676 | // The pointer returned from this function should have the generic address space, | | |
| 12677 | // if this isn't the case then cast it to the generic address space. | | |
| 12678 | return wip.conv(.unneeded, alloca, .ptr, ""); | | |
| 12679 | } | | |
| 12680 | | | |
| 12681 | fn errUnionPayloadOffset(payload_ty: Type, pt: Zcu.PerThread) !u1 { | | |
| 12682 | const zcu = pt.zcu; | | |
| 12683 | const err_int_ty = try pt.errorIntType(); | | |
| 12684 | return @intFromBool(err_int_ty.abiAlignment(zcu).compare(.gt, payload_ty.abiAlignment(zcu))); | | |
| 12685 | } | | |
| 12686 | | | |
| 12687 | fn errUnionErrorOffset(payload_ty: Type, pt: Zcu.PerThread) !u1 { | | |
| 12688 | const zcu = pt.zcu; | | |
| 12689 | const err_int_ty = try pt.errorIntType(); | | |
| 12690 | return @intFromBool(err_int_ty.abiAlignment(zcu).compare(.lte, payload_ty.abiAlignment(zcu))); | | |
| 12691 | } | | |
| 12692 | | | |
| 12693 | /// Returns true for asm constraint (e.g. "=*m", "=r") if it accepts a memory location | | |
| 12694 | /// | | |
| 12695 | /// See also TargetInfo::validateOutputConstraint, AArch64TargetInfo::validateAsmConstraint, etc. in Clang | | |
| 12696 | fn constraintAllowsMemory(constraint: []const u8) bool { | | |
| 12697 | // TODO: This implementation is woefully incomplete. | | |
| 12698 | for (constraint) |byte| { | | |
| 12699 | switch (byte) { | | |
| 12700 | '=', '*', ',', '&' => {}, | | |
| 12701 | 'm', 'o', 'X', 'g' => return true, | | |
| 12702 | else => {}, | | |
| 12703 | } | | |
| 12704 | } else return false; | | |
| 12705 | } | | |
| 12706 | | | |
| 12707 | /// Returns true for asm constraint (e.g. "=*m", "=r") if it accepts a register | | |
| 12708 | /// | | |
| 12709 | /// See also TargetInfo::validateOutputConstraint, AArch64TargetInfo::validateAsmConstraint, etc. in Clang | | |
| 12710 | fn constraintAllowsRegister(constraint: []const u8) bool { | | |
| 12711 | // TODO: This implementation is woefully incomplete. | | |
| 12712 | for (constraint) |byte| { | | |
| 12713 | switch (byte) { | | |
| 12714 | '=', '*', ',', '&' => {}, | | |
| 12715 | 'm', 'o' => {}, | | |
| 12716 | else => return true, | | |
| 12717 | } | | |
| 12718 | } else return false; | | |
| 12719 | } | | |
| 12720 | | | |
| 12721 | pub fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void { | 4858 | pub fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void { |
| 12722 | switch (arch) { | 4859 | switch (arch) { |
| 12723 | .aarch64, .aarch64_be => { | 4860 | .aarch64, .aarch64_be => { |
| 12724 | llvm.LLVMInitializeAArch64Target(); | 4861 | bindings.LLVMInitializeAArch64Target(); |
| 12725 | llvm.LLVMInitializeAArch64TargetInfo(); | 4862 | bindings.LLVMInitializeAArch64TargetInfo(); |
| 12726 | llvm.LLVMInitializeAArch64TargetMC(); | 4863 | bindings.LLVMInitializeAArch64TargetMC(); |
| 12727 | llvm.LLVMInitializeAArch64AsmPrinter(); | 4864 | bindings.LLVMInitializeAArch64AsmPrinter(); |
| 12728 | llvm.LLVMInitializeAArch64AsmParser(); | 4865 | bindings.LLVMInitializeAArch64AsmParser(); |
| 12729 | }, | 4866 | }, |
| 12730 | .amdgcn => { | 4867 | .amdgcn => { |
| 12731 | llvm.LLVMInitializeAMDGPUTarget(); | 4868 | bindings.LLVMInitializeAMDGPUTarget(); |
| 12732 | llvm.LLVMInitializeAMDGPUTargetInfo(); | 4869 | bindings.LLVMInitializeAMDGPUTargetInfo(); |
| 12733 | llvm.LLVMInitializeAMDGPUTargetMC(); | 4870 | bindings.LLVMInitializeAMDGPUTargetMC(); |
| 12734 | llvm.LLVMInitializeAMDGPUAsmPrinter(); | 4871 | bindings.LLVMInitializeAMDGPUAsmPrinter(); |
| 12735 | llvm.LLVMInitializeAMDGPUAsmParser(); | 4872 | bindings.LLVMInitializeAMDGPUAsmParser(); |
| 12736 | }, | 4873 | }, |
| 12737 | .thumb, .thumbeb, .arm, .armeb => { | 4874 | .thumb, .thumbeb, .arm, .armeb => { |
| 12738 | llvm.LLVMInitializeARMTarget(); | 4875 | bindings.LLVMInitializeARMTarget(); |
| 12739 | llvm.LLVMInitializeARMTargetInfo(); | 4876 | bindings.LLVMInitializeARMTargetInfo(); |
| 12740 | llvm.LLVMInitializeARMTargetMC(); | 4877 | bindings.LLVMInitializeARMTargetMC(); |
| 12741 | llvm.LLVMInitializeARMAsmPrinter(); | 4878 | bindings.LLVMInitializeARMAsmPrinter(); |
| 12742 | llvm.LLVMInitializeARMAsmParser(); | 4879 | bindings.LLVMInitializeARMAsmParser(); |
| 12743 | }, | 4880 | }, |
| 12744 | .avr => { | 4881 | .avr => { |
| 12745 | llvm.LLVMInitializeAVRTarget(); | 4882 | bindings.LLVMInitializeAVRTarget(); |
| 12746 | llvm.LLVMInitializeAVRTargetInfo(); | 4883 | bindings.LLVMInitializeAVRTargetInfo(); |
| 12747 | llvm.LLVMInitializeAVRTargetMC(); | 4884 | bindings.LLVMInitializeAVRTargetMC(); |
| 12748 | llvm.LLVMInitializeAVRAsmPrinter(); | 4885 | bindings.LLVMInitializeAVRAsmPrinter(); |
| 12749 | llvm.LLVMInitializeAVRAsmParser(); | 4886 | bindings.LLVMInitializeAVRAsmParser(); |
| 12750 | }, | 4887 | }, |
| 12751 | .bpfel, .bpfeb => { | 4888 | .bpfel, .bpfeb => { |
| 12752 | llvm.LLVMInitializeBPFTarget(); | 4889 | bindings.LLVMInitializeBPFTarget(); |
| 12753 | llvm.LLVMInitializeBPFTargetInfo(); | 4890 | bindings.LLVMInitializeBPFTargetInfo(); |
| 12754 | llvm.LLVMInitializeBPFTargetMC(); | 4891 | bindings.LLVMInitializeBPFTargetMC(); |
| 12755 | llvm.LLVMInitializeBPFAsmPrinter(); | 4892 | bindings.LLVMInitializeBPFAsmPrinter(); |
| 12756 | llvm.LLVMInitializeBPFAsmParser(); | 4893 | bindings.LLVMInitializeBPFAsmParser(); |
| 12757 | }, | 4894 | }, |
| 12758 | .hexagon => { | 4895 | .hexagon => { |
| 12759 | llvm.LLVMInitializeHexagonTarget(); | 4896 | bindings.LLVMInitializeHexagonTarget(); |
| 12760 | llvm.LLVMInitializeHexagonTargetInfo(); | 4897 | bindings.LLVMInitializeHexagonTargetInfo(); |
| 12761 | llvm.LLVMInitializeHexagonTargetMC(); | 4898 | bindings.LLVMInitializeHexagonTargetMC(); |
| 12762 | llvm.LLVMInitializeHexagonAsmPrinter(); | 4899 | bindings.LLVMInitializeHexagonAsmPrinter(); |
| 12763 | llvm.LLVMInitializeHexagonAsmParser(); | 4900 | bindings.LLVMInitializeHexagonAsmParser(); |
| 12764 | }, | 4901 | }, |
| 12765 | .lanai => { | 4902 | .lanai => { |
| 12766 | llvm.LLVMInitializeLanaiTarget(); | 4903 | bindings.LLVMInitializeLanaiTarget(); |
| 12767 | llvm.LLVMInitializeLanaiTargetInfo(); | 4904 | bindings.LLVMInitializeLanaiTargetInfo(); |
| 12768 | llvm.LLVMInitializeLanaiTargetMC(); | 4905 | bindings.LLVMInitializeLanaiTargetMC(); |
| 12769 | llvm.LLVMInitializeLanaiAsmPrinter(); | 4906 | bindings.LLVMInitializeLanaiAsmPrinter(); |
| 12770 | llvm.LLVMInitializeLanaiAsmParser(); | 4907 | bindings.LLVMInitializeLanaiAsmParser(); |
| 12771 | }, | 4908 | }, |
| 12772 | .mips, .mipsel, .mips64, .mips64el => { | 4909 | .mips, .mipsel, .mips64, .mips64el => { |
| 12773 | llvm.LLVMInitializeMipsTarget(); | 4910 | bindings.LLVMInitializeMipsTarget(); |
| 12774 | llvm.LLVMInitializeMipsTargetInfo(); | 4911 | bindings.LLVMInitializeMipsTargetInfo(); |
| 12775 | llvm.LLVMInitializeMipsTargetMC(); | 4912 | bindings.LLVMInitializeMipsTargetMC(); |
| 12776 | llvm.LLVMInitializeMipsAsmPrinter(); | 4913 | bindings.LLVMInitializeMipsAsmPrinter(); |
| 12777 | llvm.LLVMInitializeMipsAsmParser(); | 4914 | bindings.LLVMInitializeMipsAsmParser(); |
| 12778 | }, | 4915 | }, |
| 12779 | .msp430 => { | 4916 | .msp430 => { |
| 12780 | llvm.LLVMInitializeMSP430Target(); | 4917 | bindings.LLVMInitializeMSP430Target(); |
| 12781 | llvm.LLVMInitializeMSP430TargetInfo(); | 4918 | bindings.LLVMInitializeMSP430TargetInfo(); |
| 12782 | llvm.LLVMInitializeMSP430TargetMC(); | 4919 | bindings.LLVMInitializeMSP430TargetMC(); |
| 12783 | llvm.LLVMInitializeMSP430AsmPrinter(); | 4920 | bindings.LLVMInitializeMSP430AsmPrinter(); |
| 12784 | llvm.LLVMInitializeMSP430AsmParser(); | 4921 | bindings.LLVMInitializeMSP430AsmParser(); |
| 12785 | }, | 4922 | }, |
| 12786 | .nvptx, .nvptx64 => { | 4923 | .nvptx, .nvptx64 => { |
| 12787 | llvm.LLVMInitializeNVPTXTarget(); | 4924 | bindings.LLVMInitializeNVPTXTarget(); |
| 12788 | llvm.LLVMInitializeNVPTXTargetInfo(); | 4925 | bindings.LLVMInitializeNVPTXTargetInfo(); |
| 12789 | llvm.LLVMInitializeNVPTXTargetMC(); | 4926 | bindings.LLVMInitializeNVPTXTargetMC(); |
| 12790 | llvm.LLVMInitializeNVPTXAsmPrinter(); | 4927 | bindings.LLVMInitializeNVPTXAsmPrinter(); |
| 12791 | // There is no LLVMInitializeNVPTXAsmParser function available. | 4928 | // There is no LLVMInitializeNVPTXAsmParser function available. |
| 12792 | }, | 4929 | }, |
| 12793 | .powerpc, .powerpcle, .powerpc64, .powerpc64le => { | 4930 | .powerpc, .powerpcle, .powerpc64, .powerpc64le => { |
| 12794 | llvm.LLVMInitializePowerPCTarget(); | 4931 | bindings.LLVMInitializePowerPCTarget(); |
| 12795 | llvm.LLVMInitializePowerPCTargetInfo(); | 4932 | bindings.LLVMInitializePowerPCTargetInfo(); |
| 12796 | llvm.LLVMInitializePowerPCTargetMC(); | 4933 | bindings.LLVMInitializePowerPCTargetMC(); |
| 12797 | llvm.LLVMInitializePowerPCAsmPrinter(); | 4934 | bindings.LLVMInitializePowerPCAsmPrinter(); |
| 12798 | llvm.LLVMInitializePowerPCAsmParser(); | 4935 | bindings.LLVMInitializePowerPCAsmParser(); |
| 12799 | }, | 4936 | }, |
| 12800 | .riscv32, .riscv32be, .riscv64, .riscv64be => { | 4937 | .riscv32, .riscv32be, .riscv64, .riscv64be => { |
| 12801 | llvm.LLVMInitializeRISCVTarget(); | 4938 | bindings.LLVMInitializeRISCVTarget(); |
| 12802 | llvm.LLVMInitializeRISCVTargetInfo(); | 4939 | bindings.LLVMInitializeRISCVTargetInfo(); |
| 12803 | llvm.LLVMInitializeRISCVTargetMC(); | 4940 | bindings.LLVMInitializeRISCVTargetMC(); |
| 12804 | llvm.LLVMInitializeRISCVAsmPrinter(); | 4941 | bindings.LLVMInitializeRISCVAsmPrinter(); |
| 12805 | llvm.LLVMInitializeRISCVAsmParser(); | 4942 | bindings.LLVMInitializeRISCVAsmParser(); |
| 12806 | }, | 4943 | }, |
| 12807 | .sparc, .sparc64 => { | 4944 | .sparc, .sparc64 => { |
| 12808 | llvm.LLVMInitializeSparcTarget(); | 4945 | bindings.LLVMInitializeSparcTarget(); |
| 12809 | llvm.LLVMInitializeSparcTargetInfo(); | 4946 | bindings.LLVMInitializeSparcTargetInfo(); |
| 12810 | llvm.LLVMInitializeSparcTargetMC(); | 4947 | bindings.LLVMInitializeSparcTargetMC(); |
| 12811 | llvm.LLVMInitializeSparcAsmPrinter(); | 4948 | bindings.LLVMInitializeSparcAsmPrinter(); |
| 12812 | llvm.LLVMInitializeSparcAsmParser(); | 4949 | bindings.LLVMInitializeSparcAsmParser(); |
| 12813 | }, | 4950 | }, |
| 12814 | .s390x => { | 4951 | .s390x => { |
| 12815 | llvm.LLVMInitializeSystemZTarget(); | 4952 | bindings.LLVMInitializeSystemZTarget(); |
| 12816 | llvm.LLVMInitializeSystemZTargetInfo(); | 4953 | bindings.LLVMInitializeSystemZTargetInfo(); |
| 12817 | llvm.LLVMInitializeSystemZTargetMC(); | 4954 | bindings.LLVMInitializeSystemZTargetMC(); |
| 12818 | llvm.LLVMInitializeSystemZAsmPrinter(); | 4955 | bindings.LLVMInitializeSystemZAsmPrinter(); |
| 12819 | llvm.LLVMInitializeSystemZAsmParser(); | 4956 | bindings.LLVMInitializeSystemZAsmParser(); |
| 12820 | }, | 4957 | }, |
| 12821 | .wasm32, .wasm64 => { | 4958 | .wasm32, .wasm64 => { |
| 12822 | llvm.LLVMInitializeWebAssemblyTarget(); | 4959 | bindings.LLVMInitializeWebAssemblyTarget(); |
| 12823 | llvm.LLVMInitializeWebAssemblyTargetInfo(); | 4960 | bindings.LLVMInitializeWebAssemblyTargetInfo(); |
| 12824 | llvm.LLVMInitializeWebAssemblyTargetMC(); | 4961 | bindings.LLVMInitializeWebAssemblyTargetMC(); |
| 12825 | llvm.LLVMInitializeWebAssemblyAsmPrinter(); | 4962 | bindings.LLVMInitializeWebAssemblyAsmPrinter(); |
| 12826 | llvm.LLVMInitializeWebAssemblyAsmParser(); | 4963 | bindings.LLVMInitializeWebAssemblyAsmParser(); |
| 12827 | }, | 4964 | }, |
| 12828 | .x86, .x86_64 => { | 4965 | .x86, .x86_64 => { |
| 12829 | llvm.LLVMInitializeX86Target(); | 4966 | bindings.LLVMInitializeX86Target(); |
| 12830 | llvm.LLVMInitializeX86TargetInfo(); | 4967 | bindings.LLVMInitializeX86TargetInfo(); |
| 12831 | llvm.LLVMInitializeX86TargetMC(); | 4968 | bindings.LLVMInitializeX86TargetMC(); |
| 12832 | llvm.LLVMInitializeX86AsmPrinter(); | 4969 | bindings.LLVMInitializeX86AsmPrinter(); |
| 12833 | llvm.LLVMInitializeX86AsmParser(); | 4970 | bindings.LLVMInitializeX86AsmParser(); |
| 12834 | }, | 4971 | }, |
| 12835 | .xtensa => { | 4972 | .xtensa => { |
| 12836 | if (build_options.llvm_has_xtensa) { | 4973 | if (build_options.llvm_has_xtensa) { |
| 12837 | llvm.LLVMInitializeXtensaTarget(); | 4974 | bindings.LLVMInitializeXtensaTarget(); |
| 12838 | llvm.LLVMInitializeXtensaTargetInfo(); | 4975 | bindings.LLVMInitializeXtensaTargetInfo(); |
| 12839 | llvm.LLVMInitializeXtensaTargetMC(); | 4976 | bindings.LLVMInitializeXtensaTargetMC(); |
| 12840 | // There is no LLVMInitializeXtensaAsmPrinter function. | 4977 | // There is no LLVMInitializeXtensaAsmPrinter function. |
| 12841 | llvm.LLVMInitializeXtensaAsmParser(); | 4978 | bindings.LLVMInitializeXtensaAsmParser(); |
| 12842 | } | 4979 | } |
| 12843 | }, | 4980 | }, |
| 12844 | .xcore => { | 4981 | .xcore => { |
| 12845 | llvm.LLVMInitializeXCoreTarget(); | 4982 | bindings.LLVMInitializeXCoreTarget(); |
| 12846 | llvm.LLVMInitializeXCoreTargetInfo(); | 4983 | bindings.LLVMInitializeXCoreTargetInfo(); |
| 12847 | llvm.LLVMInitializeXCoreTargetMC(); | 4984 | bindings.LLVMInitializeXCoreTargetMC(); |
| 12848 | llvm.LLVMInitializeXCoreAsmPrinter(); | 4985 | bindings.LLVMInitializeXCoreAsmPrinter(); |
| 12849 | // There is no LLVMInitializeXCoreAsmParser function. | 4986 | // There is no LLVMInitializeXCoreAsmParser function. |
| 12850 | }, | 4987 | }, |
| 12851 | .m68k => { | 4988 | .m68k => { |
| 12852 | if (build_options.llvm_has_m68k) { | 4989 | if (build_options.llvm_has_m68k) { |
| 12853 | llvm.LLVMInitializeM68kTarget(); | 4990 | bindings.LLVMInitializeM68kTarget(); |
| 12854 | llvm.LLVMInitializeM68kTargetInfo(); | 4991 | bindings.LLVMInitializeM68kTargetInfo(); |
| 12855 | llvm.LLVMInitializeM68kTargetMC(); | 4992 | bindings.LLVMInitializeM68kTargetMC(); |
| 12856 | llvm.LLVMInitializeM68kAsmPrinter(); | 4993 | bindings.LLVMInitializeM68kAsmPrinter(); |
| 12857 | llvm.LLVMInitializeM68kAsmParser(); | 4994 | bindings.LLVMInitializeM68kAsmParser(); |
| 12858 | } | 4995 | } |
| 12859 | }, | 4996 | }, |
| 12860 | .csky => { | 4997 | .csky => { |
| 12861 | if (build_options.llvm_has_csky) { | 4998 | if (build_options.llvm_has_csky) { |
| 12862 | llvm.LLVMInitializeCSKYTarget(); | 4999 | bindings.LLVMInitializeCSKYTarget(); |
| 12863 | llvm.LLVMInitializeCSKYTargetInfo(); | 5000 | bindings.LLVMInitializeCSKYTargetInfo(); |
| 12864 | llvm.LLVMInitializeCSKYTargetMC(); | 5001 | bindings.LLVMInitializeCSKYTargetMC(); |
| 12865 | // There is no LLVMInitializeCSKYAsmPrinter function. | 5002 | // There is no LLVMInitializeCSKYAsmPrinter function. |
| 12866 | llvm.LLVMInitializeCSKYAsmParser(); | 5003 | bindings.LLVMInitializeCSKYAsmParser(); |
| 12867 | } | 5004 | } |
| 12868 | }, | 5005 | }, |
| 12869 | .ve => { | 5006 | .ve => { |
| 12870 | llvm.LLVMInitializeVETarget(); | 5007 | bindings.LLVMInitializeVETarget(); |
| 12871 | llvm.LLVMInitializeVETargetInfo(); | 5008 | bindings.LLVMInitializeVETargetInfo(); |
| 12872 | llvm.LLVMInitializeVETargetMC(); | 5009 | bindings.LLVMInitializeVETargetMC(); |
| 12873 | llvm.LLVMInitializeVEAsmPrinter(); | 5010 | bindings.LLVMInitializeVEAsmPrinter(); |
| 12874 | llvm.LLVMInitializeVEAsmParser(); | 5011 | bindings.LLVMInitializeVEAsmParser(); |
| 12875 | }, | 5012 | }, |
| 12876 | .arc => { | 5013 | .arc => { |
| 12877 | if (build_options.llvm_has_arc) { | 5014 | if (build_options.llvm_has_arc) { |
| 12878 | llvm.LLVMInitializeARCTarget(); | 5015 | bindings.LLVMInitializeARCTarget(); |
| 12879 | llvm.LLVMInitializeARCTargetInfo(); | 5016 | bindings.LLVMInitializeARCTargetInfo(); |
| 12880 | llvm.LLVMInitializeARCTargetMC(); | 5017 | bindings.LLVMInitializeARCTargetMC(); |
| 12881 | llvm.LLVMInitializeARCAsmPrinter(); | 5018 | bindings.LLVMInitializeARCAsmPrinter(); |
| 12882 | // There is no LLVMInitializeARCAsmParser function. | 5019 | // There is no LLVMInitializeARCAsmParser function. |
| 12883 | } | 5020 | } |
| 12884 | }, | 5021 | }, |
| 12885 | .loongarch32, .loongarch64 => { | 5022 | .loongarch32, .loongarch64 => { |
| 12886 | llvm.LLVMInitializeLoongArchTarget(); | 5023 | bindings.LLVMInitializeLoongArchTarget(); |
| 12887 | llvm.LLVMInitializeLoongArchTargetInfo(); | 5024 | bindings.LLVMInitializeLoongArchTargetInfo(); |
| 12888 | llvm.LLVMInitializeLoongArchTargetMC(); | 5025 | bindings.LLVMInitializeLoongArchTargetMC(); |
| 12889 | llvm.LLVMInitializeLoongArchAsmPrinter(); | 5026 | bindings.LLVMInitializeLoongArchAsmPrinter(); |
| 12890 | llvm.LLVMInitializeLoongArchAsmParser(); | 5027 | bindings.LLVMInitializeLoongArchAsmParser(); |
| 12891 | }, | 5028 | }, |
| 12892 | .spirv32, | 5029 | .spirv32, |
| 12893 | .spirv64, | 5030 | .spirv64, |
| 12894 | => { | 5031 | => { |
| 12895 | llvm.LLVMInitializeSPIRVTarget(); | 5032 | bindings.LLVMInitializeSPIRVTarget(); |
| 12896 | llvm.LLVMInitializeSPIRVTargetInfo(); | 5033 | bindings.LLVMInitializeSPIRVTargetInfo(); |
| 12897 | llvm.LLVMInitializeSPIRVTargetMC(); | 5034 | bindings.LLVMInitializeSPIRVTargetMC(); |
| 12898 | llvm.LLVMInitializeSPIRVAsmPrinter(); | 5035 | bindings.LLVMInitializeSPIRVAsmPrinter(); |
| 12899 | }, | 5036 | }, |
| 12900 | | 5037 | |
| 12901 | // LLVM does does not have a backend for these. | 5038 | // LLVM does does not have a backend for these. |
| ... | @@ -12916,296 +5053,3 @@ pub fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void { | ... | @@ -12916,296 +5053,3 @@ pub fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void { |
| 12916 | => unreachable, | 5053 | => unreachable, |
| 12917 | } | 5054 | } |
| 12918 | } | 5055 | } |
| 12919 | | | |
| 12920 | fn minIntConst(b: *Builder, min_ty: Type, as_ty: Builder.Type, zcu: *const Zcu) Allocator.Error!Builder.Constant { | | |
| 12921 | const info = min_ty.intInfo(zcu); | | |
| 12922 | if (info.signedness == .unsigned or info.bits == 0) { | | |
| 12923 | return b.intConst(as_ty, 0); | | |
| 12924 | } | | |
| 12925 | if (std.math.cast(u6, info.bits - 1)) |shift| { | | |
| 12926 | const min_val: i64 = @as(i64, std.math.minInt(i64)) >> (63 - shift); | | |
| 12927 | return b.intConst(as_ty, min_val); | | |
| 12928 | } | | |
| 12929 | var res: std.math.big.int.Managed = try .init(zcu.gpa); | | |
| 12930 | defer res.deinit(); | | |
| 12931 | try res.setTwosCompIntLimit(.min, info.signedness, info.bits); | | |
| 12932 | return b.bigIntConst(as_ty, res.toConst()); | | |
| 12933 | } | | |
| 12934 | | | |
| 12935 | fn maxIntConst(b: *Builder, max_ty: Type, as_ty: Builder.Type, zcu: *const Zcu) Allocator.Error!Builder.Constant { | | |
| 12936 | const info = max_ty.intInfo(zcu); | | |
| 12937 | switch (info.bits) { | | |
| 12938 | 0 => return b.intConst(as_ty, 0), | | |
| 12939 | 1 => switch (info.signedness) { | | |
| 12940 | .signed => return b.intConst(as_ty, 0), | | |
| 12941 | .unsigned => return b.intConst(as_ty, 1), | | |
| 12942 | }, | | |
| 12943 | else => {}, | | |
| 12944 | } | | |
| 12945 | const unsigned_bits = switch (info.signedness) { | | |
| 12946 | .unsigned => info.bits, | | |
| 12947 | .signed => info.bits - 1, | | |
| 12948 | }; | | |
| 12949 | if (std.math.cast(u6, unsigned_bits)) |shift| { | | |
| 12950 | const max_val: u64 = (@as(u64, 1) << shift) - 1; | | |
| 12951 | return b.intConst(as_ty, max_val); | | |
| 12952 | } | | |
| 12953 | var res: std.math.big.int.Managed = try .init(zcu.gpa); | | |
| 12954 | defer res.deinit(); | | |
| 12955 | try res.setTwosCompIntLimit(.max, info.signedness, info.bits); | | |
| 12956 | return b.bigIntConst(as_ty, res.toConst()); | | |
| 12957 | } | | |
| 12958 | | | |
| 12959 | /// Appends zero or more LLVM constraints to `llvm_constraints`, returning how many were added. | | |
| 12960 | fn appendConstraints( | | |
| 12961 | gpa: Allocator, | | |
| 12962 | llvm_constraints: *std.ArrayList(u8), | | |
| 12963 | zig_name: []const u8, | | |
| 12964 | target: *const std.Target, | | |
| 12965 | ) error{OutOfMemory}!usize { | | |
| 12966 | switch (target.cpu.arch) { | | |
| 12967 | .mips, .mipsel, .mips64, .mips64el => if (mips_clobber_overrides.get(zig_name)) |llvm_tag| { | | |
| 12968 | const llvm_name = @tagName(llvm_tag); | | |
| 12969 | try llvm_constraints.ensureUnusedCapacity(gpa, llvm_name.len + 4); | | |
| 12970 | llvm_constraints.appendSliceAssumeCapacity("~{"); | | |
| 12971 | llvm_constraints.appendSliceAssumeCapacity(llvm_name); | | |
| 12972 | llvm_constraints.appendSliceAssumeCapacity("},"); | | |
| 12973 | return 1; | | |
| 12974 | }, | | |
| 12975 | else => {}, | | |
| 12976 | } | | |
| 12977 | | | |
| 12978 | try llvm_constraints.ensureUnusedCapacity(gpa, zig_name.len + 4); | | |
| 12979 | llvm_constraints.appendSliceAssumeCapacity("~{"); | | |
| 12980 | llvm_constraints.appendSliceAssumeCapacity(zig_name); | | |
| 12981 | llvm_constraints.appendSliceAssumeCapacity("},"); | | |
| 12982 | return 1; | | |
| 12983 | } | | |
| 12984 | | | |
| 12985 | const mips_clobber_overrides = std.StaticStringMap(enum { | | |
| 12986 | @"$msair", | | |
| 12987 | @"$msacsr", | | |
| 12988 | @"$msaaccess", | | |
| 12989 | @"$msasave", | | |
| 12990 | @"$msamodify", | | |
| 12991 | @"$msarequest", | | |
| 12992 | @"$msamap", | | |
| 12993 | @"$msaunmap", | | |
| 12994 | @"$f0", | | |
| 12995 | @"$f1", | | |
| 12996 | @"$f2", | | |
| 12997 | @"$f3", | | |
| 12998 | @"$f4", | | |
| 12999 | @"$f5", | | |
| 13000 | @"$f6", | | |
| 13001 | @"$f7", | | |
| 13002 | @"$f8", | | |
| 13003 | @"$f9", | | |
| 13004 | @"$f10", | | |
| 13005 | @"$f11", | | |
| 13006 | @"$f12", | | |
| 13007 | @"$f13", | | |
| 13008 | @"$f14", | | |
| 13009 | @"$f15", | | |
| 13010 | @"$f16", | | |
| 13011 | @"$f17", | | |
| 13012 | @"$f18", | | |
| 13013 | @"$f19", | | |
| 13014 | @"$f20", | | |
| 13015 | @"$f21", | | |
| 13016 | @"$f22", | | |
| 13017 | @"$f23", | | |
| 13018 | @"$f24", | | |
| 13019 | @"$f25", | | |
| 13020 | @"$f26", | | |
| 13021 | @"$f27", | | |
| 13022 | @"$f28", | | |
| 13023 | @"$f29", | | |
| 13024 | @"$f30", | | |
| 13025 | @"$f31", | | |
| 13026 | @"$fcc0", | | |
| 13027 | @"$fcc1", | | |
| 13028 | @"$fcc2", | | |
| 13029 | @"$fcc3", | | |
| 13030 | @"$fcc4", | | |
| 13031 | @"$fcc5", | | |
| 13032 | @"$fcc6", | | |
| 13033 | @"$fcc7", | | |
| 13034 | @"$w0", | | |
| 13035 | @"$w1", | | |
| 13036 | @"$w2", | | |
| 13037 | @"$w3", | | |
| 13038 | @"$w4", | | |
| 13039 | @"$w5", | | |
| 13040 | @"$w6", | | |
| 13041 | @"$w7", | | |
| 13042 | @"$w8", | | |
| 13043 | @"$w9", | | |
| 13044 | @"$w10", | | |
| 13045 | @"$w11", | | |
| 13046 | @"$w12", | | |
| 13047 | @"$w13", | | |
| 13048 | @"$w14", | | |
| 13049 | @"$w15", | | |
| 13050 | @"$w16", | | |
| 13051 | @"$w17", | | |
| 13052 | @"$w18", | | |
| 13053 | @"$w19", | | |
| 13054 | @"$w20", | | |
| 13055 | @"$w21", | | |
| 13056 | @"$w22", | | |
| 13057 | @"$w23", | | |
| 13058 | @"$w24", | | |
| 13059 | @"$w25", | | |
| 13060 | @"$w26", | | |
| 13061 | @"$w27", | | |
| 13062 | @"$w28", | | |
| 13063 | @"$w29", | | |
| 13064 | @"$w30", | | |
| 13065 | @"$w31", | | |
| 13066 | @"$0", | | |
| 13067 | @"$1", | | |
| 13068 | @"$2", | | |
| 13069 | @"$3", | | |
| 13070 | @"$4", | | |
| 13071 | @"$5", | | |
| 13072 | @"$6", | | |
| 13073 | @"$7", | | |
| 13074 | @"$8", | | |
| 13075 | @"$9", | | |
| 13076 | @"$10", | | |
| 13077 | @"$11", | | |
| 13078 | @"$12", | | |
| 13079 | @"$13", | | |
| 13080 | @"$14", | | |
| 13081 | @"$15", | | |
| 13082 | @"$16", | | |
| 13083 | @"$17", | | |
| 13084 | @"$18", | | |
| 13085 | @"$19", | | |
| 13086 | @"$20", | | |
| 13087 | @"$21", | | |
| 13088 | @"$22", | | |
| 13089 | @"$23", | | |
| 13090 | @"$24", | | |
| 13091 | @"$25", | | |
| 13092 | @"$26", | | |
| 13093 | @"$27", | | |
| 13094 | @"$28", | | |
| 13095 | @"$29", | | |
| 13096 | @"$30", | | |
| 13097 | @"$31", | | |
| 13098 | }).initComptime(.{ | | |
| 13099 | .{ "msa_ir", .@"$msair" }, | | |
| 13100 | .{ "msa_csr", .@"$msacsr" }, | | |
| 13101 | .{ "msa_access", .@"$msaaccess" }, | | |
| 13102 | .{ "msa_save", .@"$msasave" }, | | |
| 13103 | .{ "msa_modify", .@"$msamodify" }, | | |
| 13104 | .{ "msa_request", .@"$msarequest" }, | | |
| 13105 | .{ "msa_map", .@"$msamap" }, | | |
| 13106 | .{ "msa_unmap", .@"$msaunmap" }, | | |
| 13107 | .{ "f0", .@"$f0" }, | | |
| 13108 | .{ "f1", .@"$f1" }, | | |
| 13109 | .{ "f2", .@"$f2" }, | | |
| 13110 | .{ "f3", .@"$f3" }, | | |
| 13111 | .{ "f4", .@"$f4" }, | | |
| 13112 | .{ "f5", .@"$f5" }, | | |
| 13113 | .{ "f6", .@"$f6" }, | | |
| 13114 | .{ "f7", .@"$f7" }, | | |
| 13115 | .{ "f8", .@"$f8" }, | | |
| 13116 | .{ "f9", .@"$f9" }, | | |
| 13117 | .{ "f10", .@"$f10" }, | | |
| 13118 | .{ "f11", .@"$f11" }, | | |
| 13119 | .{ "f12", .@"$f12" }, | | |
| 13120 | .{ "f13", .@"$f13" }, | | |
| 13121 | .{ "f14", .@"$f14" }, | | |
| 13122 | .{ "f15", .@"$f15" }, | | |
| 13123 | .{ "f16", .@"$f16" }, | | |
| 13124 | .{ "f17", .@"$f17" }, | | |
| 13125 | .{ "f18", .@"$f18" }, | | |
| 13126 | .{ "f19", .@"$f19" }, | | |
| 13127 | .{ "f20", .@"$f20" }, | | |
| 13128 | .{ "f21", .@"$f21" }, | | |
| 13129 | .{ "f22", .@"$f22" }, | | |
| 13130 | .{ "f23", .@"$f23" }, | | |
| 13131 | .{ "f24", .@"$f24" }, | | |
| 13132 | .{ "f25", .@"$f25" }, | | |
| 13133 | .{ "f26", .@"$f26" }, | | |
| 13134 | .{ "f27", .@"$f27" }, | | |
| 13135 | .{ "f28", .@"$f28" }, | | |
| 13136 | .{ "f29", .@"$f29" }, | | |
| 13137 | .{ "f30", .@"$f30" }, | | |
| 13138 | .{ "f31", .@"$f31" }, | | |
| 13139 | .{ "fcc0", .@"$fcc0" }, | | |
| 13140 | .{ "fcc1", .@"$fcc1" }, | | |
| 13141 | .{ "fcc2", .@"$fcc2" }, | | |
| 13142 | .{ "fcc3", .@"$fcc3" }, | | |
| 13143 | .{ "fcc4", .@"$fcc4" }, | | |
| 13144 | .{ "fcc5", .@"$fcc5" }, | | |
| 13145 | .{ "fcc6", .@"$fcc6" }, | | |
| 13146 | .{ "fcc7", .@"$fcc7" }, | | |
| 13147 | .{ "w0", .@"$w0" }, | | |
| 13148 | .{ "w1", .@"$w1" }, | | |
| 13149 | .{ "w2", .@"$w2" }, | | |
| 13150 | .{ "w3", .@"$w3" }, | | |
| 13151 | .{ "w4", .@"$w4" }, | | |
| 13152 | .{ "w5", .@"$w5" }, | | |
| 13153 | .{ "w6", .@"$w6" }, | | |
| 13154 | .{ "w7", .@"$w7" }, | | |
| 13155 | .{ "w8", .@"$w8" }, | | |
| 13156 | .{ "w9", .@"$w9" }, | | |
| 13157 | .{ "w10", .@"$w10" }, | | |
| 13158 | .{ "w11", .@"$w11" }, | | |
| 13159 | .{ "w12", .@"$w12" }, | | |
| 13160 | .{ "w13", .@"$w13" }, | | |
| 13161 | .{ "w14", .@"$w14" }, | | |
| 13162 | .{ "w15", .@"$w15" }, | | |
| 13163 | .{ "w16", .@"$w16" }, | | |
| 13164 | .{ "w17", .@"$w17" }, | | |
| 13165 | .{ "w18", .@"$w18" }, | | |
| 13166 | .{ "w19", .@"$w19" }, | | |
| 13167 | .{ "w20", .@"$w20" }, | | |
| 13168 | .{ "w21", .@"$w21" }, | | |
| 13169 | .{ "w22", .@"$w22" }, | | |
| 13170 | .{ "w23", .@"$w23" }, | | |
| 13171 | .{ "w24", .@"$w24" }, | | |
| 13172 | .{ "w25", .@"$w25" }, | | |
| 13173 | .{ "w26", .@"$w26" }, | | |
| 13174 | .{ "w27", .@"$w27" }, | | |
| 13175 | .{ "w28", .@"$w28" }, | | |
| 13176 | .{ "w29", .@"$w29" }, | | |
| 13177 | .{ "w30", .@"$w30" }, | | |
| 13178 | .{ "w31", .@"$w31" }, | | |
| 13179 | .{ "r0", .@"$0" }, | | |
| 13180 | .{ "r1", .@"$1" }, | | |
| 13181 | .{ "r2", .@"$2" }, | | |
| 13182 | .{ "r3", .@"$3" }, | | |
| 13183 | .{ "r4", .@"$4" }, | | |
| 13184 | .{ "r5", .@"$5" }, | | |
| 13185 | .{ "r6", .@"$6" }, | | |
| 13186 | .{ "r7", .@"$7" }, | | |
| 13187 | .{ "r8", .@"$8" }, | | |
| 13188 | .{ "r9", .@"$9" }, | | |
| 13189 | .{ "r10", .@"$10" }, | | |
| 13190 | .{ "r11", .@"$11" }, | | |
| 13191 | .{ "r12", .@"$12" }, | | |
| 13192 | .{ "r13", .@"$13" }, | | |
| 13193 | .{ "r14", .@"$14" }, | | |
| 13194 | .{ "r15", .@"$15" }, | | |
| 13195 | .{ "r16", .@"$16" }, | | |
| 13196 | .{ "r17", .@"$17" }, | | |
| 13197 | .{ "r18", .@"$18" }, | | |
| 13198 | .{ "r19", .@"$19" }, | | |
| 13199 | .{ "r20", .@"$20" }, | | |
| 13200 | .{ "r21", .@"$21" }, | | |
| 13201 | .{ "r22", .@"$22" }, | | |
| 13202 | .{ "r23", .@"$23" }, | | |
| 13203 | .{ "r24", .@"$24" }, | | |
| 13204 | .{ "r25", .@"$25" }, | | |
| 13205 | .{ "r26", .@"$26" }, | | |
| 13206 | .{ "r27", .@"$27" }, | | |
| 13207 | .{ "r28", .@"$28" }, | | |
| 13208 | .{ "r29", .@"$29" }, | | |
| 13209 | .{ "r30", .@"$30" }, | | |
| 13210 | .{ "r31", .@"$31" }, | | |
| 13211 | }); | | |