Commit aa44f8f0fd
Changed files (4)
src
codegen
src/codegen/llvm/bindings.zig
@@ -26,10 +26,13 @@ pub const Context = opaque {
extern fn LLVMContextDispose(C: *Context) void;
pub const createEnumAttribute = LLVMCreateEnumAttribute;
- extern fn LLVMCreateEnumAttribute(*Context, KindID: c_uint, Val: u64) *Attribute;
+ extern fn LLVMCreateEnumAttribute(C: *Context, KindID: c_uint, Val: u64) *Attribute;
+
+ pub const createTypeAttribute = LLVMCreateTypeAttribute;
+ extern fn LLVMCreateTypeAttribute(C: *Context, KindID: c_uint, Type: *Type) *Attribute;
pub const createStringAttribute = LLVMCreateStringAttribute;
- extern fn LLVMCreateStringAttribute(*Context, Key: [*]const u8, Key_Len: c_uint, Value: [*]const u8, Value_Len: c_uint) *Attribute;
+ extern fn LLVMCreateStringAttribute(C: *Context, Key: [*]const u8, Key_Len: c_uint, Value: [*]const u8, Value_Len: c_uint) *Attribute;
pub const pointerType = LLVMPointerTypeInContext;
extern fn LLVMPointerTypeInContext(C: *Context, AddressSpace: c_uint) *Type;
@@ -309,12 +312,18 @@ pub const Value = opaque {
pub const setAlignment = LLVMSetAlignment;
extern fn LLVMSetAlignment(V: *Value, Bytes: c_uint) void;
- pub const getFunctionCallConv = LLVMGetFunctionCallConv;
- extern fn LLVMGetFunctionCallConv(Fn: *Value) CallConv;
-
pub const setFunctionCallConv = LLVMSetFunctionCallConv;
extern fn LLVMSetFunctionCallConv(Fn: *Value, CC: CallConv) void;
+ pub const setInstructionCallConv = LLVMSetInstructionCallConv;
+ extern fn LLVMSetInstructionCallConv(Instr: *Value, CC: CallConv) void;
+
+ pub const setTailCallKind = ZigLLVMSetTailCallKind;
+ extern fn ZigLLVMSetTailCallKind(CallInst: *Value, TailCallKind: TailCallKind) void;
+
+ pub const addCallSiteAttribute = LLVMAddCallSiteAttribute;
+ extern fn LLVMAddCallSiteAttribute(C: *Value, Idx: AttributeIndex, A: *Attribute) void;
+
pub const fnSetSubprogram = ZigLLVMFnSetSubprogram;
extern fn ZigLLVMFnSetSubprogram(f: *Value, subprogram: *DISubprogram) void;
@@ -642,7 +651,17 @@ pub const Builder = opaque {
Name: [*:0]const u8,
) *Value;
- pub const buildCall = ZigLLVMBuildCall;
+ pub const buildCall = LLVMBuildCall2;
+ extern fn LLVMBuildCall2(
+ *Builder,
+ *Type,
+ Fn: *Value,
+ Args: [*]const *Value,
+ NumArgs: c_uint,
+ Name: [*:0]const u8,
+ ) *Value;
+
+ pub const buildCallOld = ZigLLVMBuildCall;
extern fn ZigLLVMBuildCall(
*Builder,
*Type,
@@ -1605,6 +1624,13 @@ pub const CallAttr = enum(c_int) {
AlwaysInline,
};
+pub const TailCallKind = enum(c_uint) {
+ None,
+ Tail,
+ MustTail,
+ NoTail,
+};
+
pub const DLLStorageClass = enum(c_uint) {
Default,
DLLImport,
src/codegen/llvm/Builder.zig
@@ -4,13 +4,15 @@ strip: bool,
llvm: if (build_options.have_llvm) struct {
context: *llvm.Context,
- module: ?*llvm.Module = null,
- target: ?*llvm.Target = null,
- di_builder: ?*llvm.DIBuilder = null,
- di_compile_unit: ?*llvm.DICompileUnit = null,
- types: std.ArrayListUnmanaged(*llvm.Type) = .{},
- globals: std.ArrayListUnmanaged(*llvm.Value) = .{},
- constants: std.ArrayListUnmanaged(*llvm.Value) = .{},
+ module: ?*llvm.Module,
+ target: ?*llvm.Target,
+ di_builder: ?*llvm.DIBuilder,
+ di_compile_unit: ?*llvm.DICompileUnit,
+ attribute_kind_ids: ?*[Attribute.Kind.len]c_uint,
+ attributes: std.ArrayListUnmanaged(*llvm.Attribute),
+ types: std.ArrayListUnmanaged(*llvm.Type),
+ globals: std.ArrayListUnmanaged(*llvm.Value),
+ constants: std.ArrayListUnmanaged(*llvm.Value),
} else void,
source_filename: String,
@@ -18,8 +20,8 @@ data_layout: String,
target_triple: String,
string_map: std.AutoArrayHashMapUnmanaged(void, void),
-string_bytes: std.ArrayListUnmanaged(u8),
string_indices: std.ArrayListUnmanaged(u32),
+string_bytes: std.ArrayListUnmanaged(u8),
types: std.AutoArrayHashMapUnmanaged(String, Type),
next_unnamed_type: String,
@@ -28,6 +30,11 @@ type_map: std.AutoArrayHashMapUnmanaged(void, void),
type_items: std.ArrayListUnmanaged(Type.Item),
type_extra: std.ArrayListUnmanaged(u32),
+attributes: std.AutoArrayHashMapUnmanaged(Attribute.Storage, void),
+attributes_map: std.AutoArrayHashMapUnmanaged(void, void),
+attributes_indices: std.ArrayListUnmanaged(u32),
+attributes_extra: std.ArrayListUnmanaged(u32),
+
globals: std.AutoArrayHashMapUnmanaged(String, Global),
next_unnamed_global: String,
next_replaced_global: String,
@@ -41,6 +48,7 @@ constant_items: std.MultiArrayList(Constant.Item),
constant_extra: std.ArrayListUnmanaged(u32),
constant_limbs: std.ArrayListUnmanaged(std.math.big.Limb),
+pub const expected_args_len = 16;
pub const expected_fields_len = 32;
pub const expected_gep_indices_len = 8;
pub const expected_cases_len = 8;
@@ -65,7 +73,7 @@ pub const String = enum(u32) {
return self.toIndex() == null;
}
- pub fn toSlice(self: String, b: *const Builder) ?[:0]const u8 {
+ pub fn slice(self: String, b: *const Builder) ?[:0]const u8 {
const index = self.toIndex() orelse return null;
const start = b.string_indices.items[index];
const end = b.string_indices.items[index + 1];
@@ -85,9 +93,9 @@ pub const String = enum(u32) {
if (comptime std.mem.indexOfNone(u8, fmt_str, "@\"")) |_|
@compileError("invalid format string: '" ++ fmt_str ++ "'");
assert(data.string != .none);
- const slice = data.string.toSlice(data.builder) orelse
+ const sentinel_slice = data.string.slice(data.builder) orelse
return writer.print("{d}", .{@intFromEnum(data.string)});
- const full_slice = slice[0 .. slice.len + comptime @intFromBool(
+ const full_slice = sentinel_slice[0 .. sentinel_slice.len + comptime @intFromBool(
std.mem.indexOfScalar(u8, fmt_str, '@') != null,
)];
const need_quotes = (comptime std.mem.indexOfScalar(u8, fmt_str, '"') != null) or
@@ -108,6 +116,7 @@ pub const String = enum(u32) {
return @enumFromInt(@as(u32, @intCast((index orelse return .none) +
@intFromEnum(String.empty))));
}
+
fn toIndex(self: String) ?usize {
return std.math.sub(u32, @intFromEnum(self), @intFromEnum(String.empty)) catch null;
}
@@ -118,7 +127,7 @@ pub const String = enum(u32) {
return @truncate(std.hash.Wyhash.hash(0, key));
}
pub fn eql(ctx: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool {
- return std.mem.eql(u8, lhs_key, String.fromIndex(rhs_index).toSlice(ctx.builder).?);
+ return std.mem.eql(u8, lhs_key, String.fromIndex(rhs_index).slice(ctx.builder).?);
}
};
};
@@ -290,6 +299,17 @@ pub const Type = enum(u32) {
};
}
+ pub fn pointerAddrSpace(self: Type, builder: *const Builder) AddrSpace {
+ switch (self) {
+ .ptr => return .default,
+ else => {
+ const item = builder.type_items.items[@intFromEnum(self)];
+ assert(item.tag == .pointer);
+ return @enumFromInt(item.data);
+ },
+ }
+ }
+
pub fn isFunction(self: Type, builder: *const Builder) bool {
return switch (self.tag(builder)) {
.function, .vararg_function => true,
@@ -606,7 +626,7 @@ pub const Type = enum(u32) {
var extra = data.builder.typeExtraDataTrail(Type.Target, item.data);
const types = extra.trail.next(extra.data.types_len, Type, data.builder);
const ints = extra.trail.next(extra.data.ints_len, u32, data.builder);
- try writer.print("t{s}", .{extra.data.name.toSlice(data.builder).?});
+ try writer.print("t{s}", .{extra.data.name.slice(data.builder).?});
for (types) |ty| try writer.print("_{m}", .{ty.fmt(data.builder)});
for (ints) |int| try writer.print("_{d}", .{int});
try writer.writeByte('t');
@@ -641,7 +661,7 @@ pub const Type = enum(u32) {
.named_structure => {
const extra = data.builder.typeExtraData(Type.NamedStructure, item.data);
try writer.writeAll("s_");
- if (extra.id.toSlice(data.builder)) |id| try writer.writeAll(id);
+ if (extra.id.slice(data.builder)) |id| try writer.writeAll(id);
},
}
return;
@@ -823,6 +843,789 @@ pub const Type = enum(u32) {
}
};
+pub const Attribute = union(Kind) {
+ // Parameter Attributes
+ zeroext,
+ signext,
+ inreg,
+ byval: Type,
+ byref: Type,
+ preallocated: Type,
+ inalloca: Type,
+ sret: Type,
+ elementtype: Type,
+ @"align": Alignment,
+ @"noalias",
+ nocapture,
+ nofree,
+ nest,
+ returned,
+ nonnull,
+ dereferenceable: u32,
+ dereferenceable_or_null: u32,
+ swiftself,
+ swiftasync,
+ swifterror,
+ immarg,
+ noundef,
+ nofpclass: FpClass,
+ alignstack: Alignment,
+ allocalign,
+ allocptr,
+ readnone,
+ readonly,
+ writeonly,
+
+ // Function Attributes
+ //alignstack: Alignment,
+ allockind: AllocKind,
+ allocsize: AllocSize,
+ alwaysinline,
+ builtin,
+ cold,
+ convergent,
+ disable_sanitizer_information,
+ fn_ret_thunk_extern,
+ hot,
+ inlinehint,
+ jumptable,
+ memory: Memory,
+ minsize,
+ naked,
+ nobuiltin,
+ nocallback,
+ noduplicate,
+ //nofree,
+ noimplicitfloat,
+ @"noinline",
+ nomerge,
+ nonlazybind,
+ noprofile,
+ skipprofile,
+ noredzone,
+ noreturn,
+ norecurse,
+ willreturn,
+ nosync,
+ nounwind,
+ nosanitize_bounds,
+ nosanitize_coverage,
+ null_pointer_is_valid,
+ optforfuzzing,
+ optnone,
+ optsize,
+ //preallocated: Type,
+ returns_twice,
+ safestack,
+ sanitize_address,
+ sanitize_memory,
+ sanitize_thread,
+ sanitize_hwaddress,
+ sanitize_memtag,
+ speculative_load_hardening,
+ speculatable,
+ ssp,
+ sspstrong,
+ sspreq,
+ strictfp,
+ uwtable: UwTable,
+ nocf_check,
+ shadowcallstack,
+ mustprogress,
+ vscale_range: VScaleRange,
+
+ // Global Attributes
+ no_sanitize_address,
+ no_sanitize_hwaddress,
+ //sanitize_memtag,
+ sanitize_address_dyninit,
+
+ string: struct { kind: String, value: String },
+ none: noreturn,
+
+ pub const Index = enum(u32) {
+ _,
+
+ pub fn getKind(self: Index, builder: *const Builder) Kind {
+ return self.toStorage(builder).kind;
+ }
+
+ pub fn toAttribute(self: Index, builder: *const Builder) Attribute {
+ @setEvalBranchQuota(2_000);
+ const storage = self.toStorage(builder);
+ if (storage.kind.toString()) |kind| return .{ .string = .{
+ .kind = kind,
+ .value = @enumFromInt(storage.value),
+ } } else return switch (storage.kind) {
+ inline .zeroext,
+ .signext,
+ .inreg,
+ .byval,
+ .byref,
+ .preallocated,
+ .inalloca,
+ .sret,
+ .elementtype,
+ .@"align",
+ .@"noalias",
+ .nocapture,
+ .nofree,
+ .nest,
+ .returned,
+ .nonnull,
+ .dereferenceable,
+ .dereferenceable_or_null,
+ .swiftself,
+ .swiftasync,
+ .swifterror,
+ .immarg,
+ .noundef,
+ .nofpclass,
+ .alignstack,
+ .allocalign,
+ .allocptr,
+ .readnone,
+ .readonly,
+ .writeonly,
+ //.alignstack,
+ .allockind,
+ .allocsize,
+ .alwaysinline,
+ .builtin,
+ .cold,
+ .convergent,
+ .disable_sanitizer_information,
+ .fn_ret_thunk_extern,
+ .hot,
+ .inlinehint,
+ .jumptable,
+ .memory,
+ .minsize,
+ .naked,
+ .nobuiltin,
+ .nocallback,
+ .noduplicate,
+ //.nofree,
+ .noimplicitfloat,
+ .@"noinline",
+ .nomerge,
+ .nonlazybind,
+ .noprofile,
+ .skipprofile,
+ .noredzone,
+ .noreturn,
+ .norecurse,
+ .willreturn,
+ .nosync,
+ .nounwind,
+ .nosanitize_bounds,
+ .nosanitize_coverage,
+ .null_pointer_is_valid,
+ .optforfuzzing,
+ .optnone,
+ .optsize,
+ //.preallocated,
+ .returns_twice,
+ .safestack,
+ .sanitize_address,
+ .sanitize_memory,
+ .sanitize_thread,
+ .sanitize_hwaddress,
+ .sanitize_memtag,
+ .speculative_load_hardening,
+ .speculatable,
+ .ssp,
+ .sspstrong,
+ .sspreq,
+ .strictfp,
+ .uwtable,
+ .nocf_check,
+ .shadowcallstack,
+ .mustprogress,
+ .vscale_range,
+ .no_sanitize_address,
+ .no_sanitize_hwaddress,
+ .sanitize_address_dyninit,
+ => |kind| {
+ const field = @typeInfo(Attribute).Union.fields[@intFromEnum(kind)];
+ comptime assert(std.mem.eql(u8, @tagName(kind), field.name));
+ return @unionInit(Attribute, field.name, switch (field.type) {
+ void => {},
+ u32 => storage.value,
+ Alignment, String, Type, UwTable => @enumFromInt(storage.value),
+ AllocKind, AllocSize, FpClass, Memory, VScaleRange => @bitCast(storage.value),
+ else => @compileError("bad payload type: " ++ @typeName(field.type)),
+ });
+ },
+ .string, .none => unreachable,
+ _ => unreachable,
+ };
+ }
+
+ const FormatData = struct {
+ attribute_index: Index,
+ builder: *const Builder,
+ };
+ fn format(
+ data: FormatData,
+ comptime fmt_str: []const u8,
+ _: std.fmt.FormatOptions,
+ writer: anytype,
+ ) @TypeOf(writer).Error!void {
+ if (comptime std.mem.indexOfNone(u8, fmt_str, "\"")) |_|
+ @compileError("invalid format string: '" ++ fmt_str ++ "'");
+ const attribute = data.attribute_index.toAttribute(data.builder);
+ switch (attribute) {
+ .zeroext,
+ .signext,
+ .inreg,
+ .@"noalias",
+ .nocapture,
+ .nofree,
+ .nest,
+ .returned,
+ .nonnull,
+ .swiftself,
+ .swiftasync,
+ .swifterror,
+ .immarg,
+ .noundef,
+ .allocalign,
+ .allocptr,
+ .readnone,
+ .readonly,
+ .writeonly,
+ .alwaysinline,
+ .builtin,
+ .cold,
+ .convergent,
+ .disable_sanitizer_information,
+ .fn_ret_thunk_extern,
+ .hot,
+ .inlinehint,
+ .jumptable,
+ .minsize,
+ .naked,
+ .nobuiltin,
+ .nocallback,
+ .noduplicate,
+ .noimplicitfloat,
+ .@"noinline",
+ .nomerge,
+ .nonlazybind,
+ .noprofile,
+ .skipprofile,
+ .noredzone,
+ .noreturn,
+ .norecurse,
+ .willreturn,
+ .nosync,
+ .nounwind,
+ .nosanitize_bounds,
+ .nosanitize_coverage,
+ .null_pointer_is_valid,
+ .optforfuzzing,
+ .optnone,
+ .optsize,
+ .returns_twice,
+ .safestack,
+ .sanitize_address,
+ .sanitize_memory,
+ .sanitize_thread,
+ .sanitize_hwaddress,
+ .sanitize_memtag,
+ .speculative_load_hardening,
+ .speculatable,
+ .ssp,
+ .sspstrong,
+ .sspreq,
+ .strictfp,
+ .nocf_check,
+ .shadowcallstack,
+ .mustprogress,
+ .no_sanitize_address,
+ .no_sanitize_hwaddress,
+ .sanitize_address_dyninit,
+ => try writer.print(" {s}", .{@tagName(attribute)}),
+ .byval,
+ .byref,
+ .preallocated,
+ .inalloca,
+ .sret,
+ .elementtype,
+ => |ty| try writer.print(" {s}({%})", .{ @tagName(attribute), ty.fmt(data.builder) }),
+ .@"align" => @panic("todo"),
+ .dereferenceable,
+ .dereferenceable_or_null,
+ => @panic("todo"),
+ .nofpclass => @panic("todo"),
+ .alignstack => @panic("todo"),
+ .allockind => @panic("todo"),
+ .allocsize => @panic("todo"),
+ .memory => @panic("todo"),
+ .uwtable => @panic("todo"),
+ .vscale_range => @panic("todo"),
+ .string => |string_attr| if (comptime std.mem.indexOfScalar(u8, fmt_str, '"') != null) {
+ try writer.print(" {\"}", .{string_attr.kind.fmt(data.builder)});
+ if (string_attr.value != .empty)
+ try writer.print("={\"}", .{string_attr.value.fmt(data.builder)});
+ },
+ .none => unreachable,
+ }
+ }
+ pub fn fmt(self: Index, builder: *const Builder) std.fmt.Formatter(format) {
+ return .{ .data = .{ .attribute_index = self, .builder = builder } };
+ }
+
+ fn toStorage(self: Index, builder: *const Builder) Storage {
+ return builder.attributes.keys()[@intFromEnum(self)];
+ }
+
+ fn toLlvm(self: Index, builder: *const Builder) *llvm.Attribute {
+ assert(builder.useLibLlvm());
+ return builder.llvm.attributes.items[@intFromEnum(self)];
+ }
+ };
+
+ pub const Kind = enum(u32) {
+ // Parameter Attributes
+ zeroext,
+ signext,
+ inreg,
+ byval,
+ byref,
+ preallocated,
+ inalloca,
+ sret,
+ elementtype,
+ @"align",
+ @"noalias",
+ nocapture,
+ nofree,
+ nest,
+ returned,
+ nonnull,
+ dereferenceable,
+ dereferenceable_or_null,
+ swiftself,
+ swiftasync,
+ swifterror,
+ immarg,
+ noundef,
+ nofpclass,
+ alignstack,
+ allocalign,
+ allocptr,
+ readnone,
+ readonly,
+ writeonly,
+
+ // Function Attributes
+ //alignstack,
+ allockind,
+ allocsize,
+ alwaysinline,
+ builtin,
+ cold,
+ convergent,
+ disable_sanitizer_information,
+ fn_ret_thunk_extern,
+ hot,
+ inlinehint,
+ jumptable,
+ memory,
+ minsize,
+ naked,
+ nobuiltin,
+ nocallback,
+ noduplicate,
+ //nofree,
+ noimplicitfloat,
+ @"noinline",
+ nomerge,
+ nonlazybind,
+ noprofile,
+ skipprofile,
+ noredzone,
+ noreturn,
+ norecurse,
+ willreturn,
+ nosync,
+ nounwind,
+ nosanitize_bounds,
+ nosanitize_coverage,
+ null_pointer_is_valid,
+ optforfuzzing,
+ optnone,
+ optsize,
+ //preallocated,
+ returns_twice,
+ safestack,
+ sanitize_address,
+ sanitize_memory,
+ sanitize_thread,
+ sanitize_hwaddress,
+ sanitize_memtag,
+ speculative_load_hardening,
+ speculatable,
+ ssp,
+ sspstrong,
+ sspreq,
+ strictfp,
+ uwtable,
+ nocf_check,
+ shadowcallstack,
+ mustprogress,
+ vscale_range,
+
+ // Global Attributes
+ no_sanitize_address,
+ no_sanitize_hwaddress,
+ //sanitize_memtag,
+ sanitize_address_dyninit,
+
+ string = std.math.maxInt(u31) - 1,
+ none = std.math.maxInt(u31),
+ _,
+
+ pub const len = @typeInfo(Kind).Enum.fields.len - 2;
+
+ pub fn fromString(str: String) Kind {
+ assert(!str.isAnon());
+ return @enumFromInt(@intFromEnum(str));
+ }
+
+ fn toString(self: Kind) ?String {
+ const str: String = @enumFromInt(@intFromEnum(self));
+ return if (str.isAnon()) null else str;
+ }
+ };
+
+ pub const FpClass = packed struct(u32) {
+ signaling_nan: bool = false,
+ quiet_nan: bool = false,
+ negative_infinity: bool = false,
+ negative_normal: bool = false,
+ negative_subnormal: bool = false,
+ negative_zero: bool = false,
+ positive_zero: bool = false,
+ positive_subnormal: bool = false,
+ positive_normal: bool = false,
+ positive_infinity: bool = false,
+ _: u22 = 0,
+
+ pub const nan = FpClass{ .signaling_nan = true, .quiet_nan = true };
+ pub const inf = FpClass{ .negative_infinity = true, .positive_infinity = true };
+ pub const norm = FpClass{ .positive_normal = true, .negative_normal = true };
+ pub const sub = FpClass{ .positive_subnormal = true, .negative_subnormal = true };
+ pub const zero = FpClass{ .positive_zero = true, .negative_zero = true };
+ pub const all = FpClass{
+ .signaling_nan = true,
+ .quiet_nan = true,
+ .negative_infinity = true,
+ .negative_normal = true,
+ .negative_subnormal = true,
+ .negative_zero = true,
+ .positive_zero = true,
+ .positive_subnormal = true,
+ .positive_normal = true,
+ .positive_infinity = true,
+ };
+ pub const snan = FpClass{ .signaling_nan = true };
+ pub const qnan = FpClass{ .quiet_nan = true };
+ pub const ninf = FpClass{ .negative_infinity = true };
+ pub const nnorm = FpClass{ .negative_normal = true };
+ pub const nsub = FpClass{ .negative_subnormal = true };
+ pub const nzero = FpClass{ .negative_zero = true };
+ pub const pzero = FpClass{ .positive_zero = true };
+ pub const psub = FpClass{ .positive_subnormal = true };
+ pub const pnorm = FpClass{ .positive_normal = true };
+ pub const pinf = FpClass{ .positive_infinity = true };
+ };
+
+ pub const AllocKind = packed struct(u32) {
+ alloc: bool,
+ realloc: bool,
+ free: bool,
+ uninitialized: bool,
+ zeroed: bool,
+ aligned: bool,
+ _: u26 = 0,
+ };
+
+ pub const AllocSize = packed struct(u32) {
+ elem_size: u16,
+ num_elems: u16,
+
+ pub const none = std.math.maxInt(u16);
+
+ fn toLlvm(self: AllocSize) packed struct(u64) { num_elems: u32, elem_size: u32 } {
+ return .{ .num_elems = switch (self.num_elems) {
+ else => self.num_elems,
+ none => std.math.maxInt(u32),
+ }, .elem_size = self.elem_size };
+ }
+ };
+
+ pub const Memory = packed struct(u32) {
+ argmem: Effect,
+ inaccessiblemem: Effect,
+ other: Effect,
+ _: u26 = 0,
+
+ pub const Effect = enum(u2) { none, read, write, readwrite };
+ };
+
+ pub const UwTable = enum(u32) {
+ none,
+ sync,
+ @"async",
+
+ pub const default = UwTable.@"async";
+ };
+
+ pub const VScaleRange = packed struct(u32) {
+ min: Alignment,
+ max: Alignment,
+ _: u20 = 0,
+
+ fn toLlvm(self: VScaleRange) packed struct(u64) { max: u32, min: u32 } {
+ return .{
+ .max = @intCast(self.max.toByteUnits() orelse 0),
+ .min = @intCast(self.min.toByteUnits().?),
+ };
+ }
+ };
+
+ pub fn getKind(self: Attribute) Kind {
+ return switch (self) {
+ else => self,
+ .string => |string_attr| Kind.fromString(string_attr.kind),
+ };
+ }
+
+ const Storage = extern struct {
+ kind: Kind,
+ value: u32,
+ };
+
+ fn toStorage(self: Attribute) Storage {
+ return switch (self) {
+ inline else => |value| .{ .kind = @as(Kind, self), .value = switch (@TypeOf(value)) {
+ void => 0,
+ u32 => value,
+ Alignment, String, Type, UwTable => @intFromEnum(value),
+ AllocKind, AllocSize, FpClass, Memory, VScaleRange => @bitCast(value),
+ else => @compileError("bad payload type: " ++ @typeName(@TypeOf(value))),
+ } },
+ .string => |string_attr| .{
+ .kind = Kind.fromString(string_attr.kind),
+ .value = @intFromEnum(string_attr.value),
+ },
+ .none => unreachable,
+ };
+ }
+};
+
+pub const Attributes = enum(u32) {
+ none,
+ _,
+
+ pub fn slice(self: Attributes, builder: *const Builder) []const Attribute.Index {
+ const start = builder.attributes_indices.items[@intFromEnum(self)];
+ const end = builder.attributes_indices.items[@intFromEnum(self) + 1];
+ return @ptrCast(builder.attributes_extra.items[start..end]);
+ }
+
+ const FormatData = struct {
+ attributes: Attributes,
+ builder: *const Builder,
+ };
+ fn format(
+ data: FormatData,
+ comptime fmt_str: []const u8,
+ fmt_opts: std.fmt.FormatOptions,
+ writer: anytype,
+ ) @TypeOf(writer).Error!void {
+ for (data.attributes.slice(data.builder)) |attribute_index| try Attribute.Index.format(.{
+ .attribute_index = attribute_index,
+ .builder = data.builder,
+ }, fmt_str, fmt_opts, writer);
+ }
+ pub fn fmt(self: Attributes, builder: *const Builder) std.fmt.Formatter(format) {
+ return .{ .data = .{ .attributes = self, .builder = builder } };
+ }
+};
+
+pub const FunctionAttributes = enum(u32) {
+ none,
+ _,
+
+ const function_index = 0;
+ const return_index = 1;
+ const params_index = 2;
+
+ pub const Wip = struct {
+ maps: Maps = .{},
+
+ const Map = std.AutoArrayHashMapUnmanaged(Attribute.Kind, Attribute.Index);
+ const Maps = std.ArrayListUnmanaged(Map);
+
+ pub fn deinit(self: *Wip, builder: *const Builder) void {
+ for (self.maps.items) |*map| map.deinit(builder.gpa);
+ self.maps.deinit(builder.gpa);
+ self.* = undefined;
+ }
+
+ pub fn addFnAttr(self: *Wip, attribute: Attribute, builder: *Builder) Allocator.Error!void {
+ try self.addAttr(function_index, attribute, builder);
+ }
+
+ pub fn addFnAttrIndex(
+ self: *Wip,
+ attribute_index: Attribute.Index,
+ builder: *const Builder,
+ ) Allocator.Error!void {
+ try self.addAttrIndex(function_index, attribute_index, builder);
+ }
+
+ pub fn removeFnAttr(self: *Wip, attribute_kind: Attribute.Kind) Allocator.Error!bool {
+ return self.removeAttr(function_index, attribute_kind);
+ }
+
+ pub fn addRetAttr(self: *Wip, attribute: Attribute, builder: *Builder) Allocator.Error!void {
+ try self.addAttr(return_index, attribute, builder);
+ }
+
+ pub fn addRetAttrIndex(
+ self: *Wip,
+ attribute_index: Attribute.Index,
+ builder: *const Builder,
+ ) Allocator.Error!void {
+ try self.addAttrIndex(return_index, attribute_index, builder);
+ }
+
+ pub fn removeRetAttr(self: *Wip, attribute_kind: Attribute.Kind) Allocator.Error!bool {
+ return self.removeAttr(return_index, attribute_kind);
+ }
+
+ pub fn addParamAttr(
+ self: *Wip,
+ param_index: usize,
+ attribute: Attribute,
+ builder: *Builder,
+ ) Allocator.Error!void {
+ try self.addAttr(params_index + param_index, attribute, builder);
+ }
+
+ pub fn addParamAttrIndex(
+ self: *Wip,
+ param_index: usize,
+ attribute_index: Attribute.Index,
+ builder: *const Builder,
+ ) Allocator.Error!void {
+ try self.addAttrIndex(params_index + param_index, attribute_index, builder);
+ }
+
+ pub fn removeParamAttr(
+ self: *Wip,
+ param_index: usize,
+ attribute_kind: Attribute.Kind,
+ ) Allocator.Error!bool {
+ return self.removeAttr(params_index + param_index, attribute_kind);
+ }
+
+ pub fn finish(self: *const Wip, builder: *Builder) Allocator.Error!FunctionAttributes {
+ const attributes = try builder.gpa.alloc(Attributes, self.maps.items.len);
+ defer builder.gpa.free(attributes);
+ for (attributes, self.maps.items) |*attribute, map|
+ attribute.* = try builder.attrs(map.values());
+ return builder.fnAttrs(attributes);
+ }
+
+ fn addAttr(
+ self: *Wip,
+ index: usize,
+ attribute: Attribute,
+ builder: *Builder,
+ ) Allocator.Error!void {
+ const map = try self.getOrPutMap(builder.gpa, index);
+ try map.put(builder.gpa, attribute.getKind(), try builder.attr(attribute));
+ }
+
+ fn addAttrIndex(
+ self: *Wip,
+ index: usize,
+ attribute_index: Attribute.Index,
+ builder: *const Builder,
+ ) Allocator.Error!void {
+ const map = try self.getOrPutMap(builder.gpa, index);
+ try map.put(builder.gpa, attribute_index.getKind(builder), attribute_index);
+ }
+
+ fn removeAttr(self: *Wip, index: usize, attribute_kind: Attribute.Kind) Allocator.Error!bool {
+ const map = self.getMap(index) orelse return false;
+ return map.swapRemove(attribute_kind);
+ }
+
+ fn getOrPutMap(self: *Wip, allocator: Allocator, index: usize) Allocator.Error!*Map {
+ if (index >= self.maps.items.len)
+ try self.maps.appendNTimes(allocator, .{}, index + 1 - self.maps.items.len);
+ return &self.maps.items[index];
+ }
+
+ fn getMap(self: *Wip, index: usize) ?*Map {
+ return if (index >= self.maps.items.len) null else &self.maps.items[index];
+ }
+
+ fn ensureTotalLength(self: *Wip, new_len: usize) Allocator.Error!void {
+ try self.maps.appendNTimes(
+ .{},
+ std.math.sub(usize, new_len, self.maps.items.len) catch return,
+ );
+ }
+ };
+
+ pub fn func(self: FunctionAttributes, builder: *const Builder) Attributes {
+ return self.get(function_index, builder);
+ }
+
+ pub fn ret(self: FunctionAttributes, builder: *const Builder) Attributes {
+ return self.get(return_index, builder);
+ }
+
+ pub fn param(self: FunctionAttributes, param_index: usize, builder: *const Builder) Attributes {
+ return self.get(params_index + param_index, builder);
+ }
+
+ pub fn toWip(self: FunctionAttributes, builder: *const Builder) Allocator.Error!Wip {
+ var wip: Wip = .{};
+ errdefer wip.deinit(builder);
+ const attributes_slice = self.slice(builder);
+ try wip.maps.ensureTotalCapacityPrecise(builder.gpa, attributes_slice.len);
+ for (attributes_slice) |attributes| {
+ const map = wip.maps.addOneAssumeCapacity();
+ map.* = .{};
+ const attribute_slice = attributes.slice(builder);
+ try map.ensureTotalCapacity(builder.gpa, attribute_slice.len);
+ for (attributes.slice(builder)) |attribute|
+ map.putAssumeCapacityNoClobber(attribute.getKind(builder), attribute);
+ }
+ return wip;
+ }
+
+ fn get(self: FunctionAttributes, index: usize, builder: *const Builder) Attributes {
+ const attribute_slice = self.slice(builder);
+ return if (index < attribute_slice.len) attribute_slice[index] else .none;
+ }
+
+ fn slice(self: FunctionAttributes, builder: *const Builder) []const Attributes {
+ const start = builder.attributes_indices.items[@intFromEnum(self)];
+ const end = builder.attributes_indices.items[@intFromEnum(self) + 1];
+ return @ptrCast(builder.attributes_extra.items[start..end]);
+ }
+};
+
pub const Linkage = enum {
external,
private,
@@ -1053,6 +1856,127 @@ pub const Alignment = enum(u6) {
}
};
+pub const CallConv = enum(u10) {
+ ccc,
+
+ fastcc = 8,
+ coldcc,
+ ghccc,
+
+ webkit_jscc = 12,
+ anyregcc,
+ preserve_mostcc,
+ preserve_allcc,
+ swiftcc,
+ cxx_fast_tlscc,
+ tailcc,
+ cfguard_checkcc,
+ swifttailcc,
+
+ x86_stdcallcc = 64,
+ x86_fastcallcc,
+ arm_apcscc,
+ arm_aapcscc,
+ arm_aapcs_vfpcc,
+ msp430_intrcc,
+ x86_thiscallcc,
+ ptx_kernel,
+ ptx_device,
+
+ spir_func = 75,
+ spir_kernel,
+ intel_ocl_bicc,
+ x86_64_sysvcc,
+ win64cc,
+ x86_vectorcallcc,
+ hhvmcc,
+ hhvm_ccc,
+ x86_intrcc,
+ avr_intrcc,
+ avr_signalcc,
+
+ amdgpu_vs = 87,
+ amdgpu_gs,
+ amdgpu_ps,
+ amdgpu_cs,
+ amdgpu_kernel,
+ x86_regcallcc,
+ amdgpu_hs,
+
+ amdgpu_ls = 95,
+ amdgpu_es,
+ aarch64_vector_pcs,
+ aarch64_sve_vector_pcs,
+
+ amdgpu_gfx = 100,
+
+ aarch64_sme_preservemost_from_x0 = 102,
+ aarch64_sme_preservemost_from_x2,
+
+ _,
+
+ pub const default = CallConv.ccc;
+
+ pub fn format(
+ self: CallConv,
+ comptime _: []const u8,
+ _: std.fmt.FormatOptions,
+ writer: anytype,
+ ) @TypeOf(writer).Error!void {
+ switch (self) {
+ .ccc => {},
+ .fastcc,
+ .coldcc,
+ .ghccc,
+ .webkit_jscc,
+ .anyregcc,
+ .preserve_mostcc,
+ .preserve_allcc,
+ .swiftcc,
+ .cxx_fast_tlscc,
+ .tailcc,
+ .cfguard_checkcc,
+ .swifttailcc,
+ .x86_stdcallcc,
+ .x86_fastcallcc,
+ .arm_apcscc,
+ .arm_aapcscc,
+ .arm_aapcs_vfpcc,
+ .msp430_intrcc,
+ .x86_thiscallcc,
+ .ptx_kernel,
+ .ptx_device,
+ .spir_func,
+ .spir_kernel,
+ .intel_ocl_bicc,
+ .x86_64_sysvcc,
+ .win64cc,
+ .x86_vectorcallcc,
+ .hhvmcc,
+ .hhvm_ccc,
+ .x86_intrcc,
+ .avr_intrcc,
+ .avr_signalcc,
+ .amdgpu_vs,
+ .amdgpu_gs,
+ .amdgpu_ps,
+ .amdgpu_cs,
+ .amdgpu_kernel,
+ .x86_regcallcc,
+ .amdgpu_hs,
+ .amdgpu_ls,
+ .amdgpu_es,
+ .aarch64_vector_pcs,
+ .aarch64_sve_vector_pcs,
+ .amdgpu_gfx,
+ .aarch64_sme_preservemost_from_x0,
+ .aarch64_sme_preservemost_from_x2,
+ => try writer.print(" {s}", .{@tagName(self)}),
+ _ => try writer.print(" cc{d}", .{@intFromEnum(self)}),
+ }
+ }
+};
+
pub const Global = struct {
linkage: Linkage = .external,
preemption: Preemption = .dso_preemptable,
@@ -1170,7 +2094,7 @@ pub const Global = struct {
fn updateName(self: Index, builder: *const Builder) void {
if (!builder.useLibLlvm()) return;
const index = @intFromEnum(self.unwrap(builder));
- const name_slice = self.name(builder).toSlice(builder) orelse "";
+ const name_slice = self.name(builder).slice(builder) orelse "";
builder.llvm.globals.items[index].setValueName2(name_slice.ptr, name_slice.len);
}
@@ -1301,6 +2225,8 @@ pub const Variable = struct {
pub const Function = struct {
global: Global.Index,
+ call_conv: CallConv = CallConv.default,
+ attributes: FunctionAttributes = .none,
section: String = .none,
alignment: Alignment = .default,
blocks: []const Block = &.{},
@@ -1364,6 +2290,8 @@ pub const Function = struct {
block,
br,
br_cond,
+ call,
+ @"call fast",
extractelement,
extractvalue,
fadd,
@@ -1454,6 +2382,10 @@ pub const Function = struct {
@"mul nsw",
@"mul nuw",
@"mul nuw nsw",
+ @"musttail call",
+ @"musttail call fast",
+ @"notail call",
+ @"notail call fast",
@"or",
phi,
@"phi fast",
@@ -1481,6 +2413,8 @@ pub const Function = struct {
@"sub nuw",
@"sub nuw nsw",
@"switch",
+ @"tail call",
+ @"tail call fast",
trunc,
udiv,
@"udiv exact",
@@ -1530,6 +2464,15 @@ pub const Function = struct {
.@"store volatile",
.@"unreachable",
=> false,
+ .call,
+ .@"call fast",
+ .@"musttail call",
+ .@"musttail call fast",
+ .@"notail call",
+ .@"notail call fast",
+ .@"tail call",
+ .@"tail call fast",
+ => self.typeOfWip(wip) != .void,
else => true,
};
}
@@ -1625,6 +2568,15 @@ pub const Function = struct {
.@"switch",
.@"unreachable",
=> .none,
+ .call,
+ .@"call fast",
+ .@"musttail call",
+ .@"musttail call fast",
+ .@"notail call",
+ .@"notail call fast",
+ .@"tail call",
+ .@"tail call fast",
+ => wip.extraData(Call, instruction.data).ty.functionReturn(wip.builder),
.extractelement => wip.extraData(ExtractElement, instruction.data)
.val.typeOfWip(wip).childType(wip.builder),
.extractvalue => {
@@ -1813,6 +2765,15 @@ pub const Function = struct {
.@"switch",
.@"unreachable",
=> .none,
+ .call,
+ .@"call fast",
+ .@"musttail call",
+ .@"musttail call fast",
+ .@"notail call",
+ .@"notail call fast",
+ .@"tail call",
+ .@"tail call fast",
+ => function.extraData(Call, instruction.data).ty.functionReturn(builder),
.extractelement => function.extraData(ExtractElement, instruction.data)
.val.typeOf(function_index, builder).childType(builder),
.extractvalue => {
@@ -1955,7 +2916,7 @@ pub const Function = struct {
return if (wip.builder.strip)
""
else
- wip.names.items[@intFromEnum(self)].toSlice(wip.builder).?;
+ wip.names.items[@intFromEnum(self)].slice(wip.builder).?;
}
};
@@ -2063,6 +3024,30 @@ pub const Function = struct {
rhs: Value,
};
+ pub const Call = struct {
+ info: Info,
+ attributes: FunctionAttributes,
+ ty: Type,
+ callee: Value,
+ args_len: u32,
+ //args: [args_len]Value,
+
+ pub const Kind = enum {
+ normal,
+ fast,
+ musttail,
+ musttail_fast,
+ notail,
+ notail_fast,
+ tail,
+ tail_fast,
+ };
+ pub const Info = packed struct(u32) {
+ call_conv: CallConv,
+ _: u22 = undefined,
+ };
+ };
+
pub const VaArg = struct {
list: Value,
type: Type,
@@ -2117,8 +3102,17 @@ pub const Function = struct {
inline for (fields, self.extra[index..][0..fields.len]) |field, value|
@field(result, field.name) = switch (field.type) {
u32 => value,
- Alignment, AtomicOrdering, Block.Index, Type, Value => @enumFromInt(value),
- MemoryAccessInfo, Instruction.Alloca.Info => @bitCast(value),
+ Alignment,
+ AtomicOrdering,
+ Block.Index,
+ FunctionAttributes,
+ Type,
+ Value,
+ => @enumFromInt(value),
+ MemoryAccessInfo,
+ Instruction.Alloca.Info,
+ Instruction.Call.Info,
+ => @bitCast(value),
else => @compileError("bad field type: " ++ @typeName(field.type)),
};
return .{
@@ -2243,7 +3237,7 @@ pub const WipFunction = struct {
if (self.builder.useLibLlvm()) self.llvm.blocks.appendAssumeCapacity(
self.builder.llvm.context.appendBasicBlock(
self.function.toLlvm(self.builder),
- final_name.toSlice(self.builder).?,
+ final_name.slice(self.builder).?,
),
);
return index;
@@ -3162,6 +4156,88 @@ pub const WipFunction = struct {
return self.selectTag(.@"select fast", cond, lhs, rhs, name);
}
+ pub fn call(
+ self: *WipFunction,
+ kind: Instruction.Call.Kind,
+ call_conv: CallConv,
+ function_attributes: FunctionAttributes,
+ ty: Type,
+ callee: Value,
+ args: []const Value,
+ name: []const u8,
+ ) if (build_options.have_llvm) Allocator.Error!Value else Value {
+ const ret_ty = ty.functionReturn(self.builder);
+ assert(ty.isFunction(self.builder));
+ assert(callee.typeOfWip(self).isPointer(self.builder));
+ const params = ty.functionParameters(self.builder);
+ for (params, args[0..params.len]) |param, arg_val| assert(param == arg_val.typeOfWip(self));
+
+ try self.ensureUnusedExtraCapacity(1, Instruction.Call, args.len);
+ const instruction = try self.addInst(switch (ret_ty) {
+ .void => null,
+ else => name,
+ }, .{
+ .tag = .call,
+ .data = self.addExtraAssumeCapacity(Instruction.Call{
+ .info = .{ .call_conv = call_conv },
+ .attributes = function_attributes,
+ .ty = ty,
+ .callee = callee,
+ .args_len = @intCast(args.len),
+ }),
+ });
+ self.extra.appendSliceAssumeCapacity(@ptrCast(args));
+ if (self.builder.useLibLlvm()) {
+ const ExpectedContents = [expected_args_len]*llvm.Value;
+ var stack align(@alignOf(ExpectedContents)) =
+ std.heap.stackFallback(@sizeOf(ExpectedContents), self.builder.gpa);
+ const allocator = stack.get();
+
+ const llvm_args = try allocator.alloc(*llvm.Value, args.len);
+ defer allocator.free(llvm_args);
+ for (llvm_args, args) |*llvm_arg, arg_val| llvm_arg.* = arg_val.toLlvm(self);
+
+ switch (kind) {
+ .normal,
+ .musttail,
+ .notail,
+ .tail,
+ => self.llvm.builder.setFastMath(false),
+ .fast,
+ .musttail_fast,
+ .notail_fast,
+ .tail_fast,
+ => self.llvm.builder.setFastMath(true),
+ }
+ const llvm_instruction = self.llvm.builder.buildCall(
+ ty.toLlvm(self.builder),
+ callee.toLlvm(self),
+ llvm_args.ptr,
+ @intCast(llvm_args.len),
+ switch (ret_ty) {
+ .void => "",
+ else => instruction.llvmName(self),
+ },
+ );
+ llvm_instruction.setInstructionCallConv(@enumFromInt(@intFromEnum(call_conv)));
+ llvm_instruction.setTailCallKind(switch (kind) {
+ .normal, .fast => .None,
+ .musttail, .musttail_fast => .MustTail,
+ .notail, .notail_fast => .NoTail,
+ .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,
+ attribute.toLlvm(self.builder),
+ );
+ }
+ self.llvm.instructions.appendAssumeCapacity(llvm_instruction);
+ }
+ return instruction.toValue();
+ }
+
pub fn vaArg(self: *WipFunction, list: Value, ty: Type, name: []const u8) Allocator.Error!Value {
try self.ensureUnusedExtraCapacity(1, Instruction.VaArg, 0);
const instruction = try self.addInst(name, .{
@@ -3246,8 +4322,17 @@ pub const WipFunction = struct {
const value = @field(extra, field.name);
wip_extra.items[wip_extra.index] = switch (field.type) {
u32 => value,
- Alignment, AtomicOrdering, Block.Index, Type, Value => @intFromEnum(value),
- MemoryAccessInfo, Instruction.Alloca.Info => @bitCast(value),
+ Alignment,
+ AtomicOrdering,
+ Block.Index,
+ FunctionAttributes,
+ Type,
+ Value,
+ => @intFromEnum(value),
+ MemoryAccessInfo,
+ Instruction.Alloca.Info,
+ Instruction.Call.Info,
+ => @bitCast(value),
else => @compileError("bad field type: " ++ @typeName(field.type)),
};
wip_extra.index += 1;
@@ -3256,13 +4341,14 @@ pub const WipFunction = struct {
}
fn appendSlice(wip_extra: *@This(), slice: anytype) void {
- if (@typeInfo(@TypeOf(slice)).Pointer.child == Value) @compileError("use appendValues");
+ if (@typeInfo(@TypeOf(slice)).Pointer.child == Value)
+ @compileError("use appendMappedValues");
const data: []const u32 = @ptrCast(slice);
@memcpy(wip_extra.items[wip_extra.index..][0..data.len], data);
wip_extra.index += @intCast(data.len);
}
- fn appendValues(wip_extra: *@This(), vals: []const Value, ctx: anytype) void {
+ fn appendMappedValues(wip_extra: *@This(), vals: []const Value, ctx: anytype) void {
for (wip_extra.items[wip_extra.index..][0..vals.len], vals) |*extra, val|
extra.* = @intFromEnum(ctx.map(val));
wip_extra.index += @intCast(vals.len);
@@ -3494,6 +4580,26 @@ pub const WipFunction = struct {
.@"else" = extra.@"else",
});
},
+ .call,
+ .@"call fast",
+ .@"musttail call",
+ .@"musttail call fast",
+ .@"notail call",
+ .@"notail call fast",
+ .@"tail call",
+ .@"tail call fast",
+ => {
+ var extra = self.extraDataTrail(Instruction.Call, instruction.data);
+ const args = extra.trail.next(extra.data.args_len, Value, self);
+ instruction.data = wip_extra.addExtra(Instruction.Call{
+ .info = extra.data.info,
+ .attributes = extra.data.attributes,
+ .ty = extra.data.ty,
+ .callee = instructions.map(extra.data.callee),
+ .args_len = extra.data.args_len,
+ });
+ wip_extra.appendMappedValues(args, instructions);
+ },
.extractvalue => {
var extra = self.extraDataTrail(Instruction.ExtractValue, instruction.data);
const indices = extra.trail.next(extra.data.indices_len, u32, self);
@@ -3517,7 +4623,7 @@ pub const WipFunction = struct {
.base = instructions.map(extra.data.base),
.indices_len = extra.data.indices_len,
});
- wip_extra.appendValues(indices, instructions);
+ wip_extra.appendMappedValues(indices, instructions);
},
.insertelement => {
const extra = self.extraData(Instruction.InsertElement, instruction.data);
@@ -3559,7 +4665,7 @@ pub const WipFunction = struct {
instruction.data = wip_extra.addExtra(Instruction.Phi{
.type = extra.data.type,
});
- wip_extra.appendValues(incoming_vals, instructions);
+ wip_extra.appendMappedValues(incoming_vals, instructions);
wip_extra.appendSlice(incoming_blocks);
},
.select,
@@ -3932,8 +5038,17 @@ pub const WipFunction = struct {
const value = @field(extra, field.name);
self.extra.appendAssumeCapacity(switch (field.type) {
u32 => value,
- Alignment, AtomicOrdering, Block.Index, Type, Value => @intFromEnum(value),
- MemoryAccessInfo, Instruction.Alloca.Info => @bitCast(value),
+ Alignment,
+ AtomicOrdering,
+ Block.Index,
+ FunctionAttributes,
+ Type,
+ Value,
+ => @intFromEnum(value),
+ MemoryAccessInfo,
+ Instruction.Alloca.Info,
+ Instruction.Call.Info,
+ => @bitCast(value),
else => @compileError("bad field type: " ++ @typeName(field.type)),
});
}
@@ -3971,8 +5086,17 @@ pub const WipFunction = struct {
inline for (fields, self.extra.items[index..][0..fields.len]) |field, value|
@field(result, field.name) = switch (field.type) {
u32 => value,
- Alignment, AtomicOrdering, Block.Index, Type, Value => @enumFromInt(value),
- MemoryAccessInfo, Instruction.Alloca.Info => @bitCast(value),
+ Alignment,
+ AtomicOrdering,
+ Block.Index,
+ FunctionAttributes,
+ Type,
+ Value,
+ => @enumFromInt(value),
+ MemoryAccessInfo,
+ Instruction.Alloca.Info,
+ Instruction.Call.Info,
+ => @bitCast(value),
else => @compileError("bad field type: " ++ @typeName(field.type)),
};
return .{
@@ -4294,7 +5418,7 @@ pub const Constant = enum(u32) {
.string,
.string_null,
=> builder.arrayTypeAssumeCapacity(
- @as(String, @enumFromInt(item.data)).toSlice(builder).?.len +
+ @as(String, @enumFromInt(item.data)).slice(builder).?.len +
@intFromBool(item.tag == .string_null),
.i8,
),
@@ -4821,8 +5945,8 @@ pub fn init(options: Options) InitError!Builder {
.target_triple = .none,
.string_map = .{},
- .string_bytes = .{},
.string_indices = .{},
+ .string_bytes = .{},
.types = .{},
.next_unnamed_type = @enumFromInt(0),
@@ -4831,6 +5955,11 @@ pub fn init(options: Options) InitError!Builder {
.type_items = .{},
.type_extra = .{},
+ .attributes = .{},
+ .attributes_map = .{},
+ .attributes_indices = .{},
+ .attributes_extra = .{},
+
.globals = .{},
.next_unnamed_global = @enumFromInt(0),
.next_replaced_global = .none,
@@ -4844,7 +5973,18 @@ pub fn init(options: Options) InitError!Builder {
.constant_extra = .{},
.constant_limbs = .{},
};
- if (self.useLibLlvm()) self.llvm = .{ .context = llvm.Context.create() };
+ if (self.useLibLlvm()) self.llvm = .{
+ .context = llvm.Context.create(),
+ .module = null,
+ .target = null,
+ .di_builder = null,
+ .di_compile_unit = null,
+ .attribute_kind_ids = null,
+ .attributes = .{},
+ .types = .{},
+ .globals = .{},
+ .constants = .{},
+ };
errdefer self.deinit();
try self.string_indices.append(self.gpa, 0);
@@ -4853,7 +5993,7 @@ pub fn init(options: Options) InitError!Builder {
if (options.name.len > 0) self.source_filename = try self.string(options.name);
self.initializeLLVMTarget(options.target.cpu.arch);
if (self.useLibLlvm()) self.llvm.module = llvm.Module.createWithName(
- (self.source_filename.toSlice(&self) orelse "").ptr,
+ (self.source_filename.slice(&self) orelse "").ptr,
self.llvm.context,
);
@@ -4864,20 +6004,20 @@ pub fn init(options: Options) InitError!Builder {
var error_message: [*:0]const u8 = undefined;
var target: *llvm.Target = undefined;
if (llvm.Target.getFromTriple(
- self.target_triple.toSlice(&self).?,
+ self.target_triple.slice(&self).?,
&target,
&error_message,
).toBool()) {
defer llvm.disposeMessage(error_message);
log.err("LLVM failed to parse '{s}': {s}", .{
- self.target_triple.toSlice(&self).?,
+ self.target_triple.slice(&self).?,
error_message,
});
return InitError.InvalidLlvmTriple;
}
self.llvm.target = target;
- self.llvm.module.?.setTarget(self.target_triple.toSlice(&self).?);
+ self.llvm.module.?.setTarget(self.target_triple.slice(&self).?);
}
}
@@ -4902,6 +6042,16 @@ pub fn init(options: Options) InitError!Builder {
assert(self.ptrTypeAssumeCapacity(@enumFromInt(addr_space)) == .ptr);
}
+ {
+ if (self.useLibLlvm()) {
+ self.llvm.attribute_kind_ids = try self.gpa.create([Attribute.Kind.len]c_uint);
+ @memset(self.llvm.attribute_kind_ids.?, 0);
+ }
+ try self.attributes_indices.append(self.gpa, 0);
+ assert(try self.attrs(&.{}) == .none);
+ assert(try self.fnAttrs(&.{}) == .none);
+ }
+
assert(try self.intConst(.i1, 0) == .false);
assert(try self.intConst(.i1, 1) == .true);
assert(try self.noneConst(.token) == .none);
@@ -4911,8 +6061,8 @@ pub fn init(options: Options) InitError!Builder {
pub fn deinit(self: *Builder) void {
self.string_map.deinit(self.gpa);
- self.string_bytes.deinit(self.gpa);
self.string_indices.deinit(self.gpa);
+ self.string_bytes.deinit(self.gpa);
self.types.deinit(self.gpa);
self.next_unique_type_id.deinit(self.gpa);
@@ -4920,6 +6070,11 @@ pub fn deinit(self: *Builder) void {
self.type_items.deinit(self.gpa);
self.type_extra.deinit(self.gpa);
+ self.attributes.deinit(self.gpa);
+ self.attributes_map.deinit(self.gpa);
+ self.attributes_indices.deinit(self.gpa);
+ self.attributes_extra.deinit(self.gpa);
+
self.globals.deinit(self.gpa);
self.next_unique_global_id.deinit(self.gpa);
self.aliases.deinit(self.gpa);
@@ -4936,6 +6091,8 @@ pub fn deinit(self: *Builder) void {
self.llvm.constants.deinit(self.gpa);
self.llvm.globals.deinit(self.gpa);
self.llvm.types.deinit(self.gpa);
+ self.llvm.attributes.deinit(self.gpa);
+ if (self.llvm.attribute_kind_ids) |attribute_kind_ids| self.gpa.destroy(attribute_kind_ids);
if (self.llvm.di_builder) |di_builder| di_builder.dispose();
if (self.llvm.module) |module| module.dispose();
self.llvm.context.dispose();
@@ -5230,7 +6387,7 @@ pub fn structType(
pub fn opaqueType(self: *Builder, name: String) Allocator.Error!Type {
try self.string_map.ensureUnusedCapacity(self.gpa, 1);
- if (name.toSlice(self)) |id| {
+ if (name.slice(self)) |id| {
const count: usize = comptime std.fmt.count("{d}" ++ .{0}, .{std.math.maxInt(u32)});
try self.string_bytes.ensureUnusedCapacity(self.gpa, id.len + count);
}
@@ -5268,6 +6425,99 @@ pub fn namedTypeSetBody(
}
}
+pub fn attr(self: *Builder, attribute: Attribute) Allocator.Error!Attribute.Index {
+ try self.attributes.ensureUnusedCapacity(self.gpa, 1);
+ if (self.useLibLlvm()) try self.llvm.attributes.ensureUnusedCapacity(self.gpa, 1);
+
+ const gop = self.attributes.getOrPutAssumeCapacity(attribute.toStorage());
+ if (!gop.found_existing) {
+ 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 name = @tagName(attribute);
+ kind_id.* = llvm.getEnumAttributeKindForName(name.ptr, name.len);
+ assert(kind_id.* != 0);
+ }
+ break :llvm_attr switch (attribute) {
+ else => switch (attribute) {
+ inline else => |value| self.llvm.context.createEnumAttribute(
+ kind_id.*,
+ switch (@TypeOf(value)) {
+ void => 0,
+ u32 => value,
+ Attribute.FpClass,
+ Attribute.AllocKind,
+ Attribute.Memory,
+ => @as(u32, @bitCast(value)),
+ Alignment => value.toByteUnits() orelse 0,
+ Attribute.AllocSize,
+ Attribute.VScaleRange,
+ => @bitCast(value.toLlvm()),
+ Attribute.UwTable => @intFromEnum(value),
+ else => @compileError(
+ "bad payload type: " ++ @typeName(@TypeOf(value)),
+ ),
+ },
+ ),
+ .byval,
+ .byref,
+ .preallocated,
+ .inalloca,
+ .sret,
+ .elementtype,
+ .string,
+ .none,
+ => unreachable,
+ },
+ .byval,
+ .byref,
+ .preallocated,
+ .inalloca,
+ .sret,
+ .elementtype,
+ => |ty| self.llvm.context.createTypeAttribute(kind_id.*, ty.toLlvm(self)),
+ .string, .none => unreachable,
+ };
+ },
+ .string => |string_attr| llvm_attr: {
+ const kind = string_attr.kind.slice(self).?;
+ const value = string_attr.value.slice(self).?;
+ break :llvm_attr self.llvm.context.createStringAttribute(
+ kind.ptr,
+ @intCast(kind.len),
+ value.ptr,
+ @intCast(value.len),
+ );
+ },
+ .none => unreachable,
+ });
+ }
+ return @enumFromInt(gop.index);
+}
+
+pub fn attrs(self: *Builder, attributes: []Attribute.Index) Allocator.Error!Attributes {
+ std.sort.heap(Attribute.Index, attributes, self, struct {
+ pub fn lessThan(builder: *const Builder, lhs: Attribute.Index, rhs: Attribute.Index) bool {
+ const lhs_kind = lhs.getKind(builder);
+ const rhs_kind = rhs.getKind(builder);
+ assert(lhs_kind != rhs_kind);
+ return @intFromEnum(lhs_kind) < @intFromEnum(rhs_kind);
+ }
+ }.lessThan);
+ return @enumFromInt(try self.attrGeneric(@ptrCast(attributes)));
+}
+
+pub fn fnAttrs(self: *Builder, fn_attributes: []const Attributes) Allocator.Error!FunctionAttributes {
+ return @enumFromInt(try self.attrGeneric(@ptrCast(
+ fn_attributes[0..if (std.mem.lastIndexOfNone(Attributes, fn_attributes, &.{.none})) |last|
+ last + 1
+ else
+ 0],
+ )));
+}
+
pub fn addGlobal(self: *Builder, name: String, global: Global) Allocator.Error!Global.Index {
assert(!name.isAnon());
try self.ensureUnusedTypeCapacity(1, NoExtra, 0);
@@ -5295,7 +6545,7 @@ pub fn addGlobalAssumeCapacity(self: *Builder, name: String, global: Global) Glo
const unique_gop = self.next_unique_global_id.getOrPutAssumeCapacity(name);
if (!unique_gop.found_existing) unique_gop.value_ptr.* = 2;
- id = self.fmtAssumeCapacity("{s}.{d}", .{ name.toSlice(self).?, unique_gop.value_ptr.* });
+ id = self.fmtAssumeCapacity("{s}.{d}", .{ name.slice(self).?, unique_gop.value_ptr.* });
unique_gop.value_ptr.* += 1;
}
}
@@ -5309,8 +6559,9 @@ pub fn intConst(self: *Builder, ty: Type, value: anytype) Allocator.Error!Consta
switch (@typeInfo(@TypeOf(value))) {
.Int => |info| std.math.big.int.calcTwosCompLimbCount(info.bits),
.ComptimeInt => std.math.big.int.calcLimbLen(value),
- else => @compileError("intConst expected an integral value, got " ++
- @typeName(@TypeOf(value))),
+ else => @compileError(
+ "intConst expected an integral value, got " ++ @typeName(@TypeOf(value)),
+ ),
}
]std.math.big.Limb = undefined;
return self.bigIntConst(ty, std.math.big.int.Mutable.init(&limbs, value).toConst());
@@ -5770,7 +7021,7 @@ pub fn printUnbuffered(
\\; ModuleID = '{s}'
\\source_filename = {"}
\\
- , .{ self.source_filename.toSlice(self).?, self.source_filename.fmt(self) });
+ , .{ self.source_filename.slice(self).?, self.source_filename.fmt(self) });
if (self.data_layout != .none) try writer.print(
\\target datalayout = {"}
\\
@@ -5780,11 +7031,13 @@ pub fn printUnbuffered(
\\
, .{self.target_triple.fmt(self)});
try writer.writeByte('\n');
+
for (self.types.keys(), self.types.values()) |id, ty| try writer.print(
\\%{} = type {}
\\
, .{ id.fmt(self), ty.fmt(self) });
try writer.writeByte('\n');
+
for (self.variables.items) |variable| {
if (variable.global.getReplacement(self) != .none) continue;
const global = variable.global.ptrConst(self);
@@ -5808,28 +7061,42 @@ pub fn printUnbuffered(
});
}
try writer.writeByte('\n');
+
+ var attribute_groups: std.AutoArrayHashMapUnmanaged(Attributes, void) = .{};
+ defer attribute_groups.deinit(self.gpa);
for (0.., self.functions.items) |function_i, function| {
const function_index: Function.Index = @enumFromInt(function_i);
if (function.global.getReplacement(self) != .none) continue;
const global = function.global.ptrConst(self);
const params_len = global.type.functionParameters(self).len;
+ const function_attributes = function.attributes.func(self);
+ if (function_attributes != .none) try writer.print(
+ \\; Function Attrs:{}
+ \\
+ , .{function_attributes.fmt(self)});
try writer.print(
- \\{s}{}{}{}{} {} {}(
+ \\{s}{}{}{}{}{}{"} {} {}(
, .{
if (function.instructions.len > 0) "define" else "declare",
global.linkage,
global.preemption,
global.visibility,
global.dll_storage_class,
+ function.call_conv,
+ function.attributes.ret(self).fmt(self),
global.type.functionReturn(self).fmt(self),
function.global.fmt(self),
});
for (0..params_len) |arg| {
if (arg > 0) try writer.writeAll(", ");
+ try writer.print(
+ \\{%}{"}
+ , .{
+ global.type.functionParameters(self)[arg].fmt(self),
+ function.attributes.param(arg, self).fmt(self),
+ });
if (function.instructions.len > 0)
- try writer.print("{%}", .{function.arg(@intCast(arg)).fmt(function_index, self)})
- else
- try writer.print("{%}", .{global.type.functionParameters(self)[arg].fmt(self)});
+ try writer.print(" {}", .{function.arg(@intCast(arg)).fmt(function_index, self)});
}
switch (global.type.functionKind(self)) {
.normal => {},
@@ -5838,7 +7105,11 @@ pub fn printUnbuffered(
try writer.writeAll("...");
},
}
- try writer.print("){}{}", .{ global.unnamed_addr, function.alignment });
+ try writer.print("){}{}", .{ global.unnamed_addr, global.addr_space });
+ if (function_attributes != .none) try writer.print(" #{d}", .{
+ (try attribute_groups.getOrPutValue(self.gpa, function_attributes, {})).index,
+ });
+ try writer.print("{}", .{function.alignment});
if (function.instructions.len > 0) {
var block_incoming_len: u32 = undefined;
try writer.writeAll(" {\n");
@@ -5992,6 +7263,48 @@ pub fn printUnbuffered(
extra.@"else".toInst(&function).fmt(function_index, self),
});
},
+ .call,
+ .@"call fast",
+ .@"musttail call",
+ .@"musttail call fast",
+ .@"notail call",
+ .@"notail call fast",
+ .@"tail call",
+ .@"tail call fast",
+ => |tag| {
+ var extra =
+ function.extraDataTrail(Function.Instruction.Call, instruction.data);
+ const args = extra.trail.next(extra.data.args_len, Value, &function);
+ try writer.writeAll(" ");
+ const ret_ty = extra.data.ty.functionReturn(self);
+ switch (ret_ty) {
+ .void => {},
+ else => try writer.print("%{} = ", .{
+ instruction_index.name(&function).fmt(self),
+ }),
+ .none => unreachable,
+ }
+ try writer.print("{s}{}{}{} {%} {}(", .{
+ @tagName(tag),
+ extra.data.info.call_conv,
+ extra.data.attributes.ret(self).fmt(self),
+ extra.data.callee.typeOf(function_index, self).pointerAddrSpace(self),
+ switch (extra.data.ty.functionKind(self)) {
+ .normal => ret_ty,
+ .vararg => extra.data.ty,
+ }.fmt(self),
+ extra.data.callee.fmt(function_index, self),
+ });
+ for (0.., args) |arg_index, arg| {
+ if (arg_index > 0) try writer.writeAll(", ");
+ try writer.print("{%}{} {}", .{
+ arg.typeOf(function_index, self).fmt(self),
+ extra.data.attributes.param(arg_index, self).fmt(self),
+ arg.fmt(function_index, self),
+ });
+ }
+ try writer.print("){}\n", .{extra.data.attributes.func(self).fmt(self)});
+ },
.extractelement => |tag| {
const extra =
function.extraData(Function.Instruction.ExtractElement, instruction.data);
@@ -6218,6 +7531,12 @@ pub fn printUnbuffered(
}
try writer.writeAll("\n\n");
}
+
+ for (0.., attribute_groups.keys()) |attribute_group_index, attribute_group|
+ try writer.print(
+ \\attribute #{d} = {{{"} }}
+ \\
+ , .{ attribute_group_index, attribute_group.fmt(self) });
}
pub inline fn useLibLlvm(self: *const Builder) bool {
@@ -6238,7 +7557,7 @@ fn isValidIdentifier(id: []const u8) bool {
fn ensureUnusedGlobalCapacity(self: *Builder, name: String) Allocator.Error!void {
if (self.useLibLlvm()) try self.llvm.globals.ensureUnusedCapacity(self.gpa, 1);
try self.string_map.ensureUnusedCapacity(self.gpa, 1);
- if (name.toSlice(self)) |id| {
+ if (name.slice(self)) |id| {
const count: usize = comptime std.fmt.count("{d}" ++ .{0}, .{std.math.maxInt(u32)});
try self.string_bytes.ensureUnusedCapacity(self.gpa, id.len + count);
}
@@ -6528,14 +7847,14 @@ fn opaqueTypeAssumeCapacity(self: *Builder, name: String) Type {
const result: Type = @enumFromInt(gop.index);
type_gop.value_ptr.* = result;
if (self.useLibLlvm()) self.llvm.types.appendAssumeCapacity(
- self.llvm.context.structCreateNamed(id.toSlice(self) orelse ""),
+ self.llvm.context.structCreateNamed(id.slice(self) orelse ""),
);
return result;
}
const unique_gop = self.next_unique_type_id.getOrPutAssumeCapacity(name);
if (!unique_gop.found_existing) unique_gop.value_ptr.* = 2;
- id = self.fmtAssumeCapacity("{s}.{d}", .{ name.toSlice(self).?, unique_gop.value_ptr.* });
+ id = self.fmtAssumeCapacity("{s}.{d}", .{ name.slice(self).?, unique_gop.value_ptr.* });
unique_gop.value_ptr.* += 1;
}
}
@@ -6636,6 +7955,30 @@ fn typeExtraData(self: *const Builder, comptime T: type, index: Type.Item.ExtraI
return self.typeExtraDataTrail(T, index).data;
}
+fn attrGeneric(self: *Builder, data: []const u32) Allocator.Error!u32 {
+ try self.attributes_map.ensureUnusedCapacity(self.gpa, 1);
+ try self.attributes_indices.ensureUnusedCapacity(self.gpa, 1);
+ try self.attributes_extra.ensureUnusedCapacity(self.gpa, data.len);
+
+ const Adapter = struct {
+ builder: *const Builder,
+ pub fn hash(_: @This(), key: []const u32) u32 {
+ return @truncate(std.hash.Wyhash.hash(1, std.mem.sliceAsBytes(key)));
+ }
+ pub fn eql(ctx: @This(), lhs_key: []const u32, _: void, rhs_index: usize) bool {
+ const start = ctx.builder.attributes_indices.items[rhs_index];
+ const end = ctx.builder.attributes_indices.items[rhs_index + 1];
+ return std.mem.eql(u32, lhs_key, ctx.builder.attributes_extra.items[start..end]);
+ }
+ };
+ const gop = self.attributes_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self });
+ if (!gop.found_existing) {
+ self.attributes_extra.appendSliceAssumeCapacity(data);
+ self.attributes_indices.appendAssumeCapacity(@intCast(self.attributes_extra.items.len));
+ }
+ return @intCast(gop.index);
+}
+
fn bigIntConstAssumeCapacity(
self: *Builder,
ty: Type,
@@ -7073,7 +8416,7 @@ fn arrayConstAssumeCapacity(
}
fn stringConstAssumeCapacity(self: *Builder, val: String) Constant {
- const slice = val.toSlice(self).?;
+ const slice = val.slice(self).?;
const ty = self.arrayTypeAssumeCapacity(slice.len, .i8);
if (std.mem.allEqual(u8, slice, 0)) return self.zeroInitConstAssumeCapacity(ty);
const result = self.getOrPutConstantNoExtraAssumeCapacity(
@@ -7086,7 +8429,7 @@ fn stringConstAssumeCapacity(self: *Builder, val: String) Constant {
}
fn stringNullConstAssumeCapacity(self: *Builder, val: String) Constant {
- const slice = val.toSlice(self).?;
+ const slice = val.slice(self).?;
const ty = self.arrayTypeAssumeCapacity(slice.len + 1, .i8);
if (std.mem.allEqual(u8, slice, 0)) return self.zeroInitConstAssumeCapacity(ty);
const result = self.getOrPutConstantNoExtraAssumeCapacity(
src/codegen/llvm.zig
@@ -359,7 +359,7 @@ const DataLayoutBuilder = struct {
.macho => 'o', // Mach-O mangling: Private symbols get `L` prefix.
// Other symbols get a `_` prefix.
.coff => switch (self.target.os.tag) {
- .windows => switch (self.target.cpu.arch) {
+ .uefi, .windows => switch (self.target.cpu.arch) {
.x86 => 'x', // Windows x86 COFF mangling: Private symbols get the usual
// prefix. Regular C symbols get a `_` prefix. Functions with `__stdcall`,
//`__fastcall`, and `__vectorcall` have custom mangling that appends `@N`
@@ -794,7 +794,7 @@ pub const Object = struct {
builder.llvm.di_compile_unit = builder.llvm.di_builder.?.createCompileUnit(
DW.LANG.C99,
builder.llvm.di_builder.?.createFile(options.root_name, compile_unit_dir_z),
- producer.toSlice(&builder).?,
+ producer.slice(&builder).?,
options.optimize_mode != .Debug,
"", // flags
0, // runtime version
@@ -830,7 +830,7 @@ pub const Object = struct {
target_machine = llvm.TargetMachine.create(
builder.llvm.target.?,
- builder.target_triple.toSlice(&builder).?,
+ builder.target_triple.slice(&builder).?,
if (options.target.cpu.model.llvm_name) |s| s.ptr else null,
options.llvm_cpu_features,
opt_level,
@@ -861,7 +861,7 @@ pub const Object = struct {
defer llvm.disposeMessage(rep);
std.testing.expectEqualStrings(
std.mem.span(rep),
- builder.data_layout.toSlice(&builder).?,
+ builder.data_layout.slice(&builder).?,
) catch unreachable;
}
}
@@ -963,7 +963,7 @@ pub const Object = struct {
llvm_error.* = try o.builder.structConst(llvm_slice_ty, &.{
global_index.toConst(),
- try o.builder.intConst(llvm_usize_ty, name.toSlice(&o.builder).?.len),
+ try o.builder.intConst(llvm_usize_ty, name.slice(&o.builder).?.len),
});
}
@@ -1223,6 +1223,7 @@ pub const Object = struct {
const func = mod.funcInfo(func_index);
const decl_index = func.owner_decl;
const decl = mod.declPtr(decl_index);
+ const fn_info = mod.typeToFunc(decl.ty).?;
const target = mod.getTarget();
const ip = &mod.intern_pool;
@@ -1237,28 +1238,43 @@ pub const Object = struct {
const global = function.ptrConst(&o.builder).global;
const llvm_func = global.toLlvm(&o.builder);
+ var attributes = try function.ptrConst(&o.builder).attributes.toWip(&o.builder);
+ defer attributes.deinit(&o.builder);
+
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
const ssp_buf_size = mod.comp.bin_file.options.stack_protector;
if (ssp_buf_size != 0) {
+ try attributes.addFnAttr(.sspstrong, &o.builder);
+ try attributes.addFnAttr(.{ .string = .{
+ .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");
@@ -1267,8 +1283,16 @@ pub const Object = struct {
// TODO: disable this if safety is off for the function scope
if (mod.comp.bin_file.options.stack_check) {
+ try attributes.addFnAttr(.{ .string = .{
+ .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", "");
}
@@ -1286,18 +1310,22 @@ pub const Object = struct {
var llvm_arg_i: u32 = 0;
// This gets the LLVM values from the function and stores them in `dg.args`.
- const fn_info = mod.typeToFunc(decl.ty).?;
const sret = firstParamSRet(fn_info, mod);
const ret_ptr: Builder.Value = if (sret) param: {
const param = wip.arg(llvm_arg_i);
llvm_arg_i += 1;
break :param param;
} else .none;
- const gpa = o.gpa;
if (ccAbiPromoteInt(fn_info.cc, mod, fn_info.return_type.toType())) |s| switch (s) {
- .signed => o.addAttr(llvm_func, 0, "signext"),
- .unsigned => o.addAttr(llvm_func, 0, "zeroext"),
+ .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");
+ },
};
const err_return_tracing = fn_info.return_type.toType().isError(mod) and
@@ -1312,6 +1340,7 @@ pub const Object = struct {
// This is the list of args we will use that correspond directly to the AIR arg
// instructions. Depending on the calling convention, this list is not necessarily
// a bijection with the actual LLVM parameters of the function.
+ const gpa = o.gpa;
var args: std.ArrayListUnmanaged(Builder.Value) = .{};
defer args.deinit(gpa);
@@ -1337,7 +1366,7 @@ pub const Object = struct {
} else {
args.appendAssumeCapacity(param);
- o.addByValParamAttrs(llvm_func, param_ty, param_index, fn_info, @intCast(llvm_arg_i));
+ try o.addByValParamAttrsOld(&attributes, llvm_func, param_ty, param_index, fn_info, llvm_arg_i);
}
llvm_arg_i += 1;
},
@@ -1347,7 +1376,7 @@ pub const Object = struct {
const param = wip.arg(llvm_arg_i);
const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod));
- o.addByRefParamAttrs(llvm_func, @intCast(llvm_arg_i), @intCast(alignment.toByteUnits() orelse 0), it.byval_attr, param_llvm_ty);
+ try o.addByRefParamAttrsOld(&attributes, llvm_func, llvm_arg_i, alignment, it.byval_attr, param_llvm_ty);
llvm_arg_i += 1;
if (isByRef(param_ty, mod)) {
@@ -1362,7 +1391,8 @@ pub const Object = struct {
const param = wip.arg(llvm_arg_i);
const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod));
- o.addArgAttr(llvm_func, @intCast(llvm_arg_i), "noundef");
+ 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)) {
@@ -1398,21 +1428,28 @@ pub const Object = struct {
if (math.cast(u5, it.zig_index - 1)) |i| {
if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) {
- o.addArgAttr(llvm_func, @intCast(llvm_arg_i), "noalias");
+ try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder);
+ o.addArgAttr(llvm_func, llvm_arg_i, "noalias");
}
}
if (param_ty.zigTypeTag(mod) != .Optional) {
- o.addArgAttr(llvm_func, @intCast(llvm_arg_i), "nonnull");
+ try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder);
+ o.addArgAttr(llvm_func, llvm_arg_i, "nonnull");
}
if (ptr_info.flags.is_const) {
- o.addArgAttr(llvm_func, @intCast(llvm_arg_i), "readonly");
+ try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder);
+ o.addArgAttr(llvm_func, llvm_arg_i, "readonly");
}
- const elem_align = ptr_info.flags.alignment.toByteUnitsOptional() orelse
- @max(ptr_info.child.toType().abiAlignment(mod), 1);
- o.addArgAttrInt(llvm_func, @intCast(llvm_arg_i), "align", elem_align);
- const ptr_param = wip.arg(llvm_arg_i + 0);
- const len_param = wip.arg(llvm_arg_i + 1);
- llvm_arg_i += 2;
+ 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);
+ llvm_arg_i += 1;
const slice_llvm_ty = try o.lowerType(param_ty);
args.appendAssumeCapacity(
@@ -1482,6 +1519,8 @@ pub const Object = struct {
}
}
+ function.ptr(&o.builder).attributes = try attributes.finish(&o.builder);
+
var di_file: ?*llvm.DIFile = null;
var di_scope: ?*llvm.DIScope = null;
@@ -1618,7 +1657,7 @@ pub const Object = struct {
llvm_global.setDLLStorageClass(.Default);
}
if (self.di_map.get(decl)) |di_node| {
- const decl_name_slice = decl_name.toSlice(&self.builder).?;
+ const decl_name_slice = decl_name.slice(&self.builder).?;
if (try decl.isFunction(mod)) {
const di_func: *llvm.DISubprogram = @ptrCast(di_node);
const linkage_name = llvm.MDString.get(self.builder.llvm.context, decl_name_slice.ptr, decl_name_slice.len);
@@ -1655,7 +1694,7 @@ pub const Object = struct {
llvm_global.setDLLStorageClass(.DLLExport);
}
if (self.di_map.get(decl)) |di_node| {
- const exp_name_slice = exp_name.toSlice(&self.builder).?;
+ const exp_name_slice = exp_name.slice(&self.builder).?;
if (try decl.isFunction(mod)) {
const di_func: *llvm.DISubprogram = @ptrCast(di_node);
const linkage_name = llvm.MDString.get(self.builder.llvm.context, exp_name_slice.ptr, exp_name_slice.len);
@@ -2816,7 +2855,7 @@ pub const Object = struct {
const fqn = try o.builder.string(ip.stringToSlice(try decl.getFullyQualifiedName(mod)));
const llvm_addrspace = toLlvmAddressSpace(decl.@"addrspace", target);
- const llvm_fn = o.llvm_module.addFunctionInAddressSpace(fqn.toSlice(&o.builder).?, fn_type.toLlvm(&o.builder), @intFromEnum(llvm_addrspace));
+ const llvm_fn = o.llvm_module.addFunctionInAddressSpace(fqn.slice(&o.builder).?, fn_type.toLlvm(&o.builder), @intFromEnum(llvm_addrspace));
var global = Builder.Global{
.type = fn_type,
@@ -2826,6 +2865,9 @@ pub const Object = struct {
.global = @enumFromInt(o.builder.globals.count()),
};
+ var attributes: Builder.FunctionAttributes.Wip = .{};
+ defer attributes.deinit(&o.builder);
+
const is_extern = decl.isExtern(mod);
if (!is_extern) {
global.linkage = .internal;
@@ -2834,43 +2876,64 @@ pub const Object = struct {
llvm_fn.setUnnamedAddr(.True);
} else {
if (target.isWasm()) {
+ try attributes.addFnAttr(.{ .string = .{
+ .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);
}
}
}
}
+ var llvm_arg_i: u32 = 0;
if (sret) {
- o.addArgAttr(llvm_fn, 0, "nonnull"); // Sret pointers must not be address 0
- o.addArgAttr(llvm_fn, 0, "noalias");
+ // 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));
- const raw_llvm_ret_ty = (try o.lowerType(fn_info.return_type.toType())).toLlvm(&o.builder);
- llvm_fn.addSretAttr(raw_llvm_ret_ty);
+ llvm_arg_i += 1;
}
const err_return_tracing = fn_info.return_type.toType().isError(mod) and
mod.comp.bin_file.options.error_return_tracing;
if (err_return_tracing) {
- o.addArgAttr(llvm_fn, @intFromBool(sret), "nonnull");
+ try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder);
+ o.addArgAttr(llvm_fn, llvm_arg_i, "nonnull");
+ llvm_arg_i += 1;
}
switch (fn_info.cc) {
.Unspecified, .Inline => {
+ function.call_conv = .fastcc;
llvm_fn.setFunctionCallConv(.Fast);
},
.Naked => {
+ try attributes.addFnAttr(.naked, &o.builder);
o.addFnAttr(llvm_fn, "naked");
},
.Async => {
+ function.call_conv = .fastcc;
llvm_fn.setFunctionCallConv(.Fast);
@panic("TODO: LLVM backend lower async function");
},
else => {
- llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target));
+ function.call_conv = toLlvmCallConv(fn_info.cc, target);
+ llvm_fn.setFunctionCallConv(@enumFromInt(@intFromEnum(function.call_conv)));
},
}
@@ -2880,9 +2943,10 @@ pub const Object = struct {
}
// Function attributes that are independent of analysis results of the function body.
- o.addCommonFnAttributes(llvm_fn);
+ try o.addCommonFnAttributes(&attributes, llvm_fn);
if (fn_info.return_type == .noreturn_type) {
+ try attributes.addFnAttr(.noreturn, &o.builder);
o.addFnAttr(llvm_fn, "noreturn");
}
@@ -2890,23 +2954,24 @@ pub const Object = struct {
// because functions with bodies are handled in `updateFunc`.
if (is_extern) {
var it = iterateParamTypes(o, fn_info);
- it.llvm_index += @intFromBool(sret);
- it.llvm_index += @intFromBool(err_return_tracing);
+ it.llvm_index = llvm_arg_i;
while (try it.next()) |lowering| switch (lowering) {
.byval => {
const param_index = it.zig_index - 1;
const param_ty = fn_info.param_types.get(ip)[param_index].toType();
if (!isByRef(param_ty, mod)) {
- o.addByValParamAttrs(llvm_fn, param_ty, param_index, fn_info, it.llvm_index - 1);
+ try o.addByValParamAttrsOld(&attributes, llvm_fn, param_ty, param_index, fn_info, it.llvm_index - 1);
}
},
.byref => {
const param_ty = fn_info.param_types.get(ip)[it.zig_index - 1];
const param_llvm_ty = try o.lowerType(param_ty.toType());
- const alignment = param_ty.toType().abiAlignment(mod);
- o.addByRefParamAttrs(llvm_fn, it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty);
+ 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);
},
.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.
@@ -2924,25 +2989,42 @@ 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);
return global.kind.function;
}
- fn addCommonFnAttributes(o: *Object, llvm_fn: *llvm.Value) void {
+ 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
@@ -2953,22 +3035,38 @@ pub const Object = struct {
// and llvm detects that the body is equivalent to memcpy, it may replace the
// 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", "");
}
}
@@ -3002,7 +3100,7 @@ pub const Object = struct {
fqn;
const llvm_global = o.llvm_module.addGlobalInAddressSpace(
global.type.toLlvm(&o.builder),
- fqn.toSlice(&o.builder).?,
+ fqn.slice(&o.builder).?,
@intFromEnum(global.addr_space),
);
@@ -4403,47 +4501,114 @@ pub const Object = struct {
fn addByValParamAttrs(
o: *Object,
+ attributes: *Builder.FunctionAttributes.Wip,
+ 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);
+ }
+ }
+ if (!param_ty.isPtrLikeOptional(mod) and !ptr_info.flags.is_allowzero) {
+ try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder);
+ }
+ if (ptr_info.flags.is_const) {
+ try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder);
+ }
+ 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);
+ } else if (ccAbiPromoteInt(fn_info.cc, mod, param_ty)) |s| switch (s) {
+ .signed => try attributes.addParamAttr(llvm_arg_i, .signext, &o.builder),
+ .unsigned => try attributes.addParamAttr(llvm_arg_i, .zeroext, &o.builder),
+ };
+ }
+
+ fn addByRefParamAttrs(
+ o: *Object,
+ attributes: *Builder.FunctionAttributes.Wip,
+ 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);
+ 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,
- ) void {
+ ) 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 = ptr_info.flags.alignment.toByteUnitsOptional() orelse
- @max(ptr_info.child.toType().abiAlignment(mod), 1);
- o.addArgAttrInt(llvm_fn, llvm_arg_i, "align", elem_align);
+ 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 => o.addArgAttr(llvm_fn, llvm_arg_i, "signext"),
- .unsigned => o.addArgAttr(llvm_fn, llvm_arg_i, "zeroext"),
+ .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 addByRefParamAttrs(
+ fn addByRefParamAttrsOld(
o: *Object,
+ attributes: *Builder.FunctionAttributes.Wip,
llvm_fn: *llvm.Value,
llvm_arg_i: u32,
- alignment: u32,
+ alignment: Builder.Alignment,
byval_attr: bool,
param_llvm_ty: Builder.Type,
- ) void {
+ ) 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);
+ 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));
}
}
@@ -4841,10 +5006,10 @@ pub const FuncGen = struct {
.slice_ptr => try self.airSliceField(inst, 0),
.slice_len => try self.airSliceField(inst, 1),
- .call => try self.airCall(inst, .Auto),
- .call_always_tail => try self.airCall(inst, .AlwaysTail),
- .call_never_tail => try self.airCall(inst, .NeverTail),
- .call_never_inline => try self.airCall(inst, .NeverInline),
+ .call => try self.airCall(inst, .auto),
+ .call_always_tail => try self.airCall(inst, .always_tail),
+ .call_never_tail => try self.airCall(inst, .never_tail),
+ .call_never_inline => try self.airCall(inst, .never_inline),
.ptr_slice_ptr_ptr => try self.airPtrSliceFieldPtr(inst, 0),
.ptr_slice_len_ptr => try self.airPtrSliceFieldPtr(inst, 1),
@@ -4953,7 +5118,15 @@ pub const FuncGen = struct {
}
}
- fn airCall(self: *FuncGen, inst: Air.Inst.Index, attr: llvm.CallAttr) !Builder.Value {
+ pub const CallAttr = enum {
+ Auto,
+ NeverTail,
+ NeverInline,
+ AlwaysTail,
+ AlwaysInline,
+ };
+
+ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !Builder.Value {
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
const extra = self.air.extraData(Air.Call, pl_op.payload);
const args: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]);
@@ -4972,14 +5145,25 @@ pub const FuncGen = struct {
const target = mod.getTarget();
const sret = firstParamSRet(fn_info, mod);
- var llvm_args = std.ArrayList(*llvm.Value).init(self.gpa);
+ var llvm_args = std.ArrayList(Builder.Value).init(self.gpa);
defer llvm_args.deinit();
+ var attributes: Builder.FunctionAttributes.Wip = .{};
+ defer attributes.deinit(&o.builder);
+
+ switch (modifier) {
+ .auto, .never_tail, .always_tail => {},
+ .never_inline => try attributes.addFnAttr(.@"noinline", &o.builder),
+ .async_kw, .no_async, .always_inline, .compile_time => unreachable,
+ }
+
const ret_ptr = if (!sret) null else blk: {
const llvm_ret_ty = try o.lowerType(return_type);
+ try attributes.addParamAttr(0, .{ .sret = llvm_ret_ty }, &o.builder);
+
const alignment = Builder.Alignment.fromByteUnits(return_type.abiAlignment(mod));
const ret_ptr = try self.buildAlloca(llvm_ret_ty, alignment);
- try llvm_args.append(ret_ptr.toLlvm(&self.wip));
+ try llvm_args.append(ret_ptr);
break :blk ret_ptr;
};
@@ -4987,7 +5171,7 @@ pub const FuncGen = struct {
o.module.comp.bin_file.options.error_return_tracing;
if (err_return_tracing) {
assert(self.err_ret_trace != .none);
- try llvm_args.append(self.err_ret_trace.toLlvm(&self.wip));
+ try llvm_args.append(self.err_ret_trace);
}
var it = iterateParamTypes(o, fn_info);
@@ -5001,9 +5185,9 @@ pub const FuncGen = struct {
if (isByRef(param_ty, mod)) {
const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod));
const loaded = try self.wip.load(.normal, llvm_param_ty, llvm_arg, alignment, "");
- try llvm_args.append(loaded.toLlvm(&self.wip));
+ try llvm_args.append(loaded);
} else {
- try llvm_args.append(llvm_arg.toLlvm(&self.wip));
+ try llvm_args.append(llvm_arg);
}
},
.byref => {
@@ -5011,13 +5195,13 @@ pub const FuncGen = struct {
const param_ty = self.typeOf(arg);
const llvm_arg = try self.resolveInst(arg);
if (isByRef(param_ty, mod)) {
- try llvm_args.append(llvm_arg.toLlvm(&self.wip));
+ try llvm_args.append(llvm_arg);
} else {
const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod));
const param_llvm_ty = llvm_arg.typeOfWip(&self.wip);
const arg_ptr = try self.buildAlloca(param_llvm_ty, alignment);
_ = try self.wip.store(.normal, llvm_arg, arg_ptr, alignment);
- try llvm_args.append(arg_ptr.toLlvm(&self.wip));
+ try llvm_args.append(arg_ptr);
}
},
.byref_mut => {
@@ -5034,7 +5218,7 @@ pub const FuncGen = struct {
} else {
_ = try self.wip.store(.normal, llvm_arg, arg_ptr, alignment);
}
- try llvm_args.append(arg_ptr.toLlvm(&self.wip));
+ try llvm_args.append(arg_ptr);
},
.abi_sized_int => {
const arg = args[it.zig_index - 1];
@@ -5045,7 +5229,7 @@ pub const FuncGen = struct {
if (isByRef(param_ty, mod)) {
const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod));
const loaded = try self.wip.load(.normal, int_llvm_ty, llvm_arg, alignment, "");
- try llvm_args.append(loaded.toLlvm(&self.wip));
+ try llvm_args.append(loaded);
} else {
// LLVM does not allow bitcasting structs so we must allocate
// a local, store as one type, and then load as another type.
@@ -5056,7 +5240,7 @@ pub const FuncGen = struct {
const int_ptr = try self.buildAlloca(int_llvm_ty, alignment);
_ = try self.wip.store(.normal, llvm_arg, int_ptr, alignment);
const loaded = try self.wip.load(.normal, int_llvm_ty, int_ptr, alignment, "");
- try llvm_args.append(loaded.toLlvm(&self.wip));
+ try llvm_args.append(loaded);
}
},
.slice => {
@@ -5064,7 +5248,7 @@ pub const FuncGen = struct {
const llvm_arg = try self.resolveInst(arg);
const ptr = try self.wip.extractValue(llvm_arg, &.{0}, "");
const len = try self.wip.extractValue(llvm_arg, &.{1}, "");
- try llvm_args.appendSlice(&.{ ptr.toLlvm(&self.wip), len.toLlvm(&self.wip) });
+ try llvm_args.appendSlice(&.{ ptr, len });
},
.multiple_llvm_types => {
const arg = args[it.zig_index - 1];
@@ -5086,14 +5270,14 @@ pub const FuncGen = struct {
Builder.Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
const field_ptr = try self.wip.gepStruct(llvm_ty, arg_ptr, i, "");
const loaded = try self.wip.load(.normal, field_ty, field_ptr, alignment, "");
- llvm_args.appendAssumeCapacity(loaded.toLlvm(&self.wip));
+ llvm_args.appendAssumeCapacity(loaded);
}
},
.as_u16 => {
const arg = args[it.zig_index - 1];
const llvm_arg = try self.resolveInst(arg);
const casted = try self.wip.cast(.bitcast, llvm_arg, .i16, "");
- try llvm_args.append(casted.toLlvm(&self.wip));
+ try llvm_args.append(casted);
},
.float_array => |count| {
const arg = args[it.zig_index - 1];
@@ -5110,7 +5294,7 @@ pub const FuncGen = struct {
const array_ty = try o.builder.arrayType(count, float_ty);
const loaded = try self.wip.load(.normal, array_ty, llvm_arg, alignment, "");
- try llvm_args.append(loaded.toLlvm(&self.wip));
+ try llvm_args.append(loaded);
},
.i32_array, .i64_array => |arr_len| {
const elem_size: u8 = if (lowering == .i32_array) 32 else 64;
@@ -5127,24 +5311,10 @@ pub const FuncGen = struct {
const array_ty =
try o.builder.arrayType(arr_len, try o.builder.intType(@intCast(elem_size)));
const loaded = try self.wip.load(.normal, array_ty, llvm_arg, alignment, "");
- try llvm_args.append(loaded.toLlvm(&self.wip));
+ try llvm_args.append(loaded);
},
};
- const llvm_fn_ty = try o.lowerType(zig_fn_ty);
- const call = (try self.wip.unimplemented(llvm_fn_ty.functionReturn(&o.builder), "")).finish(
- self.builder.buildCall(
- llvm_fn_ty.toLlvm(&o.builder),
- llvm_fn.toLlvm(&self.wip),
- llvm_args.items.ptr,
- @intCast(llvm_args.items.len),
- toLlvmCallConv(fn_info.cc, target),
- attr,
- "",
- ),
- &self.wip,
- );
-
if (callee_ty.zigTypeTag(mod) == .Pointer) {
// Add argument attributes for function pointer calls.
it = iterateParamTypes(o, fn_info);
@@ -5155,19 +5325,17 @@ pub const FuncGen = 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)) {
- o.addByValParamAttrs(call.toLlvm(&self.wip), 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 => {
const param_index = it.zig_index - 1;
const param_ty = fn_info.param_types.get(ip)[param_index].toType();
const param_llvm_ty = try o.lowerType(param_ty);
- const alignment = param_ty.abiAlignment(mod);
- o.addByRefParamAttrs(call.toLlvm(&self.wip), it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty);
- },
- .byref_mut => {
- o.addArgAttr(call.toLlvm(&self.wip), it.llvm_index - 1, "noundef");
+ const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod));
+ 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),
// No attributes needed for these.
.no_bits,
.abi_sized_int,
@@ -5186,23 +5354,40 @@ pub const FuncGen = struct {
if (math.cast(u5, it.zig_index - 1)) |i| {
if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) {
- o.addArgAttr(call.toLlvm(&self.wip), llvm_arg_i, "noalias");
+ try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder);
}
}
if (param_ty.zigTypeTag(mod) != .Optional) {
- o.addArgAttr(call.toLlvm(&self.wip), llvm_arg_i, "nonnull");
+ try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder);
}
if (ptr_info.flags.is_const) {
- o.addArgAttr(call.toLlvm(&self.wip), llvm_arg_i, "readonly");
+ try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder);
}
- const elem_align = ptr_info.flags.alignment.toByteUnitsOptional() orelse
- @max(ptr_info.child.toType().abiAlignment(mod), 1);
- o.addArgAttrInt(call.toLlvm(&self.wip), llvm_arg_i, "align", elem_align);
+ 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);
},
};
}
- if (fn_info.return_type == .noreturn_type and attr != .AlwaysTail) {
+ const call = try self.wip.call(
+ switch (modifier) {
+ .auto, .never_inline => .normal,
+ .never_tail => .notail,
+ .always_tail => .musttail,
+ .async_kw, .no_async, .always_inline, .compile_time => unreachable,
+ },
+ toLlvmCallConv(fn_info.cc, target),
+ try attributes.finish(&o.builder),
+ try o.lowerType(zig_fn_ty),
+ llvm_fn,
+ llvm_args.items,
+ "",
+ );
+
+ if (fn_info.return_type == .noreturn_type and modifier != .always_tail) {
return .none;
}
@@ -5211,9 +5396,7 @@ pub const FuncGen = struct {
}
const llvm_ret_ty = try o.lowerType(return_type);
-
if (ret_ptr) |rp| {
- call.toLlvm(&self.wip).setCallSret(llvm_ret_ty.toLlvm(&o.builder));
if (isByRef(return_type, mod)) {
return rp;
} else {
@@ -5269,25 +5452,24 @@ pub const FuncGen = struct {
// ptr null, ; stack trace
// ptr @2, ; addr (null ?usize)
// )
- const args = [4]*llvm.Value{
- msg_ptr.toLlvm(&o.builder),
- (try o.builder.intConst(llvm_usize, msg_len)).toLlvm(&o.builder),
- (try o.builder.nullConst(.ptr)).toLlvm(&o.builder),
- null_opt_addr_global.toLlvm(&o.builder),
- };
const panic_func = mod.funcInfo(mod.panic_func_index);
const panic_decl = mod.declPtr(panic_func.owner_decl);
const fn_info = mod.typeToFunc(panic_decl.ty).?;
const panic_global = try o.resolveLlvmFunction(panic_func.owner_decl);
- _ = (try fg.wip.unimplemented(.void, "")).finish(fg.builder.buildCall(
- (try o.lowerType(panic_decl.ty)).toLlvm(&o.builder),
- panic_global.toLlvm(&o.builder),
- &args,
- args.len,
+ _ = try fg.wip.call(
+ .normal,
toLlvmCallConv(fn_info.cc, target),
- .Auto,
+ .none,
+ panic_global.typeOf(&o.builder),
+ panic_global.toValue(&o.builder),
+ &.{
+ msg_ptr.toValue(),
+ try o.builder.intValue(llvm_usize, msg_len),
+ try o.builder.nullValue(.ptr),
+ null_opt_addr_global.toValue(),
+ },
"",
- ), &fg.wip);
+ );
_ = try fg.wip.@"unreachable"();
}
@@ -5395,7 +5577,7 @@ pub const FuncGen = struct {
o.llvm_module.addFunction(llvm_fn_name, llvm_fn_ty.toLlvm(&o.builder));
const args: [2]*llvm.Value = .{ dest_list.toLlvm(&self.wip), src_list.toLlvm(&self.wip) };
- _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall(
+ _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCallOld(
llvm_fn_ty.toLlvm(&o.builder),
llvm_fn,
&args,
@@ -5422,7 +5604,7 @@ pub const FuncGen = struct {
o.llvm_module.addFunction(llvm_fn_name, llvm_fn_ty.toLlvm(&o.builder));
const args: [1]*llvm.Value = .{list.toLlvm(&self.wip)};
- _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall(
+ _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCallOld(
llvm_fn_ty.toLlvm(&o.builder),
llvm_fn,
&args,
@@ -5449,7 +5631,7 @@ pub const FuncGen = struct {
o.llvm_module.addFunction(llvm_fn_name, llvm_fn_ty.toLlvm(&o.builder));
const args: [1]*llvm.Value = .{list.toLlvm(&self.wip)};
- _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall(
+ _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCallOld(
llvm_fn_ty.toLlvm(&o.builder),
llvm_fn,
&args,
@@ -5495,16 +5677,15 @@ pub const FuncGen = struct {
const un_op = self.air.instructions.items(.data)[inst].un_op;
const operand = try self.resolveInst(un_op);
const llvm_fn = try self.getCmpLtErrorsLenFunction();
- const args: [1]*llvm.Value = .{operand.toLlvm(&self.wip)};
- return (try self.wip.unimplemented(.i1, "")).finish(self.builder.buildCall(
- llvm_fn.typeOf(&o.builder).toLlvm(&o.builder),
- llvm_fn.toLlvm(&o.builder),
- &args,
- args.len,
- .Fast,
- .Auto,
+ return self.wip.call(
+ .normal,
+ .fastcc,
+ .none,
+ llvm_fn.typeOf(&o.builder),
+ llvm_fn.toValue(&o.builder),
+ &.{operand},
"",
- ), &self.wip);
+ );
}
fn cmp(
@@ -5953,16 +6134,15 @@ pub const FuncGen = struct {
}
const libc_fn = try self.getLibcFunction(fn_name, &.{param_type}, dest_llvm_ty);
- const params = [1]*llvm.Value{extended.toLlvm(&self.wip)};
- return (try self.wip.unimplemented(dest_llvm_ty, "")).finish(self.builder.buildCall(
- libc_fn.typeOf(&o.builder).toLlvm(&o.builder),
- libc_fn.toLlvm(&o.builder),
- ¶ms,
- params.len,
- .C,
- .Auto,
+ return self.wip.call(
+ .normal,
+ .ccc,
+ .none,
+ libc_fn.typeOf(&o.builder),
+ libc_fn.toValue(&o.builder),
+ &.{extended},
"",
- ), &self.wip);
+ );
}
fn airIntFromFloat(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !Builder.Value {
@@ -6013,16 +6193,15 @@ pub const FuncGen = struct {
const operand_llvm_ty = try o.lowerType(operand_ty);
const libc_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, libc_ret_ty);
- const params = [1]*llvm.Value{operand.toLlvm(&self.wip)};
- var result = (try self.wip.unimplemented(libc_ret_ty, "")).finish(self.builder.buildCall(
- libc_fn.typeOf(&o.builder).toLlvm(&o.builder),
- libc_fn.toLlvm(&o.builder),
- ¶ms,
- params.len,
- .C,
- .Auto,
+ var result = try self.wip.call(
+ .normal,
+ .ccc,
+ .none,
+ libc_fn.typeOf(&o.builder),
+ libc_fn.toValue(&o.builder),
+ &.{operand},
"",
- ), &self.wip);
+ );
if (libc_ret_ty != ret_ty) result = try self.wip.cast(.bitcast, result, ret_ty, "");
if (ret_ty != dest_llvm_ty) result = try self.wip.cast(.trunc, result, dest_llvm_ty, "");
@@ -6843,6 +7022,9 @@ pub const FuncGen = struct {
}
}
+ var attributes: Builder.FunctionAttributes.Wip = .{};
+ defer attributes.deinit(&o.builder);
+
const ret_llvm_ty = switch (return_count) {
0 => .void,
1 => llvm_ret_types[0],
@@ -6861,7 +7043,7 @@ pub const FuncGen = struct {
.ATT,
.False,
);
- const call = (try self.wip.unimplemented(ret_llvm_ty, "")).finish(self.builder.buildCall(
+ const call = (try self.wip.unimplemented(ret_llvm_ty, "")).finish(self.builder.buildCallOld(
llvm_fn_ty.toLlvm(&o.builder),
asm_fn,
llvm_param_values.ptr,
@@ -6872,6 +7054,7 @@ pub const FuncGen = struct {
), &self.wip);
for (llvm_param_attrs[0..param_count], 0..) |llvm_elem_ty, i| {
if (llvm_elem_ty != .none) {
+ try attributes.addParamAttr(i, .{ .elementtype = llvm_elem_ty }, &o.builder);
llvm.setCallElemTypeAttr(call.toLlvm(&self.wip), i, llvm_elem_ty.toLlvm(&o.builder));
}
}
@@ -7287,7 +7470,7 @@ pub const FuncGen = struct {
const args: [1]*llvm.Value = .{
(try o.builder.intConst(.i32, index)).toLlvm(&o.builder),
};
- return (try self.wip.unimplemented(.i32, "")).finish(self.builder.buildCall(
+ return (try self.wip.unimplemented(.i32, "")).finish(self.builder.buildCallOld(
(try o.builder.fnType(.i32, &.{.i32}, .normal)).toLlvm(&o.builder),
llvm_fn,
&args,
@@ -7308,7 +7491,7 @@ pub const FuncGen = struct {
(try o.builder.intConst(.i32, index)).toLlvm(&o.builder),
operand.toLlvm(&self.wip),
};
- return (try self.wip.unimplemented(.i32, "")).finish(self.builder.buildCall(
+ return (try self.wip.unimplemented(.i32, "")).finish(self.builder.buildCallOld(
(try o.builder.fnType(.i32, &.{ .i32, .i32 }, .normal)).toLlvm(&o.builder),
llvm_fn,
&args,
@@ -7425,7 +7608,7 @@ pub const FuncGen = struct {
});
const llvm_fn_ty = try o.builder.fnType(llvm_ret_ty, &.{ llvm_inst_ty, llvm_inst_ty }, .normal);
const llvm_fn = try fg.getIntrinsic(intrinsic_name, &.{llvm_inst_ty});
- const result_struct = (try fg.wip.unimplemented(llvm_ret_ty, "")).finish(fg.builder.buildCall(
+ const result_struct = (try fg.wip.unimplemented(llvm_ret_ty, "")).finish(fg.builder.buildCallOld(
llvm_fn_ty.toLlvm(&o.builder),
llvm_fn,
&[_]*llvm.Value{ lhs.toLlvm(&fg.wip), rhs.toLlvm(&fg.wip) },
@@ -7768,7 +7951,7 @@ pub const FuncGen = struct {
);
const llvm_fn_ty = try o.builder.fnType(llvm_ret_ty, &.{ llvm_lhs_ty, llvm_lhs_ty }, .normal);
const result_struct = (try self.wip.unimplemented(llvm_ret_ty, "")).finish(
- self.builder.buildCall(
+ self.builder.buildCallOld(
llvm_fn_ty.toLlvm(&o.builder),
llvm_fn,
&[_]*llvm.Value{ lhs.toLlvm(&self.wip), rhs.toLlvm(&self.wip) },
@@ -7818,29 +8001,23 @@ pub const FuncGen = struct {
const o = self.dg.object;
assert(args_vectors.len <= 3);
- const llvm_fn_ty = llvm_fn.typeOf(&o.builder);
- const llvm_scalar_ty = llvm_fn_ty.functionReturn(&o.builder);
-
var i: usize = 0;
var result = result_vector;
while (i < vector_len) : (i += 1) {
const index_i32 = try o.builder.intValue(.i32, i);
- var args: [3]*llvm.Value = undefined;
+ var args: [3]Builder.Value = undefined;
for (args[0..args_vectors.len], args_vectors) |*arg_elem, arg_vector| {
- arg_elem.* = (try self.wip.extractElement(arg_vector, index_i32, "")).toLlvm(&self.wip);
+ arg_elem.* = try self.wip.extractElement(arg_vector, index_i32, "");
}
- const result_elem = (try self.wip.unimplemented(llvm_scalar_ty, "")).finish(
- self.builder.buildCall(
- llvm_fn_ty.toLlvm(&o.builder),
- llvm_fn.toLlvm(&o.builder),
- &args,
- @intCast(args_vectors.len),
- .C,
- .Auto,
- "",
- ),
- &self.wip,
+ const result_elem = try self.wip.call(
+ .normal,
+ .ccc,
+ .none,
+ llvm_fn.typeOf(&o.builder),
+ llvm_fn.toValue(&o.builder),
+ args[0..args_vectors.len],
+ "",
);
result = try self.wip.insertElement(result, result_elem, index_i32, "");
}
@@ -7861,7 +8038,7 @@ pub const FuncGen = struct {
};
const fn_type = try o.builder.fnType(return_type, param_types, .normal);
- const f = o.llvm_module.addFunction(fn_name.toSlice(&o.builder).?, fn_type.toLlvm(&o.builder));
+ const f = o.llvm_module.addFunction(fn_name.slice(&o.builder).?, fn_type.toLlvm(&o.builder));
var global = Builder.Global{
.type = fn_type,
@@ -7942,20 +8119,15 @@ pub const FuncGen = struct {
return self.wip.icmp(int_cond, result, zero_vector, "");
}
- const llvm_fn_ty = libc_fn.typeOf(&o.builder);
- const llvm_params = [2]*llvm.Value{ params[0].toLlvm(&self.wip), params[1].toLlvm(&self.wip) };
- const result = (try self.wip.unimplemented(
- llvm_fn_ty.functionReturn(&o.builder),
- "",
- )).finish(self.builder.buildCall(
- libc_fn.typeOf(&o.builder).toLlvm(&o.builder),
- libc_fn.toLlvm(&o.builder),
- &llvm_params,
- llvm_params.len,
- .C,
- .Auto,
+ const result = try self.wip.call(
+ .normal,
+ .ccc,
+ .none,
+ libc_fn.typeOf(&o.builder),
+ libc_fn.toValue(&o.builder),
+ ¶ms,
"",
- ), &self.wip);
+ );
return self.wip.icmp(int_cond, result, zero.toValue(), "");
}
@@ -8085,7 +8257,7 @@ pub const FuncGen = struct {
);
var llvm_params: [params_len]*llvm.Value = undefined;
for (&llvm_params, params) |*llvm_param, param| llvm_param.* = param.toLlvm(&self.wip);
- return (try self.wip.unimplemented(llvm_ty, "")).finish(self.builder.buildCall(
+ return (try self.wip.unimplemented(llvm_ty, "")).finish(self.builder.buildCallOld(
llvm_fn_ty.toLlvm(&o.builder),
llvm_fn,
&llvm_params,
@@ -8311,17 +8483,16 @@ pub const FuncGen = struct {
compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits),
});
- const llvm_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty);
- const params = [1]*llvm.Value{operand.toLlvm(&self.wip)};
- return (try self.wip.unimplemented(dest_llvm_ty, "")).finish(self.builder.buildCall(
- llvm_fn.typeOf(&o.builder).toLlvm(&o.builder),
- llvm_fn.toLlvm(&o.builder),
- ¶ms,
- params.len,
- .C,
- .Auto,
+ const libc_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty);
+ return self.wip.call(
+ .normal,
+ .ccc,
+ .none,
+ libc_fn.typeOf(&o.builder),
+ libc_fn.toValue(&o.builder),
+ &.{operand},
"",
- ), &self.wip);
+ );
}
}
@@ -8346,17 +8517,16 @@ pub const FuncGen = struct {
compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits),
});
- const llvm_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty);
- const params = [1]*llvm.Value{operand.toLlvm(&self.wip)};
- return (try self.wip.unimplemented(dest_llvm_ty, "")).finish(self.builder.buildCall(
- llvm_fn.typeOf(&o.builder).toLlvm(&o.builder),
- llvm_fn.toLlvm(&o.builder),
- ¶ms,
- params.len,
- .C,
- .Auto,
+ const libc_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty);
+ return self.wip.call(
+ .normal,
+ .ccc,
+ .none,
+ libc_fn.typeOf(&o.builder),
+ libc_fn.toValue(&o.builder),
+ &.{operand},
"",
- ), &self.wip);
+ );
}
}
@@ -8657,7 +8827,7 @@ pub const FuncGen = struct {
_ = inst;
const o = self.dg.object;
const llvm_fn = try self.getIntrinsic("llvm.trap", &.{});
- _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall(
+ _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCallOld(
(try o.builder.fnType(.void, &.{}, .normal)).toLlvm(&o.builder),
llvm_fn,
undefined,
@@ -8674,7 +8844,7 @@ pub const FuncGen = struct {
_ = inst;
const o = self.dg.object;
const llvm_fn = try self.getIntrinsic("llvm.debugtrap", &.{});
- _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall(
+ _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCallOld(
(try o.builder.fnType(.void, &.{}, .normal)).toLlvm(&o.builder),
llvm_fn,
undefined,
@@ -8701,7 +8871,7 @@ pub const FuncGen = struct {
const params = [_]*llvm.Value{
(try o.builder.intConst(.i32, 0)).toLlvm(&o.builder),
};
- const ptr_val = (try self.wip.unimplemented(.ptr, "")).finish(self.builder.buildCall(
+ const ptr_val = (try self.wip.unimplemented(.ptr, "")).finish(self.builder.buildCallOld(
(try o.builder.fnType(.ptr, &.{.i32}, .normal)).toLlvm(&o.builder),
llvm_fn,
¶ms,
@@ -8727,7 +8897,7 @@ pub const FuncGen = struct {
(try o.builder.intConst(.i32, 0)).toLlvm(&o.builder),
};
const ptr_val = (try self.wip.unimplemented(llvm_fn_ty.functionReturn(&o.builder), "")).finish(
- self.builder.buildCall(
+ self.builder.buildCallOld(
llvm_fn_ty.toLlvm(&o.builder),
llvm_fn,
¶ms,
@@ -9256,7 +9426,7 @@ pub const FuncGen = struct {
Builder.Constant.false.toLlvm(&o.builder),
};
const wrong_size_result = (try self.wip.unimplemented(llvm_operand_ty, "")).finish(
- self.builder.buildCall(
+ self.builder.buildCallOld(
llvm_fn_ty.toLlvm(&o.builder),
fn_val,
¶ms,
@@ -9283,7 +9453,7 @@ pub const FuncGen = struct {
const params = [_]*llvm.Value{operand.toLlvm(&self.wip)};
const wrong_size_result = (try self.wip.unimplemented(llvm_operand_ty, "")).finish(
- self.builder.buildCall(
+ self.builder.buildCallOld(
llvm_fn_ty.toLlvm(&o.builder),
fn_val,
¶ms,
@@ -9331,7 +9501,7 @@ pub const FuncGen = struct {
const params = [_]*llvm.Value{operand.toLlvm(&self.wip)};
const wrong_size_result = (try self.wip.unimplemented(llvm_operand_ty, "")).finish(
- self.builder.buildCall(
+ self.builder.buildCallOld(
llvm_fn_ty.toLlvm(&o.builder),
fn_val,
¶ms,
@@ -9389,16 +9559,15 @@ pub const FuncGen = struct {
const enum_ty = self.typeOf(un_op);
const llvm_fn = try self.getIsNamedEnumValueFunction(enum_ty);
- const params = [_]*llvm.Value{operand.toLlvm(&self.wip)};
- return (try self.wip.unimplemented(.i1, "")).finish(self.builder.buildCall(
- llvm_fn.typeOf(&o.builder).toLlvm(&o.builder),
- llvm_fn.toLlvm(&o.builder),
- ¶ms,
- params.len,
- .Fast,
- .Auto,
+ return self.wip.call(
+ .normal,
+ .fastcc,
+ .none,
+ llvm_fn.typeOf(&o.builder),
+ llvm_fn.toValue(&o.builder),
+ &.{operand},
"",
- ), &self.wip);
+ );
}
fn getIsNamedEnumValueFunction(self: *FuncGen, enum_ty: Type) !Builder.Function.Index {
@@ -9416,13 +9585,16 @@ pub const FuncGen = struct {
fqn.fmt(&mod.intern_pool),
});
+ var attributes: Builder.FunctionAttributes.Wip = .{};
+ defer attributes.deinit(&o.builder);
+
const fn_type = try o.builder.fnType(.i1, &.{
try o.lowerType(enum_type.tag_ty.toType()),
}, .normal);
- const fn_val = o.llvm_module.addFunction(llvm_fn_name.toSlice(&o.builder).?, fn_type.toLlvm(&o.builder));
+ const fn_val = o.llvm_module.addFunction(llvm_fn_name.slice(&o.builder).?, fn_type.toLlvm(&o.builder));
fn_val.setLinkage(.Internal);
fn_val.setFunctionCallConv(.Fast);
- o.addCommonFnAttributes(fn_val);
+ try o.addCommonFnAttributes(&attributes, fn_val);
var global = Builder.Global{
.linkage = .internal,
@@ -9431,6 +9603,8 @@ pub const FuncGen = struct {
};
var function = Builder.Function{
.global = @enumFromInt(o.builder.globals.count()),
+ .call_conv = .fastcc,
+ .attributes = try attributes.finish(&o.builder),
};
try o.builder.llvm.globals.append(self.gpa, fn_val);
_ = try o.builder.addGlobal(llvm_fn_name, global);
@@ -9470,19 +9644,14 @@ pub const FuncGen = struct {
const enum_ty = self.typeOf(un_op);
const llvm_fn = try self.getEnumTagNameFunction(enum_ty);
- const llvm_fn_ty = llvm_fn.typeOf(&o.builder);
- const params = [_]*llvm.Value{operand.toLlvm(&self.wip)};
- return (try self.wip.unimplemented(llvm_fn_ty.functionReturn(&o.builder), "")).finish(
- self.builder.buildCall(
- llvm_fn_ty.toLlvm(&o.builder),
- llvm_fn.toLlvm(&o.builder),
- ¶ms,
- params.len,
- .Fast,
- .Auto,
- "",
- ),
- &self.wip,
+ return self.wip.call(
+ .normal,
+ .fastcc,
+ .none,
+ llvm_fn.typeOf(&o.builder),
+ llvm_fn.toValue(&o.builder),
+ &.{operand},
+ "",
);
}
@@ -9499,16 +9668,19 @@ pub const FuncGen = struct {
const fqn = try mod.declPtr(enum_type.decl).getFullyQualifiedName(mod);
const llvm_fn_name = try o.builder.fmt("__zig_tag_name_{}", .{fqn.fmt(&mod.intern_pool)});
+ var attributes: Builder.FunctionAttributes.Wip = .{};
+ defer attributes.deinit(&o.builder);
+
const ret_ty = try o.lowerType(Type.slice_const_u8_sentinel_0);
const usize_ty = try o.lowerType(Type.usize);
const fn_type = try o.builder.fnType(ret_ty, &.{
try o.lowerType(enum_type.tag_ty.toType()),
}, .normal);
- const fn_val = o.llvm_module.addFunction(llvm_fn_name.toSlice(&o.builder).?, fn_type.toLlvm(&o.builder));
+ const fn_val = o.llvm_module.addFunction(llvm_fn_name.slice(&o.builder).?, fn_type.toLlvm(&o.builder));
fn_val.setLinkage(.Internal);
fn_val.setFunctionCallConv(.Fast);
- o.addCommonFnAttributes(fn_val);
+ try o.addCommonFnAttributes(&attributes, fn_val);
var global = Builder.Global{
.linkage = .internal,
@@ -9517,6 +9689,8 @@ pub const FuncGen = struct {
};
var function = Builder.Function{
.global = @enumFromInt(o.builder.globals.count()),
+ .call_conv = .fastcc,
+ .attributes = try attributes.finish(&o.builder),
};
try o.builder.llvm.globals.append(self.gpa, fn_val);
gop.value_ptr.* = try o.builder.addGlobal(llvm_fn_name, global);
@@ -9561,7 +9735,7 @@ pub const FuncGen = struct {
const slice_val = try o.builder.structValue(ret_ty, &.{
global_index.toConst(),
- try o.builder.intConst(usize_ty, name.toSlice(&o.builder).?.len),
+ try o.builder.intConst(usize_ty, name.slice(&o.builder).?.len),
});
const return_block = try wip.block(1, "Name");
@@ -9590,11 +9764,14 @@ pub const FuncGen = struct {
// Function signature: fn (anyerror) bool
const fn_type = try o.builder.fnType(.i1, &.{Builder.Type.err_int}, .normal);
- const llvm_fn = o.llvm_module.addFunction(name.toSlice(&o.builder).?, fn_type.toLlvm(&o.builder));
+ const llvm_fn = o.llvm_module.addFunction(name.slice(&o.builder).?, fn_type.toLlvm(&o.builder));
+
+ var attributes: Builder.FunctionAttributes.Wip = .{};
+ defer attributes.deinit(&o.builder);
llvm_fn.setLinkage(.Internal);
llvm_fn.setFunctionCallConv(.Fast);
- o.addCommonFnAttributes(llvm_fn);
+ try o.addCommonFnAttributes(&attributes, llvm_fn);
var global = Builder.Global{
.linkage = .internal,
@@ -9603,6 +9780,8 @@ pub const FuncGen = struct {
};
var function = Builder.Function{
.global = @enumFromInt(o.builder.globals.count()),
+ .call_conv = .fastcc,
+ .attributes = try attributes.finish(&o.builder),
};
try o.builder.llvm.globals.append(self.gpa, llvm_fn);
@@ -9731,18 +9910,14 @@ pub const FuncGen = struct {
// accum = f(accum, vec[i]);
const accum = try self.wip.load(.normal, llvm_result_ty, accum_ptr, .default, "");
const element = try self.wip.extractElement(operand_vector, i, "");
- const params = [2]*llvm.Value{ accum.toLlvm(&self.wip), element.toLlvm(&self.wip) };
- const new_accum = (try self.wip.unimplemented(llvm_result_ty, "")).finish(
- self.builder.buildCall(
- llvm_fn.typeOf(&o.builder).toLlvm(&o.builder),
- llvm_fn.toLlvm(&o.builder),
- ¶ms,
- params.len,
- .C,
- .Auto,
- "",
- ),
- &self.wip,
+ const new_accum = try self.wip.call(
+ .normal,
+ .ccc,
+ .none,
+ llvm_fn.typeOf(&o.builder),
+ llvm_fn.toValue(&o.builder),
+ &.{ accum, element },
+ "",
);
_ = try self.wip.store(.normal, new_accum, accum_ptr, .default);
@@ -10190,7 +10365,7 @@ pub const FuncGen = struct {
(try o.builder.intConst(.i32, prefetch.locality)).toLlvm(&o.builder),
(try o.builder.intConst(.i32, @intFromEnum(prefetch.cache))).toLlvm(&o.builder),
};
- _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall(
+ _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCallOld(
llvm_fn_ty.toLlvm(&o.builder),
fn_val,
¶ms,
@@ -10222,7 +10397,7 @@ pub const FuncGen = struct {
const args: [0]*llvm.Value = .{};
const llvm_fn = try self.getIntrinsic(llvm_fn_name, &.{});
- return (try self.wip.unimplemented(.i32, "")).finish(self.builder.buildCall(
+ return (try self.wip.unimplemented(.i32, "")).finish(self.builder.buildCallOld(
(try o.builder.fnType(.i32, &.{}, .normal)).toLlvm(&o.builder),
llvm_fn,
&args,
@@ -10252,12 +10427,15 @@ pub const FuncGen = struct {
const dimension = pl_op.payload;
if (dimension >= 3) return o.builder.intValue(.i32, 1);
+ var attributes: Builder.FunctionAttributes.Wip = .{};
+ defer attributes.deinit(&o.builder);
+
// Fetch the dispatch pointer, which points to this structure:
// https://github.com/RadeonOpenCompute/ROCR-Runtime/blob/adae6c61e10d371f7cbc3d0e94ae2c070cab18a4/src/inc/hsa.h#L2913
const llvm_fn = try self.getIntrinsic("llvm.amdgcn.dispatch.ptr", &.{});
const args: [0]*llvm.Value = .{};
const llvm_ret_ty = try o.builder.ptrType(Builder.AddrSpace.amdgpu.constant);
- const dispatch_ptr = (try self.wip.unimplemented(llvm_ret_ty, "")).finish(self.builder.buildCall(
+ const dispatch_ptr = (try self.wip.unimplemented(llvm_ret_ty, "")).finish(self.builder.buildCallOld(
(try o.builder.fnType(llvm_ret_ty, &.{}, .normal)).toLlvm(&o.builder),
llvm_fn,
&args,
@@ -10266,6 +10444,9 @@ pub const FuncGen = struct {
.Auto,
"",
), &self.wip);
+ try attributes.addRetAttr(.{
+ .@"align" = comptime Builder.Alignment.fromByteUnits(4),
+ }, &o.builder);
o.addAttrInt(dispatch_ptr.toLlvm(&self.wip), 0, "align", 4);
// Load the work_group_* member from the struct as u16.
@@ -10298,7 +10479,7 @@ pub const FuncGen = struct {
const undef_init = try o.builder.undefConst(.ptr); // TODO: Address space
const name = try o.builder.string("__zig_err_name_table");
- const error_name_table_global = o.llvm_module.addGlobal(Builder.Type.ptr.toLlvm(&o.builder), name.toSlice(&o.builder).?);
+ const error_name_table_global = o.llvm_module.addGlobal(Builder.Type.ptr.toLlvm(&o.builder), name.slice(&o.builder).?);
error_name_table_global.setInitializer(undef_init.toLlvm(&o.builder));
error_name_table_global.setLinkage(.Private);
error_name_table_global.setGlobalConstant(.True);
@@ -10751,7 +10932,7 @@ pub const FuncGen = struct {
);
const call = (try fg.wip.unimplemented(llvm_usize, "")).finish(
- fg.builder.buildCall(fn_llvm_ty, asm_fn, &args, args.len, .C, .Auto, ""),
+ fg.builder.buildCallOld(fn_llvm_ty, asm_fn, &args, args.len, .C, .Auto, ""),
&fg.wip,
);
return call;
@@ -10991,33 +11172,33 @@ fn toLlvmAtomicRmwBinOp(
};
}
-fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) llvm.CallConv {
+fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) Builder.CallConv {
return switch (cc) {
- .Unspecified, .Inline, .Async => .Fast,
- .C, .Naked => .C,
- .Stdcall => .X86_StdCall,
- .Fastcall => .X86_FastCall,
+ .Unspecified, .Inline, .Async => .fastcc,
+ .C, .Naked => .ccc,
+ .Stdcall => .x86_stdcallcc,
+ .Fastcall => .x86_fastcallcc,
.Vectorcall => return switch (target.cpu.arch) {
- .x86, .x86_64 => .X86_VectorCall,
- .aarch64, .aarch64_be, .aarch64_32 => .AArch64_VectorCall,
+ .x86, .x86_64 => .x86_vectorcallcc,
+ .aarch64, .aarch64_be, .aarch64_32 => .aarch64_vector_pcs,
else => unreachable,
},
- .Thiscall => .X86_ThisCall,
- .APCS => .ARM_APCS,
- .AAPCS => .ARM_AAPCS,
- .AAPCSVFP => .ARM_AAPCS_VFP,
+ .Thiscall => .x86_thiscallcc,
+ .APCS => .arm_apcscc,
+ .AAPCS => .arm_aapcscc,
+ .AAPCSVFP => .arm_aapcs_vfpcc,
.Interrupt => return switch (target.cpu.arch) {
- .x86, .x86_64 => .X86_INTR,
- .avr => .AVR_INTR,
- .msp430 => .MSP430_INTR,
+ .x86, .x86_64 => .x86_intrcc,
+ .avr => .avr_intrcc,
+ .msp430 => .msp430_intrcc,
else => unreachable,
},
- .Signal => .AVR_SIGNAL,
- .SysV => .X86_64_SysV,
- .Win64 => .Win64,
+ .Signal => .avr_signalcc,
+ .SysV => .x86_64_sysvcc,
+ .Win64 => .win64cc,
.Kernel => return switch (target.cpu.arch) {
- .nvptx, .nvptx64 => .PTX_Kernel,
- .amdgcn => .AMDGPU_KERNEL,
+ .nvptx, .nvptx64 => .ptx_kernel,
+ .amdgcn => .amdgpu_kernel,
else => unreachable,
},
};
src/zig_llvm.cpp
@@ -453,6 +453,10 @@ LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
return wrap(call_inst);
}
+ZIG_EXTERN_C void ZigLLVMSetTailCallKind(LLVMValueRef Call, CallInst::TailCallKind TailCallKind) {
+ unwrap<CallInst>(Call)->setTailCallKind(TailCallKind);
+}
+
void ZigLLVMAddAttributeAtIndex(LLVMValueRef Val, unsigned Idx, LLVMAttributeRef A) {
if (isa<Function>(unwrap(Val))) {
unwrap<Function>(Val)->addAttributeAtIndex(Idx, unwrap(A));
@@ -461,7 +465,6 @@ void ZigLLVMAddAttributeAtIndex(LLVMValueRef Val, unsigned Idx, LLVMAttributeRef
}
}
-
LLVMValueRef ZigLLVMBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign,
LLVMValueRef Src, unsigned SrcAlign, LLVMValueRef Size, bool isVolatile)
{
@@ -1116,11 +1119,6 @@ void ZigLLVMAddFunctionAttr(LLVMValueRef fn_ref, const char *attr_name, const ch
func->addFnAttr(attr_name, attr_value);
}
-void ZigLLVMAddFunctionAttrCold(LLVMValueRef fn_ref) {
- Function *func = unwrap<Function>(fn_ref);
- func->addFnAttr(Attribute::Cold);
-}
-
void ZigLLVMParseCommandLineOptions(size_t argc, const char *const *argv) {
cl::ParseCommandLineOptions(argc, argv);
}