diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index 93c2e492f0ea906030564d298477eb59a76d2478..17f08aa00ba089ceb285d25a41d63ada31eb8ccc 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -2415,6 +2415,10 @@ pub const Global = struct { self.ptr(builder).unnamed_addr = unnamed_addr; } + pub fn setType(self: Index, ty: Type, builder: *Builder) void { + self.ptr(builder).type = ty; + } + pub fn setDebugMetadata(self: Index, dbg: Metadata, builder: *Builder) void { self.ptr(builder).dbg = dbg.toOptional(); } @@ -2661,6 +2665,18 @@ pub const Variable = struct { return self.ptr(builder).alignment; } + pub fn setLinkage(self: Index, linkage: Linkage, builder: *Builder) void { + self.ptrConst(builder).global.setLinkage(linkage, builder); + } + + pub fn setType(self: Index, ty: Type, builder: *Builder) void { + self.ptrConst(builder).global.setType(ty, builder); + } + + pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void { + return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder); + } + pub fn setGlobalVariableExpression(self: Index, expression: Metadata, builder: *Builder) void { self.ptrConst(builder).global.setDebugMetadata(expression, builder); } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 344a4fcf56cbb98e592a2665c640dfa146ca964e..9565c88ec05d8ac3180a25563bc342b10b5729de 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -558,6 +558,8 @@ pub const Object = struct { val: InternPool.Index, @"addrspace": std.builtin.AddressSpace, }, Builder.Variable.Index), + /// Maps restricted types to supporting decls. + restricted_map: std.array_hash_map.Auto(InternPool.Index, RestrictedDecls), /// Maps enum types to their corresponding LLVM functions for implementing the `tag_name` instruction. enum_tag_name_map: std.AutoHashMapUnmanaged(InternPool.Index, Builder.Function.Index), /// 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 { .zcu = zcu, .nav_map = .empty, .uav_map = .empty, + .restricted_map = .empty, .enum_tag_name_map = .empty, .named_enum_map = .empty, .type_map = .empty, @@ -686,6 +689,8 @@ pub const Object = struct { self.debug_types.deinit(gpa); self.nav_map.deinit(gpa); self.uav_map.deinit(gpa); + for (self.restricted_map.values()) |*value| value.deinit(gpa); + self.restricted_map.deinit(gpa); self.enum_tag_name_map.deinit(gpa); self.named_enum_map.deinit(gpa); self.type_map.deinit(gpa); @@ -693,6 +698,65 @@ pub const Object = struct { self.* = undefined; } + const RestrictedDecls = struct { + len: Builder.Variable.Index, + array: Builder.Variable.Index, + values: std.array_hash_map.Auto(InternPool.Index, Builder.Constant), + + fn deinit(rd: *RestrictedDecls, gpa: Allocator) void { + rd.values.deinit(gpa); + rd.* = undefined; + } + }; + pub fn getRestrictedDecls(o: *Object, ty: Type) Allocator.Error!*RestrictedDecls { + const gop = try o.restricted_map.getOrPut(o.gpa, ty.toIntern()); + if (gop.found_existing) return gop.value_ptr; + errdefer _ = o.restricted_map.pop().?; + + const target = o.zcu.getTarget(); + const ip = &o.zcu.intern_pool; + const ptr_align = Type.ptrAbiAlignment(target).toLlvm(); + + const ty_name = ty.containerTypeName(ip).toSlice(ip); + gop.value_ptr.* = .{ + .len = try o.builder.addVariable( + try o.builder.strtabStringFmt("{s}.len", .{ty_name}), + try o.lowerType(.usize), + .default, + ), + .array = try o.builder.addVariable( + try o.builder.strtabString(ty_name), + .void, + .default, + ), + .values = .empty, + }; + gop.value_ptr.len.setLinkage(.private, &o.builder); + gop.value_ptr.len.setMutability(.constant, &o.builder); + gop.value_ptr.len.setAlignment(ptr_align, &o.builder); + gop.value_ptr.len.setUnnamedAddr(.unnamed_addr, &o.builder); + gop.value_ptr.array.setLinkage(.private, &o.builder); + gop.value_ptr.array.setMutability(.constant, &o.builder); + gop.value_ptr.array.setAlignment(ptr_align, &o.builder); + // Setting unnamed_addr here would reduce safety, and the module emitting the safety checks may not be the same module + // that defined the restricted type. In any case, llvm will add unnamed_addr itself if no safety checks end up being emitted. + gop.value_ptr.array.setUnnamedAddr(.default, &o.builder); + return gop.value_ptr; + } + fn genRestrictedDecls(o: *Object) Allocator.Error!void { + for (o.restricted_map.values()) |restricted_decls| { + const len = restricted_decls.values.count(); + try restricted_decls.len.setInitializer( + try o.builder.intConst(restricted_decls.len.typeOf(&o.builder), len), + &o.builder, + ); + try restricted_decls.array.setInitializer(try o.builder.arrayConst( + try o.builder.arrayType(len, .ptr), + restricted_decls.values.values(), + ), &o.builder); + } + } + fn genErrorNameTable(o: *Object) Allocator.Error!void { // If o.error_name_table is null, then it was not referenced by any instructions. if (o.error_name_table == .none) return; @@ -771,6 +835,7 @@ pub const Object = struct { const diags = &comp.link_diags; { + try o.genRestrictedDecls(); if (o.errors_len_variable != .none) { const errors_len = zcu.intern_pool.global_error_set.getNamesFromMainThread().len; const init_val = try o.builder.intConst(try o.errorIntType(), errors_len); @@ -2006,55 +2071,64 @@ pub const Object = struct { .pointer => { const ptr_size = Type.ptrAbiSize(zcu.getTarget()); const ptr_align = Type.ptrAbiAlignment(zcu.getTarget()); - - if (ty.isSlice(zcu)) { - const debug_ptr_type = try o.builder.debugMemberType( - try o.builder.metadataString("ptr"), + switch (ty.restrictedRepr(zcu)) { + .indirect => return o.builder.debugPointerType( + name, null, // file - ty_fwd_ref, + o.debug_compile_unit.unwrap().?, // scope 0, // line - try o.getDebugType(pt, ty.slicePtrFieldType(zcu)), + try o.getDebugType(pt, ty.unrestrictedType(zcu).?), ptr_size * 8, ptr_align.toByteUnits().? * 8, 0, // offset - ); + ), + .direct => if (ty.isSlice(zcu)) { + const debug_ptr_type = try o.builder.debugMemberType( + try o.builder.metadataString("ptr"), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, ty.slicePtrFieldType(zcu)), + ptr_size * 8, + ptr_align.toByteUnits().? * 8, + 0, // offset + ); - const debug_len_type = try o.builder.debugMemberType( - try o.builder.metadataString("len"), - null, // file - ty_fwd_ref, - 0, // line - try o.getDebugType(pt, .usize), - ptr_size * 8, - ptr_align.toByteUnits().? * 8, - ptr_size * 8, - ); + const debug_len_type = try o.builder.debugMemberType( + try o.builder.metadataString("len"), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, .usize), + ptr_size * 8, + ptr_align.toByteUnits().? * 8, + ptr_size * 8, + ); - return o.builder.debugStructType( + return o.builder.debugStructType( + name, + null, // file + o.debug_compile_unit.unwrap().?, // scope + 0, // line + null, // underlying type + ptr_size * 2 * 8, + ptr_align.toByteUnits().? * 8, + try o.builder.metadataTuple(&.{ + debug_ptr_type, + debug_len_type, + }), + ); + } else return o.builder.debugPointerType( name, null, // file o.debug_compile_unit.unwrap().?, // scope 0, // line - null, // underlying type - ptr_size * 2 * 8, + try o.getDebugType(pt, ty.childType(zcu)), + ptr_size * 8, ptr_align.toByteUnits().? * 8, - try o.builder.metadataTuple(&.{ - debug_ptr_type, - debug_len_type, - }), - ); + 0, // offset + ), } - - return o.builder.debugPointerType( - name, - null, // file - o.debug_compile_unit.unwrap().?, // scope - 0, // line - try o.getDebugType(pt, ty.childType(zcu)), - ptr_size * 8, - ptr_align.toByteUnits().? * 8, - 0, // offset - ); }, .array => return o.builder.debugArrayType( name, @@ -3047,7 +3121,7 @@ pub const Object = struct { .empty_tuple, .none, => unreachable, - else => switch (ip.indexToKey(t.toIntern())) { + else => t: switch (ip.indexToKey(t.toIntern())) { .int_type => |int_type| try o.builder.intType(int_type.bits), .ptr_type => |ptr_type| type: { const ptr_ty = try o.builder.ptrType( @@ -3061,7 +3135,10 @@ pub const Object = struct { }), }; }, - .restricted_ptr_type => @panic("TODO implement restricted pointers"), + .restricted_ptr_type => |restricted_ptr_type| switch (t.restrictedRepr(zcu)) { + .indirect => .ptr, + .direct => continue :t .{ .ptr_type = ip.indexToKey(restricted_ptr_type.unrestricted_ptr_type).ptr_type }, + }, .array_type => |array_type| o.builder.arrayType( array_type.lenIncludingSentinel(), try o.lowerType(.fromInterned(array_type.child)), @@ -3545,7 +3622,27 @@ pub const Object = struct { 128 => try o.builder.fp128Const(val.toFloat(f128, zcu)), else => unreachable, }, - .ptr => try o.lowerPtr(arg_val, 0), + .ptr => switch (ty.restrictedRepr(zcu)) { + .indirect => { + const restricted_decls = try o.getRestrictedDecls(ty); + const gop = try restricted_decls.values.getOrPut(o.gpa, arg_val); + if (!gop.found_existing) gop.value_ptr.* = try o.lowerValue(try ip.getCoerced( + zcu.gpa, + zcu.comp.io, + .main, // FIXME + arg_val, + ty.unrestrictedType(zcu).?.toIntern(), + )); + return o.builder.gepConst( + .inbounds, + .ptr, + restricted_decls.array.toConst(&o.builder), + null, + &.{try o.builder.intConst(.i64, gop.index)}, + ); + }, + .direct => try o.lowerPtr(arg_val, 0), + }, .slice => |slice| return o.builder.structConst(try o.lowerType(ty), &.{ try o.lowerValue(slice.ptr), try o.lowerValue(slice.len), diff --git a/src/codegen/llvm/FuncGen.zig b/src/codegen/llvm/FuncGen.zig index 5b730dfbeb04f7a714c857a5a1821c8408033b62..872deee59fa4c1dd6ec8e10c533a22925eb38782 100644 --- a/src/codegen/llvm/FuncGen.zig +++ b/src/codegen/llvm/FuncGen.zig @@ -3253,19 +3253,50 @@ fn airWrapErrUnionErr(self: *FuncGen, body_tail: []const Air.Inst.Index) Allocat return result_ptr; } -fn airUnwrapRestricted(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Builder.Value { - const o = self.object; +fn airUnwrapRestricted(fg: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Builder.Value { + const o = fg.object; const zcu = o.zcu; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const unrestricted_ty = ty_op.ty.toType(); - const restricted_ty = self.typeOf(ty_op.operand); - const operand = try self.resolveInst(ty_op.operand); + const target = zcu.getTarget(); + const ty_op = fg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; + const restricted_ty = fg.typeOf(ty_op.operand); + const operand = try fg.resolveInst(ty_op.operand); switch (restricted_ty.restrictedRepr(zcu)) { .indirect => { + const unrestricted_ty = ty_op.ty.toType(); if (safety) { - // TODO + const restricted_decls = try o.getRestrictedDecls(restricted_ty); + const llvm_usize_ty = restricted_decls.len.typeOf(&o.builder); + const array = try o.builder.castConst(.ptrtoint, restricted_decls.array.toConst(&o.builder), llvm_usize_ty); + const ptr_diff = try fg.wip.bin( + .sub, + try fg.wip.cast(.ptrtoint, operand, llvm_usize_ty, "unwrap_restricted.operand_int"), + array.toValue(), + "unwrap_restricted.ptr_diff", + ); + const index = try fg.wip.callIntrinsic(.normal, .none, .fshr, &.{llvm_usize_ty}, &.{ + ptr_diff, + ptr_diff, + try o.builder.intValue(llvm_usize_ty, std.math.log2_int(u64, Type.ptrAbiSize(target))), + }, "unwrap_restricted.index"); + const len = try fg.wip.load( + .normal, + llvm_usize_ty, + restricted_decls.len.toValue(&o.builder), + Type.ptrAbiAlignment(target).toLlvm(), + "unwrap_restricted.len", + ); + const ok = try fg.wip.icmp(.ult, index, len, "unwrap_restricted.ok"); + + const invalid_block = try fg.wip.block(1, "unwrap_restricted.invalid"); + const valid_block = try fg.wip.block(1, "unwrap_restricted.valid"); + _ = try fg.wip.brCond(ok, valid_block, invalid_block, .none); + + fg.wip.cursor = .{ .block = invalid_block }; + try fg.buildSimplePanic(.corrupt_restricted_pointer); + + fg.wip.cursor = .{ .block = valid_block }; } - return self.wip.load(.normal, .ptr, operand, unrestricted_ty.abiAlignment(zcu).toLlvm(), "restricted.unwrap"); + return fg.wip.load(.normal, .ptr, operand, unrestricted_ty.abiAlignment(zcu).toLlvm(), "unwrap_restricted"); }, .direct => return operand, } diff --git a/src/target.zig b/src/target.zig index 1b49472b56791aed6933b9c5a5355734b7d20598..104687fd11124713670fa2af2d62cc9beb0a21c1 100644 --- a/src/target.zig +++ b/src/target.zig @@ -949,7 +949,7 @@ pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, compt else => true, }, .restricted_types => switch (backend) { - .stage2_c, .stage2_x86_64 => true, + .stage2_c, .stage2_llvm, .stage2_x86_64 => true, else => false, }, };