diff --git a/src/AstGen.zig b/src/AstGen.zig index 5c4fc88483..ab5befa4ba 100644 --- a/src/AstGen.zig +++ b/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); diff --git a/src/Sema.zig b/src/Sema.zig index d4c49973a1..9f45e7ffd0 100644 --- a/src/Sema.zig +++ b/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, ); } diff --git a/src/Zir.zig b/src/Zir.zig index 040f54cc39..f09f2015e0 100644 --- a/src/Zir.zig +++ b/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 .{ diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a6ea6255e3..34203b9536 100644 --- a/src/codegen/llvm.zig +++ b/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"); } diff --git a/src/print_zir.zig b/src/print_zir.zig index 30098f5372..8081a94093 100644 --- a/src/print_zir.zig +++ b/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(") "); diff --git a/src/type.zig b/src/type.zig index 9a072fa911..94fcd0a96c 100644 --- a/src/type.zig +++ b/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);