authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-24 10:28:04+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-28 16:47:57+00:00
log500e6c7cfe7fe57db51b99a8e76e67a3441de7ac
tree9463c73d0aac628c8b8bf21cbcc8cc869d83fa9e
parent9c0c65c313776fdc394d8e481ee4eb548a5333ed
signaturelock-open Commit is signed but in an unrecognized format.

llvm: some more random enhancements

Avoid directly querying `Builder.Type`s in favour of `lowerType` calls in a couple of places. The idea here is to avoid querying state stored in the `Builder` to try and move towards a world where codegen (essentially the logic in `codegen.llvm.FuncGen`) can happen on a separate thread to "linking" (which actually interacts with shared state on `codegen.llvm.Object` and `std.zig.llvm.Builder`). Don't clear the `Builder` state during `emit`; this is clearly incompatible with incremental compilation. With that line of code removed, incremental compilation is actually already somewhat functional with the LLVM backend. Also, don't use `c_uint` for source location state---I have no idea where this came from but it definitely isn't correct.

2 files changed, 49 insertions(+), 56 deletions(-)

src/codegen/llvm.zig-1
......@@ -944,7 +944,6 @@ pub const Object = struct {
944944 .version = build_options.semver,
945945 });
946946 defer o.gpa.free(bitcode);
947 o.builder.clearAndFree();
948947
949948 if (options.pre_bc_path) |path| {
950949 var file = Io.Dir.cwd().createFile(io, path, .{}) catch |err|
src/codegen/llvm/FuncGen.zig+49-55
......@@ -16,8 +16,8 @@ scope: Builder.Metadata,
1616inlined_at: Builder.Metadata.Optional,
1717
1818base_line: u32,
19prev_dbg_line: c_uint,
20prev_dbg_column: c_uint,
19prev_dbg_line: u32,
20prev_dbg_column: u32,
2121
2222/// This stores the LLVM values used in a function, such that they can be referred to
2323/// in other instructions. This table is cleared before every function is generated.
......@@ -815,7 +815,7 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
815815 .always_tail => .musttail,
816816 .no_suspend, .always_inline, .compile_time => unreachable,
817817 },
818 toLlvmCallConvTag(fn_info.cc, target).?,
818 llvm.toLlvmCallConvTag(fn_info.cc, target).?,
819819 try attributes.finish(&o.builder),
820820 try o.lowerType(zig_fn_ty),
821821 llvm_fn,
......@@ -882,7 +882,7 @@ fn buildSimplePanic(fg: *FuncGen, panic_id: Zcu.SimplePanicId) Allocator.Error!v
882882 _ = try fg.wip.callIntrinsicAssumeCold();
883883 _ = try fg.wip.call(
884884 .normal,
885 toLlvmCallConvTag(fn_info.cc, target).?,
885 llvm.toLlvmCallConvTag(fn_info.cc, target).?,
886886 .none,
887887 panic_global.typeOf(&o.builder),
888888 panic_global.toValue(&o.builder),
......@@ -1394,7 +1394,7 @@ fn lowerSwitchDispatch(
13941394 // be handled by conditional branches in the `else` prong.
13951395
13961396 const llvm_usize = try o.lowerType(.usize);
1397 const cond_int = if (cond.typeOfWip(&self.wip).isPointer(&o.builder))
1397 const cond_int = if (cond_ty.zigTypeTag(zcu) == .pointer)
13981398 try self.wip.cast(.ptrtoint, cond, llvm_usize, "")
13991399 else
14001400 cond;
......@@ -1433,7 +1433,7 @@ fn lowerSwitchDispatch(
14331433
14341434 for (case.items) |item| {
14351435 const llvm_item = (try self.resolveInst(item)).toConst().?;
1436 const llvm_int_item = if (llvm_item.typeOf(&o.builder).isPointer(&o.builder))
1436 const llvm_int_item = if (cond_ty.zigTypeTag(zcu) == .pointer)
14371437 try o.builder.castConst(.ptrtoint, llvm_item, llvm_usize)
14381438 else
14391439 llvm_item;
......@@ -2840,7 +2840,7 @@ fn airIsNonNull(
28402840 operand;
28412841 if (payload_ty.isSlice(zcu)) {
28422842 const slice_ptr = try self.wip.extractValue(loaded, &.{0}, "");
2843 const ptr_ty = try o.builder.ptrType(toLlvmAddressSpace(
2843 const ptr_ty = try o.builder.ptrType(llvm.toLlvmAddressSpace(
28442844 payload_ty.ptrAddressSpace(zcu),
28452845 zcu.getTarget(),
28462846 ));
......@@ -3342,17 +3342,17 @@ fn airSafeArithmetic(
33423342
33433343 const overflow_bits = try fg.wip.extractValue(results, &.{1}, "");
33443344 const overflow_bits_ty = overflow_bits.typeOfWip(&fg.wip);
3345 const overflow_bit = if (overflow_bits_ty.isVector(&o.builder))
3346 try fg.wip.callIntrinsic(
3345 const overflow_bit = switch (inst_ty.zigTypeTag(zcu)) {
3346 .vector => try fg.wip.callIntrinsic(
33473347 .normal,
33483348 .none,
33493349 .@"vector.reduce.or",
33503350 &.{overflow_bits_ty},
33513351 &.{overflow_bits},
33523352 "",
3353 )
3354 else
3355 overflow_bits;
3353 ),
3354 else => overflow_bits,
3355 };
33563356
33573357 const fail_block = try fg.wip.block(1, "OverflowFail");
33583358 const ok_block = try fg.wip.block(1, "OverflowOk");
......@@ -3508,6 +3508,7 @@ fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind)
35083508 return self.buildFloatOp(.floor, fast, inst_ty, 1, .{result});
35093509 }
35103510 if (scalar_ty.isSignedInt(zcu)) {
3511 const scalar_llvm_ty = try o.lowerType(scalar_ty);
35113512 const inst_llvm_ty = try o.lowerType(inst_ty);
35123513
35133514 const ExpectedContents = [std.math.big.int.calcTwosCompLimbCount(256)]std.math.big.Limb;
......@@ -3517,7 +3518,7 @@ fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind)
35173518 )) = std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa);
35183519 const allocator = stack.get();
35193520
3520 const scalar_bits = inst_llvm_ty.scalarBits(&o.builder);
3521 const scalar_bits = scalar_ty.intInfo(zcu).bits;
35213522 var smin_big_int: std.math.big.int.Mutable = .{
35223523 .limbs = try allocator.alloc(
35233524 std.math.big.Limb,
......@@ -3529,7 +3530,7 @@ fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind)
35293530 defer allocator.free(smin_big_int.limbs);
35303531 smin_big_int.setTwosCompIntLimit(.min, .signed, scalar_bits);
35313532 const smin = try o.builder.splatValue(inst_llvm_ty, try o.builder.bigIntConst(
3532 inst_llvm_ty.scalarType(&o.builder),
3533 scalar_llvm_ty,
35333534 smin_big_int.toConst(),
35343535 ));
35353536
......@@ -3603,7 +3604,7 @@ fn airMod(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allo
36033604 )) = std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa);
36043605 const allocator = stack.get();
36053606
3606 const scalar_bits = inst_llvm_ty.scalarBits(&o.builder);
3607 const scalar_bits = scalar_ty.intInfo(zcu).bits;
36073608 var smin_big_int: std.math.big.int.Mutable = .{
36083609 .limbs = try allocator.alloc(
36093610 std.math.big.Limb,
......@@ -3615,7 +3616,7 @@ fn airMod(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allo
36153616 defer allocator.free(smin_big_int.limbs);
36163617 smin_big_int.setTwosCompIntLimit(.min, .signed, scalar_bits);
36173618 const smin = try o.builder.splatValue(inst_llvm_ty, try o.builder.bigIntConst(
3618 inst_llvm_ty.scalarType(&o.builder),
3619 try o.lowerType(scalar_ty),
36193620 smin_big_int.toConst(),
36203621 ));
36213622
......@@ -3929,7 +3930,10 @@ fn buildFloatOp(
39293930 // In this case we can generate a softfloat negation by XORing the
39303931 // bits with a constant.
39313932 const int_ty = try o.builder.intType(@intCast(float_bits));
3932 const cast_ty = try llvm_ty.changeScalar(int_ty, &o.builder);
3933 const cast_ty = switch (ty.zigTypeTag(zcu)) {
3934 .vector => try o.builder.vectorType(.normal, ty.vectorLen(zcu), int_ty),
3935 else => int_ty,
3936 };
39333937 const sign_mask = try o.builder.splatValue(
39343938 cast_ty,
39353939 try o.builder.intConst(int_ty, @as(u128, 1) << @intCast(float_bits - 1)),
......@@ -3964,7 +3968,7 @@ fn buildFloatOp(
39643968 }),
39653969 };
39663970
3967 const scalar_llvm_ty = llvm_ty.scalarType(&o.builder);
3971 const scalar_llvm_ty = try o.lowerType(scalar_ty);
39683972 const libc_fn = try o.getLibcFunction(
39693973 fn_name,
39703974 ([1]Builder.Type{scalar_llvm_ty} ** 3)[0..params.len],
......@@ -4121,7 +4125,7 @@ fn airShlSat(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value
41214125 const lhs_ty = self.typeOf(bin_op.lhs);
41224126 const lhs_info = lhs_ty.intInfo(zcu);
41234127 const llvm_lhs_ty = try o.lowerType(lhs_ty);
4124 const llvm_lhs_scalar_ty = llvm_lhs_ty.scalarType(&o.builder);
4128 const llvm_lhs_scalar_ty = try o.lowerType(lhs_ty.scalarType(zcu));
41254129
41264130 const rhs_ty = self.typeOf(bin_op.rhs);
41274131 if (lhs_ty.isVector(zcu) and !rhs_ty.isVector(zcu)) {
......@@ -4132,7 +4136,7 @@ fn airShlSat(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value
41324136 const rhs_info = rhs_ty.intInfo(zcu);
41334137 assert(rhs_info.signedness == .unsigned);
41344138 const llvm_rhs_ty = try o.lowerType(rhs_ty);
4135 const llvm_rhs_scalar_ty = llvm_rhs_ty.scalarType(&o.builder);
4139 const llvm_rhs_scalar_ty = try o.lowerType(rhs_ty.scalarType(zcu));
41364140
41374141 const result = try self.wip.callIntrinsic(
41384142 .normal,
......@@ -4448,9 +4452,7 @@ fn bitCast(self: *FuncGen, operand: Builder.Value, operand_ty: Type, inst_ty: Ty
44484452 return operand;
44494453 }
44504454
4451 if (llvm_dest_ty.isInteger(&o.builder) and
4452 operand.typeOfWip(&self.wip).isInteger(&o.builder))
4453 {
4455 if (inst_ty.isAbiInt(zcu) and operand_ty.isAbiInt(zcu)) {
44544456 return self.wip.conv(.unsigned, operand, llvm_dest_ty, "");
44554457 }
44564458
......@@ -4524,7 +4526,7 @@ fn bitCast(self: *FuncGen, operand: Builder.Value, operand_ty: Type, inst_ty: Ty
45244526 return result_ptr;
45254527 }
45264528
4527 if (llvm_dest_ty.isStruct(&o.builder) or
4529 if (inst_ty.isSliceAtRuntime(zcu) or
45284530 ((operand_ty.zigTypeTag(zcu) == .vector or inst_ty.zigTypeTag(zcu) == .vector) and
45294531 operand_ty.bitSize(zcu) != inst_ty.bitSize(zcu)))
45304532 {
......@@ -4948,28 +4950,28 @@ fn airAtomicRmw(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Va
49484950 ), llvm_operand_ty, "");
49494951 }
49504952
4951 if (!llvm_operand_ty.isPointer(&o.builder)) return self.wip.atomicrmw(
4953 // If we are storing a pointer we need to convert to and from a plain old integer.
4954 const non_ptr_operand = switch (operand_ty.zigTypeTag(zcu)) {
4955 .pointer => try self.wip.cast(.ptrtoint, operand, try o.lowerType(.usize), ""),
4956 else => operand,
4957 };
4958
4959 const raw_result = try self.wip.atomicrmw(
49524960 access_kind,
49534961 op,
49544962 ptr,
4955 operand,
4963 non_ptr_operand,
49564964 self.sync_scope,
49574965 ordering,
49584966 ptr_alignment,
49594967 "",
49604968 );
49614969
4962 // It's a pointer but we need to treat it as an int.
4963 return self.wip.cast(.inttoptr, try self.wip.atomicrmw(
4964 access_kind,
4965 op,
4966 ptr,
4967 try self.wip.cast(.ptrtoint, operand, try o.lowerType(.usize), ""),
4968 self.sync_scope,
4969 ordering,
4970 ptr_alignment,
4971 "",
4972 ), llvm_operand_ty, "");
4970 // ...and then convert the result back.
4971 switch (operand_ty.zigTypeTag(zcu)) {
4972 .pointer => return self.wip.cast(.inttoptr, raw_result, llvm_operand_ty, ""),
4973 else => return raw_result,
4974 }
49734975}
49744976
49754977fn airAtomicLoad(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value {
......@@ -5274,19 +5276,16 @@ fn airGetUnionTag(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.
52745276 const un_ty = self.typeOf(ty_op.operand);
52755277 const layout = un_ty.unionGetLayout(zcu);
52765278 assert(layout.tag_size != 0);
5277 const union_ptr = try self.resolveInst(ty_op.operand);
5279 const operand = try self.resolveInst(ty_op.operand);
52785280 if (isByRef(un_ty, zcu)) {
5279 const llvm_un_ty = try o.lowerType(un_ty);
5280 if (layout.payload_size == 0)
5281 return self.wip.load(.normal, llvm_un_ty, union_ptr, .default, "");
5282 const tag_index = @intFromBool(layout.tag_align.compare(.lt, layout.payload_align));
5283 const tag_field_ptr = try self.ptraddConst(union_ptr, layout.tagOffset());
5284 const llvm_tag_ty = llvm_un_ty.structFields(&o.builder)[tag_index];
5281 const llvm_tag_ty = try o.lowerType(un_ty.unionTagTypeRuntime(zcu).?);
5282 const tag_field_ptr = try self.ptraddConst(operand, layout.tagOffset());
52855283 return self.wip.load(.normal, llvm_tag_ty, tag_field_ptr, .default, "");
52865284 } else {
5287 if (layout.payload_size == 0) return union_ptr;
5288 const tag_index = @intFromBool(layout.tag_align.compare(.lt, layout.payload_align));
5289 return self.wip.extractValue(union_ptr, &.{tag_index}, "");
5285 // This is only possible if all fields are zero-bit, in which case `operand` is already an
5286 // integer value (the union is lowered as its enum tag).
5287 assert(layout.payload_size == 0);
5288 return operand;
52905289 }
52915290}
52925291
......@@ -7356,9 +7355,9 @@ fn appendConstraints(
73567355/// may need to manually generate a compiler-rt call.
73577356fn intrinsicsAllowed(scalar_ty: Type, target: *const std.Target) bool {
73587357 return switch (scalar_ty.toIntern()) {
7359 .f16_type => backendSupportsF16(target),
7360 .f80_type => (target.cTypeBitSize(.longdouble) == 80) and backendSupportsF80(target),
7361 .f128_type => (target.cTypeBitSize(.longdouble) == 128) and backendSupportsF128(target),
7358 .f16_type => llvm.backendSupportsF16(target),
7359 .f80_type => (target.cTypeBitSize(.longdouble) == 80) and llvm.backendSupportsF80(target),
7360 .f128_type => (target.cTypeBitSize(.longdouble) == 128) and llvm.backendSupportsF128(target),
73627361 else => true,
73637362 };
73647363}
......@@ -7702,9 +7701,4 @@ const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev;
77027701
77037702const llvm = @import("../llvm.zig");
77047703const Object = llvm.Object;
7705const toLlvmCallConvTag = llvm.toLlvmCallConvTag;
7706const toLlvmAddressSpace = llvm.toLlvmAddressSpace;
77077704const optional_layout_version = llvm.optional_layout_version;
7708const backendSupportsF16 = llvm.backendSupportsF16;
7709const backendSupportsF80 = llvm.backendSupportsF80;
7710const backendSupportsF128 = llvm.backendSupportsF128;