authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-04-21 07:16:31-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-04-24 11:08:01-04:00
logda85c089b8911189a9b42505cc44821bc87b5b23
treea7e4f2eee70220f1067440115aab6c4f9949f660
parent14ae0638ff9721a64736bd022d133a09163fdd64

implement llvm backend support for restricted type safety check


4 files changed, 192 insertions(+), 48 deletions(-)

lib/std/zig/llvm/Builder.zig+16
......@@ -2415,6 +2415,10 @@ pub const Global = struct {
24152415 self.ptr(builder).unnamed_addr = unnamed_addr;
24162416 }
24172417
2418 pub fn setType(self: Index, ty: Type, builder: *Builder) void {
2419 self.ptr(builder).type = ty;
2420 }
2421
24182422 pub fn setDebugMetadata(self: Index, dbg: Metadata, builder: *Builder) void {
24192423 self.ptr(builder).dbg = dbg.toOptional();
24202424 }
......@@ -2661,6 +2665,18 @@ pub const Variable = struct {
26612665 return self.ptr(builder).alignment;
26622666 }
26632667
2668 pub fn setLinkage(self: Index, linkage: Linkage, builder: *Builder) void {
2669 self.ptrConst(builder).global.setLinkage(linkage, builder);
2670 }
2671
2672 pub fn setType(self: Index, ty: Type, builder: *Builder) void {
2673 self.ptrConst(builder).global.setType(ty, builder);
2674 }
2675
2676 pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void {
2677 return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder);
2678 }
2679
26642680 pub fn setGlobalVariableExpression(self: Index, expression: Metadata, builder: *Builder) void {
26652681 self.ptrConst(builder).global.setDebugMetadata(expression, builder);
26662682 }
src/codegen/llvm.zig+136-39
......@@ -558,6 +558,8 @@ pub const Object = struct {
558558 val: InternPool.Index,
559559 @"addrspace": std.builtin.AddressSpace,
560560 }, Builder.Variable.Index),
561 /// Maps restricted types to supporting decls.
562 restricted_map: std.array_hash_map.Auto(InternPool.Index, RestrictedDecls),
561563 /// Maps enum types to their corresponding LLVM functions for implementing the `tag_name` instruction.
562564 enum_tag_name_map: std.AutoHashMapUnmanaged(InternPool.Index, Builder.Function.Index),
563565 /// Serves the same purpose as `enum_tag_name_map` but for the `is_named_enum_value` instruction.
......@@ -666,6 +668,7 @@ pub const Object = struct {
666668 .zcu = zcu,
667669 .nav_map = .empty,
668670 .uav_map = .empty,
671 .restricted_map = .empty,
669672 .enum_tag_name_map = .empty,
670673 .named_enum_map = .empty,
671674 .type_map = .empty,
......@@ -686,6 +689,8 @@ pub const Object = struct {
686689 self.debug_types.deinit(gpa);
687690 self.nav_map.deinit(gpa);
688691 self.uav_map.deinit(gpa);
692 for (self.restricted_map.values()) |*value| value.deinit(gpa);
693 self.restricted_map.deinit(gpa);
689694 self.enum_tag_name_map.deinit(gpa);
690695 self.named_enum_map.deinit(gpa);
691696 self.type_map.deinit(gpa);
......@@ -693,6 +698,65 @@ pub const Object = struct {
693698 self.* = undefined;
694699 }
695700
701 const RestrictedDecls = struct {
702 len: Builder.Variable.Index,
703 array: Builder.Variable.Index,
704 values: std.array_hash_map.Auto(InternPool.Index, Builder.Constant),
705
706 fn deinit(rd: *RestrictedDecls, gpa: Allocator) void {
707 rd.values.deinit(gpa);
708 rd.* = undefined;
709 }
710 };
711 pub fn getRestrictedDecls(o: *Object, ty: Type) Allocator.Error!*RestrictedDecls {
712 const gop = try o.restricted_map.getOrPut(o.gpa, ty.toIntern());
713 if (gop.found_existing) return gop.value_ptr;
714 errdefer _ = o.restricted_map.pop().?;
715
716 const target = o.zcu.getTarget();
717 const ip = &o.zcu.intern_pool;
718 const ptr_align = Type.ptrAbiAlignment(target).toLlvm();
719
720 const ty_name = ty.containerTypeName(ip).toSlice(ip);
721 gop.value_ptr.* = .{
722 .len = try o.builder.addVariable(
723 try o.builder.strtabStringFmt("{s}.len", .{ty_name}),
724 try o.lowerType(.usize),
725 .default,
726 ),
727 .array = try o.builder.addVariable(
728 try o.builder.strtabString(ty_name),
729 .void,
730 .default,
731 ),
732 .values = .empty,
733 };
734 gop.value_ptr.len.setLinkage(.private, &o.builder);
735 gop.value_ptr.len.setMutability(.constant, &o.builder);
736 gop.value_ptr.len.setAlignment(ptr_align, &o.builder);
737 gop.value_ptr.len.setUnnamedAddr(.unnamed_addr, &o.builder);
738 gop.value_ptr.array.setLinkage(.private, &o.builder);
739 gop.value_ptr.array.setMutability(.constant, &o.builder);
740 gop.value_ptr.array.setAlignment(ptr_align, &o.builder);
741 // Setting unnamed_addr here would reduce safety, and the module emitting the safety checks may not be the same module
742 // that defined the restricted type. In any case, llvm will add unnamed_addr itself if no safety checks end up being emitted.
743 gop.value_ptr.array.setUnnamedAddr(.default, &o.builder);
744 return gop.value_ptr;
745 }
746 fn genRestrictedDecls(o: *Object) Allocator.Error!void {
747 for (o.restricted_map.values()) |restricted_decls| {
748 const len = restricted_decls.values.count();
749 try restricted_decls.len.setInitializer(
750 try o.builder.intConst(restricted_decls.len.typeOf(&o.builder), len),
751 &o.builder,
752 );
753 try restricted_decls.array.setInitializer(try o.builder.arrayConst(
754 try o.builder.arrayType(len, .ptr),
755 restricted_decls.values.values(),
756 ), &o.builder);
757 }
758 }
759
696760 fn genErrorNameTable(o: *Object) Allocator.Error!void {
697761 // If o.error_name_table is null, then it was not referenced by any instructions.
698762 if (o.error_name_table == .none) return;
......@@ -771,6 +835,7 @@ pub const Object = struct {
771835 const diags = &comp.link_diags;
772836
773837 {
838 try o.genRestrictedDecls();
774839 if (o.errors_len_variable != .none) {
775840 const errors_len = zcu.intern_pool.global_error_set.getNamesFromMainThread().len;
776841 const init_val = try o.builder.intConst(try o.errorIntType(), errors_len);
......@@ -2006,55 +2071,64 @@ pub const Object = struct {
20062071 .pointer => {
20072072 const ptr_size = Type.ptrAbiSize(zcu.getTarget());
20082073 const ptr_align = Type.ptrAbiAlignment(zcu.getTarget());
2009
2010 if (ty.isSlice(zcu)) {
2011 const debug_ptr_type = try o.builder.debugMemberType(
2012 try o.builder.metadataString("ptr"),
2074 switch (ty.restrictedRepr(zcu)) {
2075 .indirect => return o.builder.debugPointerType(
2076 name,
20132077 null, // file
2014 ty_fwd_ref,
2078 o.debug_compile_unit.unwrap().?, // scope
20152079 0, // line
2016 try o.getDebugType(pt, ty.slicePtrFieldType(zcu)),
2080 try o.getDebugType(pt, ty.unrestrictedType(zcu).?),
20172081 ptr_size * 8,
20182082 ptr_align.toByteUnits().? * 8,
20192083 0, // offset
2020 );
2084 ),
2085 .direct => if (ty.isSlice(zcu)) {
2086 const debug_ptr_type = try o.builder.debugMemberType(
2087 try o.builder.metadataString("ptr"),
2088 null, // file
2089 ty_fwd_ref,
2090 0, // line
2091 try o.getDebugType(pt, ty.slicePtrFieldType(zcu)),
2092 ptr_size * 8,
2093 ptr_align.toByteUnits().? * 8,
2094 0, // offset
2095 );
20212096
2022 const debug_len_type = try o.builder.debugMemberType(
2023 try o.builder.metadataString("len"),
2024 null, // file
2025 ty_fwd_ref,
2026 0, // line
2027 try o.getDebugType(pt, .usize),
2028 ptr_size * 8,
2029 ptr_align.toByteUnits().? * 8,
2030 ptr_size * 8,
2031 );
2097 const debug_len_type = try o.builder.debugMemberType(
2098 try o.builder.metadataString("len"),
2099 null, // file
2100 ty_fwd_ref,
2101 0, // line
2102 try o.getDebugType(pt, .usize),
2103 ptr_size * 8,
2104 ptr_align.toByteUnits().? * 8,
2105 ptr_size * 8,
2106 );
20322107
2033 return o.builder.debugStructType(
2108 return o.builder.debugStructType(
2109 name,
2110 null, // file
2111 o.debug_compile_unit.unwrap().?, // scope
2112 0, // line
2113 null, // underlying type
2114 ptr_size * 2 * 8,
2115 ptr_align.toByteUnits().? * 8,
2116 try o.builder.metadataTuple(&.{
2117 debug_ptr_type,
2118 debug_len_type,
2119 }),
2120 );
2121 } else return o.builder.debugPointerType(
20342122 name,
20352123 null, // file
20362124 o.debug_compile_unit.unwrap().?, // scope
20372125 0, // line
2038 null, // underlying type
2039 ptr_size * 2 * 8,
2126 try o.getDebugType(pt, ty.childType(zcu)),
2127 ptr_size * 8,
20402128 ptr_align.toByteUnits().? * 8,
2041 try o.builder.metadataTuple(&.{
2042 debug_ptr_type,
2043 debug_len_type,
2044 }),
2045 );
2129 0, // offset
2130 ),
20462131 }
2047
2048 return o.builder.debugPointerType(
2049 name,
2050 null, // file
2051 o.debug_compile_unit.unwrap().?, // scope
2052 0, // line
2053 try o.getDebugType(pt, ty.childType(zcu)),
2054 ptr_size * 8,
2055 ptr_align.toByteUnits().? * 8,
2056 0, // offset
2057 );
20582132 },
20592133 .array => return o.builder.debugArrayType(
20602134 name,
......@@ -3047,7 +3121,7 @@ pub const Object = struct {
30473121 .empty_tuple,
30483122 .none,
30493123 => unreachable,
3050 else => switch (ip.indexToKey(t.toIntern())) {
3124 else => t: switch (ip.indexToKey(t.toIntern())) {
30513125 .int_type => |int_type| try o.builder.intType(int_type.bits),
30523126 .ptr_type => |ptr_type| type: {
30533127 const ptr_ty = try o.builder.ptrType(
......@@ -3061,7 +3135,10 @@ pub const Object = struct {
30613135 }),
30623136 };
30633137 },
3064 .restricted_ptr_type => @panic("TODO implement restricted pointers"),
3138 .restricted_ptr_type => |restricted_ptr_type| switch (t.restrictedRepr(zcu)) {
3139 .indirect => .ptr,
3140 .direct => continue :t .{ .ptr_type = ip.indexToKey(restricted_ptr_type.unrestricted_ptr_type).ptr_type },
3141 },
30653142 .array_type => |array_type| o.builder.arrayType(
30663143 array_type.lenIncludingSentinel(),
30673144 try o.lowerType(.fromInterned(array_type.child)),
......@@ -3545,7 +3622,27 @@ pub const Object = struct {
35453622 128 => try o.builder.fp128Const(val.toFloat(f128, zcu)),
35463623 else => unreachable,
35473624 },
3548 .ptr => try o.lowerPtr(arg_val, 0),
3625 .ptr => switch (ty.restrictedRepr(zcu)) {
3626 .indirect => {
3627 const restricted_decls = try o.getRestrictedDecls(ty);
3628 const gop = try restricted_decls.values.getOrPut(o.gpa, arg_val);
3629 if (!gop.found_existing) gop.value_ptr.* = try o.lowerValue(try ip.getCoerced(
3630 zcu.gpa,
3631 zcu.comp.io,
3632 .main, // FIXME
3633 arg_val,
3634 ty.unrestrictedType(zcu).?.toIntern(),
3635 ));
3636 return o.builder.gepConst(
3637 .inbounds,
3638 .ptr,
3639 restricted_decls.array.toConst(&o.builder),
3640 null,
3641 &.{try o.builder.intConst(.i64, gop.index)},
3642 );
3643 },
3644 .direct => try o.lowerPtr(arg_val, 0),
3645 },
35493646 .slice => |slice| return o.builder.structConst(try o.lowerType(ty), &.{
35503647 try o.lowerValue(slice.ptr),
35513648 try o.lowerValue(slice.len),
src/codegen/llvm/FuncGen.zig+39-8
......@@ -3253,19 +3253,50 @@ fn airWrapErrUnionErr(self: *FuncGen, body_tail: []const Air.Inst.Index) Allocat
32533253 return result_ptr;
32543254}
32553255
3256fn airUnwrapRestricted(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Builder.Value {
3257 const o = self.object;
3256fn airUnwrapRestricted(fg: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Builder.Value {
3257 const o = fg.object;
32583258 const zcu = o.zcu;
3259 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3260 const unrestricted_ty = ty_op.ty.toType();
3261 const restricted_ty = self.typeOf(ty_op.operand);
3262 const operand = try self.resolveInst(ty_op.operand);
3259 const target = zcu.getTarget();
3260 const ty_op = fg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3261 const restricted_ty = fg.typeOf(ty_op.operand);
3262 const operand = try fg.resolveInst(ty_op.operand);
32633263 switch (restricted_ty.restrictedRepr(zcu)) {
32643264 .indirect => {
3265 const unrestricted_ty = ty_op.ty.toType();
32653266 if (safety) {
3266 // TODO
3267 const restricted_decls = try o.getRestrictedDecls(restricted_ty);
3268 const llvm_usize_ty = restricted_decls.len.typeOf(&o.builder);
3269 const array = try o.builder.castConst(.ptrtoint, restricted_decls.array.toConst(&o.builder), llvm_usize_ty);
3270 const ptr_diff = try fg.wip.bin(
3271 .sub,
3272 try fg.wip.cast(.ptrtoint, operand, llvm_usize_ty, "unwrap_restricted.operand_int"),
3273 array.toValue(),
3274 "unwrap_restricted.ptr_diff",
3275 );
3276 const index = try fg.wip.callIntrinsic(.normal, .none, .fshr, &.{llvm_usize_ty}, &.{
3277 ptr_diff,
3278 ptr_diff,
3279 try o.builder.intValue(llvm_usize_ty, std.math.log2_int(u64, Type.ptrAbiSize(target))),
3280 }, "unwrap_restricted.index");
3281 const len = try fg.wip.load(
3282 .normal,
3283 llvm_usize_ty,
3284 restricted_decls.len.toValue(&o.builder),
3285 Type.ptrAbiAlignment(target).toLlvm(),
3286 "unwrap_restricted.len",
3287 );
3288 const ok = try fg.wip.icmp(.ult, index, len, "unwrap_restricted.ok");
3289
3290 const invalid_block = try fg.wip.block(1, "unwrap_restricted.invalid");
3291 const valid_block = try fg.wip.block(1, "unwrap_restricted.valid");
3292 _ = try fg.wip.brCond(ok, valid_block, invalid_block, .none);
3293
3294 fg.wip.cursor = .{ .block = invalid_block };
3295 try fg.buildSimplePanic(.corrupt_restricted_pointer);
3296
3297 fg.wip.cursor = .{ .block = valid_block };
32673298 }
3268 return self.wip.load(.normal, .ptr, operand, unrestricted_ty.abiAlignment(zcu).toLlvm(), "restricted.unwrap");
3299 return fg.wip.load(.normal, .ptr, operand, unrestricted_ty.abiAlignment(zcu).toLlvm(), "unwrap_restricted");
32693300 },
32703301 .direct => return operand,
32713302 }
src/target.zig+1-1
......@@ -949,7 +949,7 @@ pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, compt
949949 else => true,
950950 },
951951 .restricted_types => switch (backend) {
952 .stage2_c, .stage2_x86_64 => true,
952 .stage2_c, .stage2_llvm, .stage2_x86_64 => true,
953953 else => false,
954954 },
955955 };