Commit 356a865b87

Andrew Kelley <andrew@ziglang.org>
2022-06-01 02:38:42
stage2: introduce support for noalias
Not implemented yet is enhancements to coerceInMemory to account for noalias parameters. Related to #11498.
1 parent 602af1b
src/codegen/llvm.zig
@@ -725,8 +725,12 @@ pub const Object = struct {
                         try args.append(param);
 
                         if (param_ty.isPtrAtRuntime()) {
-                            // TODO noalias attribute
                             const ptr_info = param_ty.ptrInfo().data;
+                            if (math.cast(u5, it.zig_index - 1)) |i| {
+                                if (@truncate(u1, fn_info.noalias_bits >> i) != 0) {
+                                    dg.addArgAttr(llvm_func, llvm_arg_i, "noalias");
+                                }
+                            }
                             if (!param_ty.isPtrLikeOptional() and !ptr_info.@"allowzero") {
                                 dg.addArgAttr(llvm_func, llvm_arg_i, "nonnull");
                             }
src/AstGen.zig
@@ -1155,14 +1155,20 @@ fn fnProtoExpr(
 
     const block_inst = try gz.makeBlockInst(.block_inline, node);
 
+    var noalias_bits: u32 = 0;
     const is_var_args = is_var_args: {
         var param_type_i: usize = 0;
         var it = fn_proto.iterate(tree);
         while (it.next()) |param| : (param_type_i += 1) {
-            const is_comptime = if (param.comptime_noalias) |token|
-                token_tags[token] == .keyword_comptime
-            else
-                false;
+            const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) {
+                .keyword_noalias => is_comptime: {
+                    noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
+                        return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
+                    break :is_comptime false;
+                },
+                .keyword_comptime => true,
+                else => false,
+            } else false;
 
             const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
                 switch (token_tags[token]) {
@@ -1255,6 +1261,7 @@ fn fnProtoExpr(
         .is_inferred_error = false,
         .is_test = false,
         .is_extern = false,
+        .noalias_bits = noalias_bits,
     });
 
     _ = try block_scope.addBreak(.break_inline, block_inst, result);
@@ -3381,15 +3388,21 @@ fn fnDecl(
     // align, linksection, and addrspace is passed in the func instruction in this case.
     wip_members.nextDecl(is_pub, is_export, false, false);
 
+    var noalias_bits: u32 = 0;
     var params_scope = &fn_gz.base;
     const is_var_args = is_var_args: {
         var param_type_i: usize = 0;
         var it = fn_proto.iterate(tree);
         while (it.next()) |param| : (param_type_i += 1) {
-            const is_comptime = if (param.comptime_noalias) |token|
-                token_tags[token] == .keyword_comptime
-            else
-                false;
+            const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) {
+                .keyword_noalias => is_comptime: {
+                    noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
+                        return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
+                    break :is_comptime false;
+                },
+                .keyword_comptime => true,
+                else => false,
+            } else false;
 
             const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
                 switch (token_tags[token]) {
@@ -3576,6 +3589,7 @@ fn fnDecl(
             .is_inferred_error = false,
             .is_test = false,
             .is_extern = true,
+            .noalias_bits = noalias_bits,
         });
     } else func: {
         if (is_var_args) {
@@ -3623,6 +3637,7 @@ fn fnDecl(
             .is_inferred_error = is_inferred_error,
             .is_test = false,
             .is_extern = false,
+            .noalias_bits = noalias_bits,
         });
     };
 
@@ -4057,6 +4072,7 @@ fn testDecl(
         .is_inferred_error = true,
         .is_test = true,
         .is_extern = false,
+        .noalias_bits = 0,
     });
 
     _ = try decl_block.addBreak(.break_inline, block_inst, func_inst);
@@ -10024,6 +10040,7 @@ const GenZir = struct {
         ret_ref: Zir.Inst.Ref,
 
         lib_name: u32,
+        noalias_bits: u32,
         is_var_args: bool,
         is_inferred_error: bool,
         is_test: bool,
@@ -10071,7 +10088,7 @@ const GenZir = struct {
         if (args.cc_ref != .none or args.lib_name != 0 or
             args.is_var_args or args.is_test or args.is_extern or
             args.align_ref != .none or args.section_ref != .none or
-            args.addrspace_ref != .none)
+            args.addrspace_ref != .none or args.noalias_bits != 0)
         {
             var align_body: []Zir.Inst.Index = &.{};
             var addrspace_body: []Zir.Inst.Index = &.{};
@@ -10093,7 +10110,8 @@ const GenZir = struct {
                     fancyFnExprExtraLen(cc_body, args.cc_ref) +
                     fancyFnExprExtraLen(ret_body, ret_ref) +
                     body.len + src_locs.len +
-                    @boolToInt(args.lib_name != 0),
+                    @boolToInt(args.lib_name != 0) +
+                    @boolToInt(args.noalias_bits != 0),
             );
             const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{
                 .param_block = args.param_block,
@@ -10104,6 +10122,7 @@ const GenZir = struct {
                     .is_test = args.is_test,
                     .is_extern = args.is_extern,
                     .has_lib_name = args.lib_name != 0,
+                    .has_any_noalias = args.noalias_bits != 0,
 
                     .has_align_ref = args.align_ref != .none,
                     .has_addrspace_ref = args.addrspace_ref != .none,
@@ -10159,6 +10178,10 @@ const GenZir = struct {
                 astgen.extra.appendAssumeCapacity(@enumToInt(ret_ref));
             }
 
+            if (args.noalias_bits != 0) {
+                astgen.extra.appendAssumeCapacity(args.noalias_bits);
+            }
+
             astgen.extra.appendSliceAssumeCapacity(body);
             astgen.extra.appendSliceAssumeCapacity(src_locs);
 
src/print_zir.zig
@@ -1961,6 +1961,7 @@ const Writer = struct {
             body,
             src,
             src_locs,
+            0,
         );
     }
 
@@ -2034,6 +2035,12 @@ const Writer = struct {
             extra_index += 1;
         }
 
+        const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: {
+            const x = self.code.extra[extra_index];
+            extra_index += 1;
+            break :blk x;
+        } else 0;
+
         const body = self.code.extra[extra_index..][0..extra.data.body_len];
         extra_index += body.len;
 
@@ -2059,6 +2066,7 @@ const Writer = struct {
             body,
             src,
             src_locs,
+            noalias_bits,
         );
     }
 
@@ -2216,6 +2224,7 @@ const Writer = struct {
         body: []const Zir.Inst.Index,
         src: LazySrcLoc,
         src_locs: Zir.Inst.Func.SrcLocs,
+        noalias_bits: u32,
     ) !void {
         try self.writeOptionalInstRefOrBody(stream, "align=", align_ref, align_body);
         try self.writeOptionalInstRefOrBody(stream, "addrspace=", addrspace_ref, addrspace_body);
@@ -2226,6 +2235,10 @@ const Writer = struct {
         try self.writeFlag(stream, "extern, ", is_extern);
         try self.writeFlag(stream, "inferror, ", inferred_error_set);
 
+        if (noalias_bits != 0) {
+            try stream.print("noalias=0b{b}, ", .{noalias_bits});
+        }
+
         try stream.writeAll("body=");
         try self.writeBracedBody(stream, body);
         try stream.writeAll(") ");
src/Sema.zig
@@ -6565,12 +6565,10 @@ fn zirFunc(
         has_body,
         src_locs,
         null,
+        0,
     );
 }
 
-// TODO this function and its callsites along with funcCommon need to be reworked
-// to handle when callconv, align, linksection, addrspace depend on comptime values
-// (thus triggering error.GenericPoison)
 fn resolveGenericBody(
     sema: *Sema,
     block: *Block,
@@ -6696,6 +6694,7 @@ fn funcCommon(
     has_body: bool,
     src_locs: Zir.Inst.Func.SrcLocs,
     opt_lib_name: ?[]const u8,
+    noalias_bits: u32,
 ) CompileError!Air.Inst.Ref {
     const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
 
@@ -6807,6 +6806,7 @@ fn funcCommon(
             .addrspace_is_generic = address_space == null,
             .is_var_args = var_args,
             .is_generic = is_generic,
+            .noalias_bits = noalias_bits,
         });
     };
 
@@ -9626,7 +9626,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
 
-    // In `**` rhs has to be comptime-known, but lhs can be runtime-known
+    // In `**` rhs must be comptime-known, but lhs can be runtime-known
     const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize);
 
     if (lhs_ty.isTuple()) {
@@ -11916,7 +11916,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 
             try sema.queueFullTypeResolution(try error_field_ty.copy(sema.arena));
 
-            // If the error set is inferred it has to be resolved at this point
+            // If the error set is inferred it must be resolved at this point
             try sema.resolveInferredErrorSetTy(block, src, ty);
 
             // Build our list of Error values
@@ -16970,6 +16970,12 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
         break :blk ty;
     } else Type.void;
 
+    const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: {
+        const x = sema.code.extra[extra_index];
+        extra_index += 1;
+        break :blk x;
+    } else 0;
+
     var src_locs: Zir.Inst.Func.SrcLocs = undefined;
     const has_body = extra.data.body_len != 0;
     if (has_body) {
@@ -16996,6 +17002,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
         has_body,
         src_locs,
         lib_name,
+        noalias_bits,
     );
 }
 
src/type.zig
@@ -646,6 +646,9 @@ pub const Type = extern union {
                 if (a_info.is_generic != b_info.is_generic)
                     return false;
 
+                if (a_info.noalias_bits != b_info.noalias_bits)
+                    return false;
+
                 if (!a_info.cc_is_generic and a_info.cc != b_info.cc)
                     return false;
 
@@ -1047,6 +1050,7 @@ pub const Type = extern union {
                 }
                 std.hash.autoHash(hasher, fn_info.is_var_args);
                 std.hash.autoHash(hasher, fn_info.is_generic);
+                std.hash.autoHash(hasher, fn_info.noalias_bits);
 
                 std.hash.autoHash(hasher, fn_info.param_types.len);
                 for (fn_info.param_types) |param_ty, i| {
@@ -1424,6 +1428,11 @@ pub const Type = extern union {
                     .is_var_args = payload.is_var_args,
                     .is_generic = payload.is_generic,
                     .comptime_params = comptime_params.ptr,
+                    .align_is_generic = payload.align_is_generic,
+                    .cc_is_generic = payload.cc_is_generic,
+                    .section_is_generic = payload.section_is_generic,
+                    .addrspace_is_generic = payload.addrspace_is_generic,
+                    .noalias_bits = payload.noalias_bits,
                 });
             },
             .pointer => {
@@ -4738,6 +4747,11 @@ pub const Type = extern union {
                 .alignment = 0,
                 .is_var_args = false,
                 .is_generic = false,
+                .align_is_generic = false,
+                .cc_is_generic = false,
+                .section_is_generic = false,
+                .addrspace_is_generic = false,
+                .noalias_bits = 0,
             },
             .fn_void_no_args => .{
                 .param_types = &.{},
@@ -4747,6 +4761,11 @@ pub const Type = extern union {
                 .alignment = 0,
                 .is_var_args = false,
                 .is_generic = false,
+                .align_is_generic = false,
+                .cc_is_generic = false,
+                .section_is_generic = false,
+                .addrspace_is_generic = false,
+                .noalias_bits = 0,
             },
             .fn_naked_noreturn_no_args => .{
                 .param_types = &.{},
@@ -4756,6 +4775,11 @@ pub const Type = extern union {
                 .alignment = 0,
                 .is_var_args = false,
                 .is_generic = false,
+                .align_is_generic = false,
+                .cc_is_generic = false,
+                .section_is_generic = false,
+                .addrspace_is_generic = false,
+                .noalias_bits = 0,
             },
             .fn_ccc_void_no_args => .{
                 .param_types = &.{},
@@ -4765,6 +4789,11 @@ pub const Type = extern union {
                 .alignment = 0,
                 .is_var_args = false,
                 .is_generic = false,
+                .align_is_generic = false,
+                .cc_is_generic = false,
+                .section_is_generic = false,
+                .addrspace_is_generic = false,
+                .noalias_bits = 0,
             },
             .function => ty.castTag(.function).?.data,
 
@@ -6123,13 +6152,14 @@ pub const Type = extern union {
                 return_type: Type,
                 /// If zero use default target function code alignment.
                 alignment: u32,
+                noalias_bits: u32,
                 cc: std.builtin.CallingConvention,
                 is_var_args: bool,
                 is_generic: bool,
-                align_is_generic: bool = false,
-                cc_is_generic: bool = false,
-                section_is_generic: bool = false,
-                addrspace_is_generic: bool = false,
+                align_is_generic: bool,
+                cc_is_generic: bool,
+                section_is_generic: bool,
+                addrspace_is_generic: bool,
 
                 pub fn paramIsComptime(self: @This(), i: usize) bool {
                     assert(i < self.param_types.len);
src/Zir.zig
@@ -2670,8 +2670,10 @@ pub const Inst = struct {
     ///   14. ret_ty_body_len: u32
     ///   15. ret_ty_body: u32 // for each ret_ty_body_len
     /// }
-    /// 16. body: Index // for each body_len
-    /// 17. src_locs: Func.SrcLocs // if body_len != 0
+    /// 16. noalias_bits: u32 // if has_any_noalias
+    ///     - each bit starting with LSB corresponds to parameter indexes
+    /// 17. body: Index // for each body_len
+    /// 18. src_locs: Func.SrcLocs // if body_len != 0
     pub const FuncFancy = struct {
         /// Points to the block that contains the param instructions for this function.
         param_block: Index,
@@ -2699,7 +2701,8 @@ pub const Inst = struct {
             has_ret_ty_ref: bool,
             has_ret_ty_body: bool,
             has_lib_name: bool,
-            _: u17 = undefined,
+            has_any_noalias: bool,
+            _: u16 = undefined,
         };
     };
 
@@ -3699,6 +3702,8 @@ fn findDeclsInner(
                 extra_index += 1;
             }
 
+            extra_index += @boolToInt(extra.data.bits.has_any_noalias);
+
             const body = zir.extra[extra_index..][0..extra.data.body_len];
             return zir.findDeclsBody(list, body);
         },
@@ -3906,6 +3911,9 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
                 ret_ty_ref = @intToEnum(Inst.Ref, zir.extra[extra_index]);
                 extra_index += 1;
             }
+
+            extra_index += @boolToInt(extra.data.bits.has_any_noalias);
+
             const body = zir.extra[extra_index..][0..extra.data.body_len];
             extra_index += body.len;
             break :blk .{