Commit 5b79f08ee8

Jacob Young <jacobly0@users.noreply.github.com>
2023-08-07 09:36:11
llvm: finish converting attributes to use Builder and the C LLVM API
1 parent b63d974
src/codegen/llvm/bindings.zig
@@ -115,12 +115,15 @@ pub const Context = opaque {
 };
 
 pub const Value = opaque {
-    pub const addAttributeAtIndex = ZigLLVMAddAttributeAtIndex;
-    extern fn ZigLLVMAddAttributeAtIndex(*Value, Idx: AttributeIndex, A: *Attribute) void;
+    pub const addAttributeAtIndex = LLVMAddAttributeAtIndex;
+    extern fn LLVMAddAttributeAtIndex(F: *Value, Idx: AttributeIndex, A: *Attribute) void;
 
     pub const removeEnumAttributeAtIndex = LLVMRemoveEnumAttributeAtIndex;
     extern fn LLVMRemoveEnumAttributeAtIndex(F: *Value, Idx: AttributeIndex, KindID: c_uint) void;
 
+    pub const removeStringAttributeAtIndex = LLVMRemoveStringAttributeAtIndex;
+    extern fn LLVMRemoveStringAttributeAtIndex(F: *Value, Idx: AttributeIndex, K: [*]const u8, KLen: c_uint) void;
+
     pub const getFirstBasicBlock = LLVMGetFirstBasicBlock;
     extern fn LLVMGetFirstBasicBlock(Fn: *Value) ?*BasicBlock;
 
@@ -342,9 +345,6 @@ pub const Value = opaque {
     pub const deleteFunction = LLVMDeleteFunction;
     extern fn LLVMDeleteFunction(Fn: *Value) void;
 
-    pub const addSretAttr = ZigLLVMAddSretAttr;
-    extern fn ZigLLVMAddSretAttr(fn_ref: *Value, type_val: *Type) void;
-
     pub const getParam = LLVMGetParam;
     extern fn LLVMGetParam(Fn: *Value, Index: c_uint) *Value;
 
@@ -369,9 +369,6 @@ pub const Value = opaque {
     pub const getAlignment = LLVMGetAlignment;
     extern fn LLVMGetAlignment(V: *Value) c_uint;
 
-    pub const addFunctionAttr = ZigLLVMAddFunctionAttr;
-    extern fn ZigLLVMAddFunctionAttr(Fn: *Value, attr_name: [*:0]const u8, attr_value: [*:0]const u8) void;
-
     pub const addByValAttr = ZigLLVMAddByValAttr;
     extern fn ZigLLVMAddByValAttr(Fn: *Value, ArgNo: c_uint, type: *Type) void;
 
src/codegen/llvm/Builder.zig
@@ -1349,21 +1349,29 @@ pub const Attribute = union(Kind) {
         //sanitize_memtag,
         sanitize_address_dyninit,
 
-        string = std.math.maxInt(u31) - 1,
-        none = std.math.maxInt(u31),
+        string = std.math.maxInt(u31),
+        none = std.math.maxInt(u32),
         _,
 
         pub const len = @typeInfo(Kind).Enum.fields.len - 2;
 
         pub fn fromString(str: String) Kind {
             assert(!str.isAnon());
-            return @enumFromInt(@intFromEnum(str));
+            const kind: Kind = @enumFromInt(@intFromEnum(str));
+            assert(kind != .none);
+            return kind;
         }
 
         fn toString(self: Kind) ?String {
+            assert(self != .none);
             const str: String = @enumFromInt(@intFromEnum(self));
             return if (str.isAnon()) null else str;
         }
+
+        fn toLlvm(self: Kind, builder: *const Builder) *c_uint {
+            assert(builder.useLibLlvm());
+            return &builder.llvm.attribute_kind_ids.?[@intFromEnum(self)];
+        }
     };
 
     pub const FpClass = packed struct(u32) {
@@ -3147,6 +3155,86 @@ pub const Function = struct {
             return self.toConst(builder).toValue();
         }
 
+        pub fn setAttributes(
+            self: Index,
+            new_function_attributes: FunctionAttributes,
+            builder: *Builder,
+        ) void {
+            if (builder.useLibLlvm()) {
+                const llvm_function = self.toLlvm(builder);
+                const old_function_attributes = self.ptrConst(builder).attributes;
+                for (0..@max(
+                    old_function_attributes.slice(builder).len,
+                    new_function_attributes.slice(builder).len,
+                )) |function_attribute_index| {
+                    const llvm_attribute_index =
+                        @as(llvm.AttributeIndex, @intCast(function_attribute_index)) -% 1;
+                    const old_attributes_slice =
+                        old_function_attributes.get(function_attribute_index, builder).slice(builder);
+                    const new_attributes_slice =
+                        new_function_attributes.get(function_attribute_index, builder).slice(builder);
+                    var old_attribute_index: usize = 0;
+                    var new_attribute_index: usize = 0;
+                    while (true) {
+                        const old_attribute_kind = if (old_attribute_index < old_attributes_slice.len)
+                            old_attributes_slice[old_attribute_index].getKind(builder)
+                        else
+                            .none;
+                        const new_attribute_kind = if (new_attribute_index < new_attributes_slice.len)
+                            new_attributes_slice[new_attribute_index].getKind(builder)
+                        else
+                            .none;
+                        switch (std.math.order(
+                            @intFromEnum(old_attribute_kind),
+                            @intFromEnum(new_attribute_kind),
+                        )) {
+                            .lt => {
+                                // Removed
+                                if (old_attribute_kind.toString()) |name| {
+                                    const slice = name.slice(builder).?;
+                                    llvm_function.removeStringAttributeAtIndex(
+                                        llvm_attribute_index,
+                                        slice.ptr,
+                                        @intCast(slice.len),
+                                    );
+                                } else {
+                                    const llvm_kind_id = old_attribute_kind.toLlvm(builder).*;
+                                    assert(llvm_kind_id != 0);
+                                    llvm_function.removeEnumAttributeAtIndex(
+                                        llvm_attribute_index,
+                                        llvm_kind_id,
+                                    );
+                                }
+                                old_attribute_index += 1;
+                                continue;
+                            },
+                            .eq => {
+                                // Iteration finished
+                                if (old_attribute_kind == .none) break;
+                                // No change
+                                if (old_attributes_slice[old_attribute_index] ==
+                                    new_attributes_slice[new_attribute_index])
+                                {
+                                    old_attribute_index += 1;
+                                    new_attribute_index += 1;
+                                    continue;
+                                }
+                                old_attribute_index += 1;
+                            },
+                            .gt => {},
+                        }
+                        // New or changed
+                        llvm_function.addAttributeAtIndex(
+                            llvm_attribute_index,
+                            new_attributes_slice[new_attribute_index].toLlvm(builder),
+                        );
+                        new_attribute_index += 1;
+                    }
+                }
+            }
+            self.ptr(builder).attributes = new_function_attributes;
+        }
+
         pub fn toLlvm(self: Index, builder: *const Builder) *llvm.Value {
             return self.ptrConst(builder).global.toLlvm(builder);
         }
@@ -5048,9 +5136,8 @@ pub const WipFunction = struct {
                 .tail, .tail_fast => .Tail,
             });
             for (0.., function_attributes.slice(self.builder)) |index, attributes| {
-                const attribute_index = @as(llvm.AttributeIndex, @intCast(index)) -% 1;
                 for (attributes.slice(self.builder)) |attribute| llvm_instruction.addCallSiteAttribute(
-                    attribute_index,
+                    @as(llvm.AttributeIndex, @intCast(index)) -% 1,
                     attribute.toLlvm(self.builder),
                 );
             }
@@ -7368,16 +7455,16 @@ pub fn attr(self: *Builder, attribute: Attribute) Allocator.Error!Attribute.Inde
         gop.value_ptr.* = {};
         if (self.useLibLlvm()) self.llvm.attributes.appendAssumeCapacity(switch (attribute) {
             else => llvm_attr: {
-                const kind_id = &self.llvm.attribute_kind_ids.?[@intFromEnum(attribute)];
-                if (kind_id.* == 0) {
+                const llvm_kind_id = attribute.getKind().toLlvm(self);
+                if (llvm_kind_id.* == 0) {
                     const name = @tagName(attribute);
-                    kind_id.* = llvm.getEnumAttributeKindForName(name.ptr, name.len);
-                    assert(kind_id.* != 0);
+                    llvm_kind_id.* = llvm.getEnumAttributeKindForName(name.ptr, name.len);
+                    assert(llvm_kind_id.* != 0);
                 }
                 break :llvm_attr switch (attribute) {
                     else => switch (attribute) {
                         inline else => |value| self.llvm.context.createEnumAttribute(
-                            kind_id.*,
+                            llvm_kind_id.*,
                             switch (@TypeOf(value)) {
                                 void => 0,
                                 u32 => value,
@@ -7411,7 +7498,7 @@ pub fn attr(self: *Builder, attribute: Attribute) Allocator.Error!Attribute.Inde
                     .inalloca,
                     .sret,
                     .elementtype,
-                    => |ty| self.llvm.context.createTypeAttribute(kind_id.*, ty.toLlvm(self)),
+                    => |ty| self.llvm.context.createTypeAttribute(llvm_kind_id.*, ty.toLlvm(self)),
                     .string, .none => unreachable,
                 };
             },
src/codegen/llvm.zig
@@ -1314,28 +1314,21 @@ pub const Object = struct {
 
         if (func.analysis(ip).is_noinline) {
             try attributes.addFnAttr(.@"noinline", &o.builder);
-            o.addFnAttr(llvm_func, "noinline");
         } else {
             _ = try attributes.removeFnAttr(.@"noinline");
-            Object.removeFnAttr(llvm_func, "noinline");
         }
 
         if (func.analysis(ip).stack_alignment.toByteUnitsOptional()) |alignment| {
             try attributes.addFnAttr(.{ .alignstack = Builder.Alignment.fromByteUnits(alignment) }, &o.builder);
             try attributes.addFnAttr(.@"noinline", &o.builder);
-            o.addFnAttrInt(llvm_func, "alignstack", alignment);
-            o.addFnAttr(llvm_func, "noinline");
         } else {
             _ = try attributes.removeFnAttr(.alignstack);
-            Object.removeFnAttr(llvm_func, "alignstack");
         }
 
         if (func.analysis(ip).is_cold) {
             try attributes.addFnAttr(.cold, &o.builder);
-            o.addFnAttr(llvm_func, "cold");
         } else {
             _ = try attributes.removeFnAttr(.cold);
-            Object.removeFnAttr(llvm_func, "cold");
         }
 
         // TODO: disable this if safety is off for the function scope
@@ -1346,10 +1339,6 @@ pub const Object = struct {
                 .kind = try o.builder.string("stack-protector-buffer-size"),
                 .value = try o.builder.fmt("{d}", .{ssp_buf_size}),
             } }, &o.builder);
-            var buf: [12]u8 = undefined;
-            const arg = std.fmt.bufPrintZ(&buf, "{d}", .{ssp_buf_size}) catch unreachable;
-            o.addFnAttr(llvm_func, "sspstrong");
-            o.addFnAttrString(llvm_func, "stack-protector-buffer-size", arg);
         }
 
         // TODO: disable this if safety is off for the function scope
@@ -1358,13 +1347,11 @@ pub const Object = struct {
                 .kind = try o.builder.string("probe-stack"),
                 .value = try o.builder.string("__zig_probe_stack"),
             } }, &o.builder);
-            o.addFnAttrString(llvm_func, "probe-stack", "__zig_probe_stack");
         } else if (target.os.tag == .uefi) {
             try attributes.addFnAttr(.{ .string = .{
                 .kind = try o.builder.string("no-stack-arg-probe"),
                 .value = .empty,
             } }, &o.builder);
-            o.addFnAttrString(llvm_func, "no-stack-arg-probe", "");
         }
 
         if (ip.stringToSliceUnwrap(decl.@"linksection")) |section| {
@@ -1389,14 +1376,8 @@ pub const Object = struct {
         } else .none;
 
         if (ccAbiPromoteInt(fn_info.cc, mod, fn_info.return_type.toType())) |s| switch (s) {
-            .signed => {
-                try attributes.addRetAttr(.signext, &o.builder);
-                o.addAttr(llvm_func, 0, "signext");
-            },
-            .unsigned => {
-                try attributes.addRetAttr(.zeroext, &o.builder);
-                o.addAttr(llvm_func, 0, "zeroext");
-            },
+            .signed => try attributes.addRetAttr(.signext, &o.builder),
+            .unsigned => try attributes.addRetAttr(.zeroext, &o.builder),
         };
 
         const err_return_tracing = fn_info.return_type.toType().isError(mod) and
@@ -1437,7 +1418,7 @@ pub const Object = struct {
                         } else {
                             args.appendAssumeCapacity(param);
 
-                            try o.addByValParamAttrsOld(&attributes, llvm_func, param_ty, param_index, fn_info, llvm_arg_i);
+                            try o.addByValParamAttrs(&attributes, param_ty, param_index, fn_info, llvm_arg_i);
                         }
                         llvm_arg_i += 1;
                     },
@@ -1447,7 +1428,7 @@ pub const Object = struct {
                         const param = wip.arg(llvm_arg_i);
                         const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod));
 
-                        try o.addByRefParamAttrsOld(&attributes, llvm_func, llvm_arg_i, alignment, it.byval_attr, param_llvm_ty);
+                        try o.addByRefParamAttrs(&attributes, llvm_arg_i, alignment, it.byval_attr, param_llvm_ty);
                         llvm_arg_i += 1;
 
                         if (isByRef(param_ty, mod)) {
@@ -1463,7 +1444,6 @@ pub const Object = struct {
                         const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod));
 
                         try attributes.addParamAttr(llvm_arg_i, .noundef, &o.builder);
-                        o.addArgAttr(llvm_func, llvm_arg_i, "noundef");
                         llvm_arg_i += 1;
 
                         if (isByRef(param_ty, mod)) {
@@ -1500,23 +1480,19 @@ pub const Object = struct {
                         if (math.cast(u5, it.zig_index - 1)) |i| {
                             if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) {
                                 try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder);
-                                o.addArgAttr(llvm_func, llvm_arg_i, "noalias");
                             }
                         }
                         if (param_ty.zigTypeTag(mod) != .Optional) {
                             try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder);
-                            o.addArgAttr(llvm_func, llvm_arg_i, "nonnull");
                         }
                         if (ptr_info.flags.is_const) {
                             try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder);
-                            o.addArgAttr(llvm_func, llvm_arg_i, "readonly");
                         }
                         const elem_align = Builder.Alignment.fromByteUnits(
                             ptr_info.flags.alignment.toByteUnitsOptional() orelse
                                 @max(ptr_info.child.toType().abiAlignment(mod), 1),
                         );
                         try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align }, &o.builder);
-                        o.addArgAttrInt(llvm_func, llvm_arg_i, "align", elem_align.toByteUnits() orelse 0);
                         const ptr_param = wip.arg(llvm_arg_i);
                         llvm_arg_i += 1;
                         const len_param = wip.arg(llvm_arg_i);
@@ -1590,7 +1566,7 @@ pub const Object = struct {
             }
         }
 
-        function.ptr(&o.builder).attributes = try attributes.finish(&o.builder);
+        function.setAttributes(try attributes.finish(&o.builder), &o.builder);
 
         var di_file: ?*llvm.DIFile = null;
         var di_scope: ?*llvm.DIScope = null;
@@ -2951,15 +2927,11 @@ pub const Object = struct {
                     .kind = try o.builder.string("wasm-import-name"),
                     .value = try o.builder.string(ip.stringToSlice(decl.name)),
                 } }, &o.builder);
-                o.addFnAttrString(llvm_fn, "wasm-import-name", ip.stringToSlice(decl.name));
                 if (ip.stringToSliceUnwrap(decl.getOwnedExternFunc(mod).?.lib_name)) |lib_name| {
-                    if (!std.mem.eql(u8, lib_name, "c")) {
-                        try attributes.addFnAttr(.{ .string = .{
-                            .kind = try o.builder.string("wasm-import-module"),
-                            .value = try o.builder.string(lib_name),
-                        } }, &o.builder);
-                        o.addFnAttrString(llvm_fn, "wasm-import-module", lib_name);
-                    }
+                    if (!std.mem.eql(u8, lib_name, "c")) try attributes.addFnAttr(.{ .string = .{
+                        .kind = try o.builder.string("wasm-import-module"),
+                        .value = try o.builder.string(lib_name),
+                    } }, &o.builder);
                 }
             }
         }
@@ -2969,12 +2941,9 @@ pub const Object = struct {
             // Sret pointers must not be address 0
             try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder);
             try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder);
-            o.addArgAttr(llvm_fn, llvm_arg_i, "nonnull"); // Sret pointers must not be address 0
-            o.addArgAttr(llvm_fn, llvm_arg_i, "noalias");
 
             const raw_llvm_ret_ty = try o.lowerType(fn_info.return_type.toType());
             try attributes.addParamAttr(llvm_arg_i, .{ .sret = raw_llvm_ret_ty }, &o.builder);
-            llvm_fn.addSretAttr(raw_llvm_ret_ty.toLlvm(&o.builder));
 
             llvm_arg_i += 1;
         }
@@ -2984,7 +2953,6 @@ pub const Object = struct {
 
         if (err_return_tracing) {
             try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder);
-            o.addArgAttr(llvm_fn, llvm_arg_i, "nonnull");
             llvm_arg_i += 1;
         }
 
@@ -2995,7 +2963,6 @@ pub const Object = struct {
             },
             .Naked => {
                 try attributes.addFnAttr(.naked, &o.builder);
-                o.addFnAttr(llvm_fn, "naked");
             },
             .Async => {
                 function.call_conv = .fastcc;
@@ -3014,11 +2981,10 @@ pub const Object = struct {
         }
 
         // Function attributes that are independent of analysis results of the function body.
-        try o.addCommonFnAttributes(&attributes, llvm_fn);
+        try o.addCommonFnAttributes(&attributes);
 
         if (fn_info.return_type == .noreturn_type) {
             try attributes.addFnAttr(.noreturn, &o.builder);
-            o.addFnAttr(llvm_fn, "noreturn");
         }
 
         // Add parameter attributes. We handle only the case of extern functions (no body)
@@ -3031,7 +2997,7 @@ pub const Object = struct {
                     const param_index = it.zig_index - 1;
                     const param_ty = fn_info.param_types.get(ip)[param_index].toType();
                     if (!isByRef(param_ty, mod)) {
-                        try o.addByValParamAttrsOld(&attributes, llvm_fn, param_ty, param_index, fn_info, it.llvm_index - 1);
+                        try o.addByValParamAttrs(&attributes, param_ty, param_index, fn_info, it.llvm_index - 1);
                     }
                 },
                 .byref => {
@@ -3039,11 +3005,10 @@ pub const Object = struct {
                     const param_llvm_ty = try o.lowerType(param_ty.toType());
                     const alignment =
                         Builder.Alignment.fromByteUnits(param_ty.toType().abiAlignment(mod));
-                    try o.addByRefParamAttrsOld(&attributes, llvm_fn, it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty);
+                    try o.addByRefParamAttrs(&attributes, it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty);
                 },
                 .byref_mut => {
                     try attributes.addParamAttr(it.llvm_index - 1, .noundef, &o.builder);
-                    o.addArgAttr(llvm_fn, it.llvm_index - 1, "noundef");
                 },
                 // No attributes needed for these.
                 .no_bits,
@@ -3060,43 +3025,36 @@ pub const Object = struct {
             };
         }
 
-        function.attributes = try attributes.finish(&o.builder);
-
         try o.builder.llvm.globals.append(o.gpa, llvm_fn);
         gop.value_ptr.* = try o.builder.addGlobal(fqn, global);
         try o.builder.functions.append(o.gpa, function);
+        global.kind.function.setAttributes(try attributes.finish(&o.builder), &o.builder);
         return global.kind.function;
     }
 
     fn addCommonFnAttributes(
         o: *Object,
         attributes: *Builder.FunctionAttributes.Wip,
-        llvm_fn: *llvm.Value,
     ) Allocator.Error!void {
         const comp = o.module.comp;
 
         if (!comp.bin_file.options.red_zone) {
             try attributes.addFnAttr(.noredzone, &o.builder);
-            o.addFnAttr(llvm_fn, "noredzone");
         }
         if (comp.bin_file.options.omit_frame_pointer) {
             try attributes.addFnAttr(.{ .string = .{
                 .kind = try o.builder.string("frame-pointer"),
                 .value = try o.builder.string("none"),
             } }, &o.builder);
-            o.addFnAttrString(llvm_fn, "frame-pointer", "none");
         } else {
             try attributes.addFnAttr(.{ .string = .{
                 .kind = try o.builder.string("frame-pointer"),
                 .value = try o.builder.string("all"),
             } }, &o.builder);
-            o.addFnAttrString(llvm_fn, "frame-pointer", "all");
         }
         try attributes.addFnAttr(.nounwind, &o.builder);
-        o.addFnAttr(llvm_fn, "nounwind");
         if (comp.unwind_tables) {
             try attributes.addFnAttr(.{ .uwtable = Builder.Attribute.UwTable.default }, &o.builder);
-            o.addFnAttrInt(llvm_fn, "uwtable", 2);
         }
         if (comp.bin_file.options.skip_linker_dependencies or
             comp.bin_file.options.no_builtin)
@@ -3107,38 +3065,31 @@ pub const Object = struct {
             // body of memcpy with a call to memcpy, which would then cause a stack
             // overflow instead of performing memcpy.
             try attributes.addFnAttr(.nobuiltin, &o.builder);
-            o.addFnAttr(llvm_fn, "nobuiltin");
         }
         if (comp.bin_file.options.optimize_mode == .ReleaseSmall) {
             try attributes.addFnAttr(.minsize, &o.builder);
             try attributes.addFnAttr(.optsize, &o.builder);
-            o.addFnAttr(llvm_fn, "minsize");
-            o.addFnAttr(llvm_fn, "optsize");
         }
         if (comp.bin_file.options.tsan) {
             try attributes.addFnAttr(.sanitize_thread, &o.builder);
-            o.addFnAttr(llvm_fn, "sanitize_thread");
         }
         if (comp.getTarget().cpu.model.llvm_name) |s| {
             try attributes.addFnAttr(.{ .string = .{
                 .kind = try o.builder.string("target-cpu"),
                 .value = try o.builder.string(s),
             } }, &o.builder);
-            llvm_fn.addFunctionAttr("target-cpu", s);
         }
         if (comp.bin_file.options.llvm_cpu_features) |s| {
             try attributes.addFnAttr(.{ .string = .{
                 .kind = try o.builder.string("target-features"),
                 .value = try o.builder.string(std.mem.span(s)),
             } }, &o.builder);
-            llvm_fn.addFunctionAttr("target-features", s);
         }
         if (comp.getTarget().cpu.arch.isBpf()) {
             try attributes.addFnAttr(.{ .string = .{
                 .kind = try o.builder.string("no-builtins"),
                 .value = .empty,
             } }, &o.builder);
-            llvm_fn.addFunctionAttr("no-builtins", "");
         }
     }
 
@@ -4483,69 +4434,6 @@ pub const Object = struct {
         return o.builder.castConst(.inttoptr, try o.builder.intConst(llvm_usize, int), llvm_ptr_ty);
     }
 
-    fn addAttr(o: *Object, val: *llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {
-        return o.addAttrInt(val, index, name, 0);
-    }
-
-    fn addArgAttr(o: *Object, fn_val: *llvm.Value, param_index: u32, attr_name: []const u8) void {
-        return o.addAttr(fn_val, param_index + 1, attr_name);
-    }
-
-    fn addArgAttrInt(o: *Object, fn_val: *llvm.Value, param_index: u32, attr_name: []const u8, int: u64) void {
-        return o.addAttrInt(fn_val, param_index + 1, attr_name, int);
-    }
-
-    fn removeAttr(val: *llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {
-        const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len);
-        assert(kind_id != 0);
-        val.removeEnumAttributeAtIndex(index, kind_id);
-    }
-
-    fn addAttrInt(
-        o: *Object,
-        val: *llvm.Value,
-        index: llvm.AttributeIndex,
-        name: []const u8,
-        int: u64,
-    ) void {
-        const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len);
-        assert(kind_id != 0);
-        const llvm_attr = o.builder.llvm.context.createEnumAttribute(kind_id, int);
-        val.addAttributeAtIndex(index, llvm_attr);
-    }
-
-    fn addAttrString(
-        o: *Object,
-        val: *llvm.Value,
-        index: llvm.AttributeIndex,
-        name: []const u8,
-        value: []const u8,
-    ) void {
-        const llvm_attr = o.builder.llvm.context.createStringAttribute(
-            name.ptr,
-            @intCast(name.len),
-            value.ptr,
-            @intCast(value.len),
-        );
-        val.addAttributeAtIndex(index, llvm_attr);
-    }
-
-    fn addFnAttr(o: *Object, val: *llvm.Value, name: []const u8) void {
-        o.addAttr(val, std.math.maxInt(llvm.AttributeIndex), name);
-    }
-
-    fn addFnAttrString(o: *Object, val: *llvm.Value, name: []const u8, value: []const u8) void {
-        o.addAttrString(val, std.math.maxInt(llvm.AttributeIndex), name, value);
-    }
-
-    fn removeFnAttr(fn_val: *llvm.Value, name: []const u8) void {
-        removeAttr(fn_val, std.math.maxInt(llvm.AttributeIndex), name);
-    }
-
-    fn addFnAttrInt(o: *Object, fn_val: *llvm.Value, name: []const u8, int: u64) void {
-        return o.addAttrInt(fn_val, std.math.maxInt(llvm.AttributeIndex), name, int);
-    }
-
     /// If the operand type of an atomic operation is not byte sized we need to
     /// widen it before using it and then truncate the result.
     /// RMW exchange of floating-point values is bitcasted to same-sized integer
@@ -4608,80 +4496,13 @@ pub const Object = struct {
         attributes: *Builder.FunctionAttributes.Wip,
         llvm_arg_i: u32,
         alignment: Builder.Alignment,
-        byval_attr: bool,
+        byval: bool,
         param_llvm_ty: Builder.Type,
     ) Allocator.Error!void {
         try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder);
         try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder);
         try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = alignment }, &o.builder);
-        if (byval_attr) {
-            try attributes.addParamAttr(llvm_arg_i, .{ .byval = param_llvm_ty }, &o.builder);
-        }
-    }
-
-    fn addByValParamAttrsOld(
-        o: *Object,
-        attributes: *Builder.FunctionAttributes.Wip,
-        llvm_fn: *llvm.Value,
-        param_ty: Type,
-        param_index: u32,
-        fn_info: InternPool.Key.FuncType,
-        llvm_arg_i: u32,
-    ) Allocator.Error!void {
-        const mod = o.module;
-        if (param_ty.isPtrAtRuntime(mod)) {
-            const ptr_info = param_ty.ptrInfo(mod);
-            if (math.cast(u5, param_index)) |i| {
-                if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) {
-                    try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder);
-                    o.addArgAttr(llvm_fn, llvm_arg_i, "noalias");
-                }
-            }
-            if (!param_ty.isPtrLikeOptional(mod) and !ptr_info.flags.is_allowzero) {
-                try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder);
-                o.addArgAttr(llvm_fn, llvm_arg_i, "nonnull");
-            }
-            if (ptr_info.flags.is_const) {
-                try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder);
-                o.addArgAttr(llvm_fn, llvm_arg_i, "readonly");
-            }
-            const elem_align = Builder.Alignment.fromByteUnits(
-                ptr_info.flags.alignment.toByteUnitsOptional() orelse
-                    @max(ptr_info.child.toType().abiAlignment(mod), 1),
-            );
-            try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align }, &o.builder);
-            o.addArgAttrInt(llvm_fn, llvm_arg_i, "align", elem_align.toByteUnits() orelse 0);
-        } else if (ccAbiPromoteInt(fn_info.cc, mod, param_ty)) |s| switch (s) {
-            .signed => {
-                try attributes.addParamAttr(llvm_arg_i, .signext, &o.builder);
-                o.addArgAttr(llvm_fn, llvm_arg_i, "signext");
-            },
-            .unsigned => {
-                try attributes.addParamAttr(llvm_arg_i, .zeroext, &o.builder);
-                o.addArgAttr(llvm_fn, llvm_arg_i, "zeroext");
-            },
-        };
-    }
-
-    fn addByRefParamAttrsOld(
-        o: *Object,
-        attributes: *Builder.FunctionAttributes.Wip,
-        llvm_fn: *llvm.Value,
-        llvm_arg_i: u32,
-        alignment: Builder.Alignment,
-        byval_attr: bool,
-        param_llvm_ty: Builder.Type,
-    ) Allocator.Error!void {
-        try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder);
-        try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder);
-        try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = alignment }, &o.builder);
-        o.addArgAttr(llvm_fn, llvm_arg_i, "nonnull");
-        o.addArgAttr(llvm_fn, llvm_arg_i, "readonly");
-        o.addArgAttrInt(llvm_fn, llvm_arg_i, "align", alignment.toByteUnits() orelse 0);
-        if (byval_attr) {
-            try attributes.addParamAttr(llvm_arg_i, .{ .byval = param_llvm_ty }, &o.builder);
-            llvm_fn.addByValAttr(llvm_arg_i, param_llvm_ty.toLlvm(&o.builder));
-        }
+        if (byval) try attributes.addParamAttr(llvm_arg_i, .{ .byval = param_llvm_ty }, &o.builder);
     }
 };
 
@@ -9503,16 +9324,16 @@ pub const FuncGen = struct {
 
         var attributes: Builder.FunctionAttributes.Wip = .{};
         defer attributes.deinit(&o.builder);
-
-        function_index.toLlvm(&o.builder).setLinkage(.Internal);
-        function_index.toLlvm(&o.builder).setFunctionCallConv(.Fast);
-        try o.addCommonFnAttributes(&attributes, function_index.toLlvm(&o.builder));
+        try o.addCommonFnAttributes(&attributes);
+        function_index.setAttributes(try attributes.finish(&o.builder), &o.builder);
 
         function_index.ptrConst(&o.builder).global.ptr(&o.builder).linkage = .internal;
         function_index.ptr(&o.builder).call_conv = .fastcc;
-        function_index.ptr(&o.builder).attributes = try attributes.finish(&o.builder);
         gop.value_ptr.* = function_index;
 
+        function_index.toLlvm(&o.builder).setLinkage(.Internal);
+        function_index.toLlvm(&o.builder).setFunctionCallConv(.Fast);
+
         var wip = try Builder.WipFunction.init(&o.builder, function_index);
         defer wip.deinit();
         wip.cursor = .{ .block = try wip.block(0, "Entry") };
@@ -9577,16 +9398,16 @@ pub const FuncGen = struct {
 
         var attributes: Builder.FunctionAttributes.Wip = .{};
         defer attributes.deinit(&o.builder);
-
-        function_index.toLlvm(&o.builder).setLinkage(.Internal);
-        function_index.toLlvm(&o.builder).setFunctionCallConv(.Fast);
-        try o.addCommonFnAttributes(&attributes, function_index.toLlvm(&o.builder));
+        try o.addCommonFnAttributes(&attributes);
+        function_index.setAttributes(try attributes.finish(&o.builder), &o.builder);
 
         function_index.ptrConst(&o.builder).global.ptr(&o.builder).linkage = .internal;
         function_index.ptr(&o.builder).call_conv = .fastcc;
-        function_index.ptr(&o.builder).attributes = try attributes.finish(&o.builder);
         gop.value_ptr.* = function_index.ptrConst(&o.builder).global;
 
+        function_index.toLlvm(&o.builder).setLinkage(.Internal);
+        function_index.toLlvm(&o.builder).setFunctionCallConv(.Fast);
+
         var wip = try Builder.WipFunction.init(&o.builder, function_index);
         defer wip.deinit();
         wip.cursor = .{ .block = try wip.block(0, "Entry") };
@@ -9659,14 +9480,14 @@ pub const FuncGen = struct {
 
         var attributes: Builder.FunctionAttributes.Wip = .{};
         defer attributes.deinit(&o.builder);
-
-        function_index.toLlvm(&o.builder).setLinkage(.Internal);
-        function_index.toLlvm(&o.builder).setFunctionCallConv(.Fast);
-        try o.addCommonFnAttributes(&attributes, function_index.toLlvm(&o.builder));
+        try o.addCommonFnAttributes(&attributes);
+        function_index.setAttributes(try attributes.finish(&o.builder), &o.builder);
 
         function_index.ptrConst(&o.builder).global.ptr(&o.builder).linkage = .internal;
         function_index.ptr(&o.builder).call_conv = .fastcc;
-        function_index.ptr(&o.builder).attributes = try attributes.finish(&o.builder);
+
+        function_index.toLlvm(&o.builder).setLinkage(.Internal);
+        function_index.toLlvm(&o.builder).setFunctionCallConv(.Fast);
 
         return function_index;
     }
src/zig_llvm.cpp
@@ -408,14 +408,6 @@ void ZigLLVMSetTailCallKind(LLVMValueRef Call, enum ZigLLVMTailCallKind TailCall
     unwrap<CallInst>(Call)->setTailCallKind(TCK);
 }
 
-void ZigLLVMAddAttributeAtIndex(LLVMValueRef Val, unsigned Idx, LLVMAttributeRef A) {
-    if (isa<Function>(unwrap(Val))) {
-        unwrap<Function>(Val)->addAttributeAtIndex(Idx, unwrap(A));
-    } else {
-        unwrap<CallInst>(Val)->addAttributeAtIndex(Idx, unwrap(A));
-    }
-}
-
 LLVMValueRef ZigLLVMBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign,
         LLVMValueRef Src, unsigned SrcAlign, LLVMValueRef Size, bool isVolatile)
 {
@@ -950,36 +942,6 @@ void ZigLLVMSetFastMath(LLVMBuilderRef builder_wrapped, bool on_state) {
     }
 }
 
-void ZigLLVMAddByValAttr(LLVMValueRef Val, unsigned ArgNo, LLVMTypeRef type_val) {
-    if (isa<Function>(unwrap(Val))) {
-        Function *func = unwrap<Function>(Val);
-        AttrBuilder attr_builder(func->getContext());
-        Type *llvm_type = unwrap<Type>(type_val);
-        attr_builder.addByValAttr(llvm_type);
-        func->addParamAttrs(ArgNo, attr_builder);
-    } else {
-        CallInst *call = unwrap<CallInst>(Val);
-        AttrBuilder attr_builder(call->getContext());
-        Type *llvm_type = unwrap<Type>(type_val);
-        attr_builder.addByValAttr(llvm_type);
-        // NOTE: +1 here since index 0 refers to the return value
-        call->addAttributeAtIndex(ArgNo + 1, attr_builder.getAttribute(Attribute::ByVal));
-    }
-}
-
-void ZigLLVMAddSretAttr(LLVMValueRef fn_ref, LLVMTypeRef type_val) {
-    Function *func = unwrap<Function>(fn_ref);
-    AttrBuilder attr_builder(func->getContext());
-    Type *llvm_type = unwrap<Type>(type_val);
-    attr_builder.addStructRetAttr(llvm_type);
-    func->addParamAttrs(0, attr_builder);
-}
-
-void ZigLLVMAddFunctionAttr(LLVMValueRef fn_ref, const char *attr_name, const char *attr_value) {
-    Function *func = unwrap<Function>(fn_ref);
-    func->addFnAttr(attr_name, attr_value);
-}
-
 void ZigLLVMParseCommandLineOptions(size_t argc, const char *const *argv) {
     cl::ParseCommandLineOptions(argc, argv);
 }
@@ -1172,14 +1134,6 @@ bool ZigLLDLinkWasm(int argc, const char **argv, bool can_exit_early, bool disab
     return lld::wasm::link(args, llvm::outs(), llvm::errs(), can_exit_early, disable_output);
 }
 
-inline LLVMAttributeRef wrap(Attribute Attr) {
-    return reinterpret_cast<LLVMAttributeRef>(Attr.getRawPointer());
-}
-
-inline Attribute unwrap(LLVMAttributeRef Attr) {
-    return Attribute::fromRawPointer(Attr);
-}
-
 LLVMValueRef ZigLLVMBuildAndReduce(LLVMBuilderRef B, LLVMValueRef Val) {
     return wrap(unwrap(B)->CreateAndReduce(unwrap(Val)));
 }
src/zig_llvm.h
@@ -122,15 +122,6 @@ enum ZigLLVM_CallingConv {
     ZigLLVM_MaxID = 1023,
 };
 
-enum ZigLLVM_CallAttr {
-    ZigLLVM_CallAttrAuto,
-    ZigLLVM_CallAttrNeverTail,
-    ZigLLVM_CallAttrNeverInline,
-    ZigLLVM_CallAttrAlwaysTail,
-    ZigLLVM_CallAttrAlwaysInline,
-};
-ZIG_EXTERN_C void ZigLLVMAddAttributeAtIndex(LLVMValueRef Val, unsigned Idx, LLVMAttributeRef A);
-
 ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign,
         LLVMValueRef Src, unsigned SrcAlign, LLVMValueRef Size, bool isVolatile);
 
@@ -301,11 +292,6 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMInsertDbgValueIntrinsicAtEnd(struct ZigLLVMDIBu
 
 ZIG_EXTERN_C void ZigLLVMSetFastMath(LLVMBuilderRef builder_wrapped, bool on_state);
 
-ZIG_EXTERN_C void ZigLLVMAddFunctionAttr(LLVMValueRef fn, const char *attr_name, const char *attr_value);
-ZIG_EXTERN_C void ZigLLVMAddByValAttr(LLVMValueRef fn_ref, unsigned ArgNo, LLVMTypeRef type_val);
-ZIG_EXTERN_C void ZigLLVMAddSretAttr(LLVMValueRef fn_ref, LLVMTypeRef type_val);
-ZIG_EXTERN_C void ZigLLVMAddFunctionAttrCold(LLVMValueRef fn);
-
 ZIG_EXTERN_C void ZigLLVMParseCommandLineOptions(size_t argc, const char *const *argv);
 
 ZIG_EXTERN_C ZigLLVMDIGlobalVariable* ZigLLVMGlobalGetVariable(ZigLLVMDIGlobalVariableExpression *global_variable_expression);