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 {...@@ -2415,6 +2415,10 @@ pub const Global = struct {
2415 self.ptr(builder).unnamed_addr = unnamed_addr;2415 self.ptr(builder).unnamed_addr = unnamed_addr;
2416 }2416 }
24172417
2418 pub fn setType(self: Index, ty: Type, builder: *Builder) void {
2419 self.ptr(builder).type = ty;
2420 }
2421
2418 pub fn setDebugMetadata(self: Index, dbg: Metadata, builder: *Builder) void {2422 pub fn setDebugMetadata(self: Index, dbg: Metadata, builder: *Builder) void {
2419 self.ptr(builder).dbg = dbg.toOptional();2423 self.ptr(builder).dbg = dbg.toOptional();
2420 }2424 }
...@@ -2661,6 +2665,18 @@ pub const Variable = struct {...@@ -2661,6 +2665,18 @@ pub const Variable = struct {
2661 return self.ptr(builder).alignment;2665 return self.ptr(builder).alignment;
2662 }2666 }
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
2664 pub fn setGlobalVariableExpression(self: Index, expression: Metadata, builder: *Builder) void {2680 pub fn setGlobalVariableExpression(self: Index, expression: Metadata, builder: *Builder) void {
2665 self.ptrConst(builder).global.setDebugMetadata(expression, builder);2681 self.ptrConst(builder).global.setDebugMetadata(expression, builder);
2666 }2682 }
src/codegen/llvm.zig+136-39
...@@ -558,6 +558,8 @@ pub const Object = struct {...@@ -558,6 +558,8 @@ pub const Object = struct {
558 val: InternPool.Index,558 val: InternPool.Index,
559 @"addrspace": std.builtin.AddressSpace,559 @"addrspace": std.builtin.AddressSpace,
560 }, Builder.Variable.Index),560 }, Builder.Variable.Index),
561 /// Maps restricted types to supporting decls.
562 restricted_map: std.array_hash_map.Auto(InternPool.Index, RestrictedDecls),
561 /// Maps enum types to their corresponding LLVM functions for implementing the `tag_name` instruction.563 /// Maps enum types to their corresponding LLVM functions for implementing the `tag_name` instruction.
562 enum_tag_name_map: std.AutoHashMapUnmanaged(InternPool.Index, Builder.Function.Index),564 enum_tag_name_map: std.AutoHashMapUnmanaged(InternPool.Index, Builder.Function.Index),
563 /// Serves the same purpose as `enum_tag_name_map` but for the `is_named_enum_value` instruction.565 /// 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 {...@@ -666,6 +668,7 @@ pub const Object = struct {
666 .zcu = zcu,668 .zcu = zcu,
667 .nav_map = .empty,669 .nav_map = .empty,
668 .uav_map = .empty,670 .uav_map = .empty,
671 .restricted_map = .empty,
669 .enum_tag_name_map = .empty,672 .enum_tag_name_map = .empty,
670 .named_enum_map = .empty,673 .named_enum_map = .empty,
671 .type_map = .empty,674 .type_map = .empty,
...@@ -686,6 +689,8 @@ pub const Object = struct {...@@ -686,6 +689,8 @@ pub const Object = struct {
686 self.debug_types.deinit(gpa);689 self.debug_types.deinit(gpa);
687 self.nav_map.deinit(gpa);690 self.nav_map.deinit(gpa);
688 self.uav_map.deinit(gpa);691 self.uav_map.deinit(gpa);
692 for (self.restricted_map.values()) |*value| value.deinit(gpa);
693 self.restricted_map.deinit(gpa);
689 self.enum_tag_name_map.deinit(gpa);694 self.enum_tag_name_map.deinit(gpa);
690 self.named_enum_map.deinit(gpa);695 self.named_enum_map.deinit(gpa);
691 self.type_map.deinit(gpa);696 self.type_map.deinit(gpa);
...@@ -693,6 +698,65 @@ pub const Object = struct {...@@ -693,6 +698,65 @@ pub const Object = struct {
693 self.* = undefined;698 self.* = undefined;
694 }699 }
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
696 fn genErrorNameTable(o: *Object) Allocator.Error!void {760 fn genErrorNameTable(o: *Object) Allocator.Error!void {
697 // If o.error_name_table is null, then it was not referenced by any instructions.761 // If o.error_name_table is null, then it was not referenced by any instructions.
698 if (o.error_name_table == .none) return;762 if (o.error_name_table == .none) return;
...@@ -771,6 +835,7 @@ pub const Object = struct {...@@ -771,6 +835,7 @@ pub const Object = struct {
771 const diags = &comp.link_diags;835 const diags = &comp.link_diags;
772836
773 {837 {
838 try o.genRestrictedDecls();
774 if (o.errors_len_variable != .none) {839 if (o.errors_len_variable != .none) {
775 const errors_len = zcu.intern_pool.global_error_set.getNamesFromMainThread().len;840 const errors_len = zcu.intern_pool.global_error_set.getNamesFromMainThread().len;
776 const init_val = try o.builder.intConst(try o.errorIntType(), errors_len);841 const init_val = try o.builder.intConst(try o.errorIntType(), errors_len);
...@@ -2006,55 +2071,64 @@ pub const Object = struct {...@@ -2006,55 +2071,64 @@ pub const Object = struct {
2006 .pointer => {2071 .pointer => {
2007 const ptr_size = Type.ptrAbiSize(zcu.getTarget());2072 const ptr_size = Type.ptrAbiSize(zcu.getTarget());
2008 const ptr_align = Type.ptrAbiAlignment(zcu.getTarget());2073 const ptr_align = Type.ptrAbiAlignment(zcu.getTarget());
20092074 switch (ty.restrictedRepr(zcu)) {
2010 if (ty.isSlice(zcu)) {2075 .indirect => return o.builder.debugPointerType(
2011 const debug_ptr_type = try o.builder.debugMemberType(2076 name,
2012 try o.builder.metadataString("ptr"),
2013 null, // file2077 null, // file
2014 ty_fwd_ref,2078 o.debug_compile_unit.unwrap().?, // scope
2015 0, // line2079 0, // line
2016 try o.getDebugType(pt, ty.slicePtrFieldType(zcu)),2080 try o.getDebugType(pt, ty.unrestrictedType(zcu).?),
2017 ptr_size * 8,2081 ptr_size * 8,
2018 ptr_align.toByteUnits().? * 8,2082 ptr_align.toByteUnits().? * 8,
2019 0, // offset2083 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(2097 const debug_len_type = try o.builder.debugMemberType(
2023 try o.builder.metadataString("len"),2098 try o.builder.metadataString("len"),
2024 null, // file2099 null, // file
2025 ty_fwd_ref,2100 ty_fwd_ref,
2026 0, // line2101 0, // line
2027 try o.getDebugType(pt, .usize),2102 try o.getDebugType(pt, .usize),
2028 ptr_size * 8,2103 ptr_size * 8,
2029 ptr_align.toByteUnits().? * 8,2104 ptr_align.toByteUnits().? * 8,
2030 ptr_size * 8,2105 ptr_size * 8,
2031 );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(
2034 name,2122 name,
2035 null, // file2123 null, // file
2036 o.debug_compile_unit.unwrap().?, // scope2124 o.debug_compile_unit.unwrap().?, // scope
2037 0, // line2125 0, // line
2038 null, // underlying type2126 try o.getDebugType(pt, ty.childType(zcu)),
2039 ptr_size * 2 * 8,2127 ptr_size * 8,
2040 ptr_align.toByteUnits().? * 8,2128 ptr_align.toByteUnits().? * 8,
2041 try o.builder.metadataTuple(&.{2129 0, // offset
2042 debug_ptr_type,2130 ),
2043 debug_len_type,
2044 }),
2045 );
2046 }2131 }
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 );
2058 },2132 },
2059 .array => return o.builder.debugArrayType(2133 .array => return o.builder.debugArrayType(
2060 name,2134 name,
...@@ -3047,7 +3121,7 @@ pub const Object = struct {...@@ -3047,7 +3121,7 @@ pub const Object = struct {
3047 .empty_tuple,3121 .empty_tuple,
3048 .none,3122 .none,
3049 => unreachable,3123 => unreachable,
3050 else => switch (ip.indexToKey(t.toIntern())) {3124 else => t: switch (ip.indexToKey(t.toIntern())) {
3051 .int_type => |int_type| try o.builder.intType(int_type.bits),3125 .int_type => |int_type| try o.builder.intType(int_type.bits),
3052 .ptr_type => |ptr_type| type: {3126 .ptr_type => |ptr_type| type: {
3053 const ptr_ty = try o.builder.ptrType(3127 const ptr_ty = try o.builder.ptrType(
...@@ -3061,7 +3135,10 @@ pub const Object = struct {...@@ -3061,7 +3135,10 @@ pub const Object = struct {
3061 }),3135 }),
3062 };3136 };
3063 },3137 },
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 },
3065 .array_type => |array_type| o.builder.arrayType(3142 .array_type => |array_type| o.builder.arrayType(
3066 array_type.lenIncludingSentinel(),3143 array_type.lenIncludingSentinel(),
3067 try o.lowerType(.fromInterned(array_type.child)),3144 try o.lowerType(.fromInterned(array_type.child)),
...@@ -3545,7 +3622,27 @@ pub const Object = struct {...@@ -3545,7 +3622,27 @@ pub const Object = struct {
3545 128 => try o.builder.fp128Const(val.toFloat(f128, zcu)),3622 128 => try o.builder.fp128Const(val.toFloat(f128, zcu)),
3546 else => unreachable,3623 else => unreachable,
3547 },3624 },
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 },
3549 .slice => |slice| return o.builder.structConst(try o.lowerType(ty), &.{3646 .slice => |slice| return o.builder.structConst(try o.lowerType(ty), &.{
3550 try o.lowerValue(slice.ptr),3647 try o.lowerValue(slice.ptr),
3551 try o.lowerValue(slice.len),3648 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...@@ -3253,19 +3253,50 @@ fn airWrapErrUnionErr(self: *FuncGen, body_tail: []const Air.Inst.Index) Allocat
3253 return result_ptr;3253 return result_ptr;
3254}3254}
32553255
3256fn airUnwrapRestricted(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Builder.Value {3256fn airUnwrapRestricted(fg: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Builder.Value {
3257 const o = self.object;3257 const o = fg.object;
3258 const zcu = o.zcu;3258 const zcu = o.zcu;
3259 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3259 const target = zcu.getTarget();
3260 const unrestricted_ty = ty_op.ty.toType();3260 const ty_op = fg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3261 const restricted_ty = self.typeOf(ty_op.operand);3261 const restricted_ty = fg.typeOf(ty_op.operand);
3262 const operand = try self.resolveInst(ty_op.operand);3262 const operand = try fg.resolveInst(ty_op.operand);
3263 switch (restricted_ty.restrictedRepr(zcu)) {3263 switch (restricted_ty.restrictedRepr(zcu)) {
3264 .indirect => {3264 .indirect => {
3265 const unrestricted_ty = ty_op.ty.toType();
3265 if (safety) {3266 if (safety) {
3266 // TODO3267 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 };
3267 }3298 }
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");
3269 },3300 },
3270 .direct => return operand,3301 .direct => return operand,
3271 }3302 }
src/target.zig+1-1
...@@ -949,7 +949,7 @@ pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, compt...@@ -949,7 +949,7 @@ pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, compt
949 else => true,949 else => true,
950 },950 },
951 .restricted_types => switch (backend) {951 .restricted_types => switch (backend) {
952 .stage2_c, .stage2_x86_64 => true,952 .stage2_c, .stage2_llvm, .stage2_x86_64 => true,
953 else => false,953 else => false,
954 },954 },
955 };955 };