llvm: finish converting globals

This commit is contained in:
Jacob Young 2023-08-08 10:15:11 -04:00
parent 2499d8fb73
commit 2bdd180c6f
5 changed files with 984 additions and 788 deletions

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@ llvm: if (build_options.have_llvm) struct {
types: std.ArrayListUnmanaged(*llvm.Type),
globals: std.ArrayListUnmanaged(*llvm.Value),
constants: std.ArrayListUnmanaged(*llvm.Value),
replacements: std.AutoHashMapUnmanaged(*llvm.Value, Global.Index),
} else void,
source_filename: String,
@ -1709,17 +1710,17 @@ pub const FunctionAttributes = enum(u32) {
};
pub const Linkage = enum {
external,
private,
internal,
available_externally,
linkonce,
weak,
common,
appending,
extern_weak,
linkonce_odr,
weak_odr,
linkonce,
linkonce_odr,
available_externally,
appending,
common,
extern_weak,
external,
pub fn format(
self: Linkage,
@ -1729,6 +1730,22 @@ pub const Linkage = enum {
) @TypeOf(writer).Error!void {
if (self != .external) try writer.print(" {s}", .{@tagName(self)});
}
fn toLlvm(self: Linkage) llvm.Linkage {
return switch (self) {
.private => .Private,
.internal => .Internal,
.weak => .WeakAny,
.weak_odr => .WeakODR,
.linkonce => .LinkOnceAny,
.linkonce_odr => .LinkOnceODR,
.available_externally => .AvailableExternally,
.appending => .Appending,
.common => .Common,
.extern_weak => .ExternalWeak,
.external => .External,
};
}
};
pub const Preemption = enum {
@ -1759,6 +1776,14 @@ pub const Visibility = enum {
) @TypeOf(writer).Error!void {
if (self != .default) try writer.print(" {s}", .{@tagName(self)});
}
fn toLlvm(self: Visibility) llvm.Visibility {
return switch (self) {
.default => .Default,
.hidden => .Hidden,
.protected => .Protected,
};
}
};
pub const DllStorageClass = enum {
@ -1774,6 +1799,14 @@ pub const DllStorageClass = enum {
) @TypeOf(writer).Error!void {
if (self != .default) try writer.print(" {s}", .{@tagName(self)});
}
fn toLlvm(self: DllStorageClass) llvm.DLLStorageClass {
return switch (self) {
.default => .Default,
.dllimport => .DLLImport,
.dllexport => .DLLExport,
};
}
};
pub const ThreadLocal = enum {
@ -1785,20 +1818,28 @@ pub const ThreadLocal = enum {
pub fn format(
self: ThreadLocal,
comptime _: []const u8,
comptime prefix: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) @TypeOf(writer).Error!void {
if (self == .default) return;
try writer.writeAll(" thread_local");
if (self != .generaldynamic) {
try writer.writeByte('(');
try writer.writeAll(@tagName(self));
try writer.writeByte(')');
}
try writer.print("{s}thread_local", .{prefix});
if (self != .generaldynamic) try writer.print("({s})", .{@tagName(self)});
}
fn toLlvm(self: ThreadLocal) llvm.ThreadLocalMode {
return switch (self) {
.default => .NotThreadLocal,
.generaldynamic => .GeneralDynamicTLSModel,
.localdynamic => .LocalDynamicTLSModel,
.initialexec => .InitialExecTLSModel,
.localexec => .LocalExecTLSModel,
};
}
};
pub const Mutability = enum { global, constant };
pub const UnnamedAddr = enum {
default,
unnamed_addr,
@ -2057,6 +2098,11 @@ pub const CallConv = enum(u10) {
_ => try writer.print(" cc{d}", .{@intFromEnum(self)}),
}
}
fn toLlvm(self: CallConv) llvm.CallConv {
// These enum values appear in LLVM IR, and so are guaranteed to be stable.
return @enumFromInt(@intFromEnum(self));
}
};
pub const Global = struct {
@ -2093,10 +2139,6 @@ pub const Global = struct {
return self.unwrap(builder) == other.unwrap(builder);
}
pub fn name(self: Index, builder: *const Builder) String {
return builder.globals.keys()[@intFromEnum(self.unwrap(builder))];
}
pub fn ptr(self: Index, builder: *Builder) *Global {
return &builder.globals.values()[@intFromEnum(self.unwrap(builder))];
}
@ -2105,6 +2147,10 @@ pub const Global = struct {
return &builder.globals.values()[@intFromEnum(self.unwrap(builder))];
}
pub fn name(self: Index, builder: *const Builder) String {
return builder.globals.keys()[@intFromEnum(self.unwrap(builder))];
}
pub fn typeOf(self: Index, builder: *const Builder) Type {
return self.ptrConst(builder).type;
}
@ -2113,6 +2159,30 @@ pub const Global = struct {
return @enumFromInt(@intFromEnum(Constant.first_global) + @intFromEnum(self));
}
pub fn setLinkage(self: Index, linkage: Linkage, builder: *Builder) void {
if (builder.useLibLlvm()) self.toLlvm(builder).setLinkage(linkage.toLlvm());
self.ptr(builder).linkage = linkage;
self.updateDsoLocal(builder);
}
pub fn setVisibility(self: Index, visibility: Visibility, builder: *Builder) void {
if (builder.useLibLlvm()) self.toLlvm(builder).setVisibility(visibility.toLlvm());
self.ptr(builder).visibility = visibility;
self.updateDsoLocal(builder);
}
pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void {
if (builder.useLibLlvm()) self.toLlvm(builder).setDLLStorageClass(class.toLlvm());
self.ptr(builder).dll_storage_class = class;
}
pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void {
if (builder.useLibLlvm()) self.toLlvm(builder).setUnnamedAddr(
llvm.Bool.fromBool(unnamed_addr != .default),
);
self.ptr(builder).unnamed_addr = unnamed_addr;
}
pub fn toLlvm(self: Index, builder: *const Builder) *llvm.Value {
assert(builder.useLibLlvm());
return builder.llvm.globals.items[@intFromEnum(self.unwrap(builder))];
@ -2148,9 +2218,36 @@ pub const Global = struct {
pub fn replace(self: Index, other: Index, builder: *Builder) Allocator.Error!void {
try builder.ensureUnusedGlobalCapacity(.empty);
if (builder.useLibLlvm())
try builder.llvm.replacements.ensureUnusedCapacity(builder.gpa, 1);
self.replaceAssumeCapacity(other, builder);
}
pub fn delete(self: Index, builder: *Builder) void {
if (builder.useLibLlvm()) self.toLlvm(builder).eraseGlobalValue();
self.ptr(builder).kind = .{ .replaced = .none };
}
fn updateDsoLocal(self: Index, builder: *Builder) void {
const self_ptr = self.ptr(builder);
switch (self_ptr.linkage) {
.private, .internal => {
self_ptr.visibility = .default;
self_ptr.dll_storage_class = .default;
self_ptr.preemption = .implicit_dso_local;
},
.extern_weak => if (self_ptr.preemption == .implicit_dso_local) {
self_ptr.preemption = .dso_local;
},
else => switch (self_ptr.visibility) {
.default => if (self_ptr.preemption == .implicit_dso_local) {
self_ptr.preemption = .dso_local;
},
else => self_ptr.preemption = .implicit_dso_local,
},
}
}
fn renameAssumeCapacity(self: Index, new_name: String, builder: *Builder) void {
const old_name = self.name(builder);
if (new_name == old_name) return;
@ -2187,13 +2284,8 @@ pub const Global = struct {
if (builder.useLibLlvm()) {
const self_llvm = self.toLlvm(builder);
self_llvm.replaceAllUsesWith(other.toLlvm(builder));
switch (self.ptr(builder).kind) {
.alias,
.variable,
=> self_llvm.deleteGlobal(),
.function => self_llvm.deleteFunction(),
.replaced => unreachable,
}
self_llvm.removeGlobalValue();
builder.llvm.replacements.putAssumeCapacityNoClobber(self_llvm, other);
}
self.ptr(builder).kind = .{ .replaced = other.unwrap(builder) };
}
@ -2205,42 +2297,17 @@ pub const Global = struct {
};
}
};
pub fn updateAttributes(self: *Global) void {
switch (self.linkage) {
.private, .internal => {
self.visibility = .default;
self.dll_storage_class = .default;
self.preemption = .implicit_dso_local;
},
.extern_weak => if (self.preemption == .implicit_dso_local) {
self.preemption = .dso_local;
},
else => switch (self.visibility) {
.default => if (self.preemption == .implicit_dso_local) {
self.preemption = .dso_local;
},
else => self.preemption = .implicit_dso_local,
},
}
}
};
pub const Alias = struct {
global: Global.Index,
thread_local: ThreadLocal = .default,
init: Constant = .no_init,
aliasee: Constant = .no_init,
pub const Index = enum(u32) {
none = std.math.maxInt(u32),
_,
pub fn getAliasee(self: Index, builder: *const Builder) Global.Index {
const aliasee = self.ptrConst(builder).init.getBase(builder);
assert(aliasee != .none);
return aliasee;
}
pub fn ptr(self: Index, builder: *Builder) *Alias {
return &builder.aliases.items[@intFromEnum(self)];
}
@ -2249,6 +2316,10 @@ pub const Alias = struct {
return &builder.aliases.items[@intFromEnum(self)];
}
pub fn name(self: Index, builder: *const Builder) String {
return self.ptrConst(builder).global.name(builder);
}
pub fn typeOf(self: Index, builder: *const Builder) Type {
return self.ptrConst(builder).global.typeOf(builder);
}
@ -2261,7 +2332,18 @@ pub const Alias = struct {
return self.toConst(builder).toValue();
}
pub fn toLlvm(self: Index, builder: *const Builder) *llvm.Value {
pub fn getAliasee(self: Index, builder: *const Builder) Global.Index {
const aliasee = self.ptrConst(builder).aliasee.getBase(builder);
assert(aliasee != .none);
return aliasee;
}
pub fn setAliasee(self: Index, aliasee: Constant, builder: *Builder) void {
if (builder.useLibLlvm()) self.toLlvm(builder).setAliasee(aliasee.toLlvm(builder));
self.ptr(builder).aliasee = aliasee;
}
fn toLlvm(self: Index, builder: *const Builder) *llvm.Value {
return self.ptrConst(builder).global.toLlvm(builder);
}
};
@ -2270,7 +2352,7 @@ pub const Alias = struct {
pub const Variable = struct {
global: Global.Index,
thread_local: ThreadLocal = .default,
mutability: enum { global, constant } = .global,
mutability: Mutability = .global,
init: Constant = .no_init,
section: String = .none,
alignment: Alignment = .default,
@ -2287,6 +2369,10 @@ pub const Variable = struct {
return &builder.variables.items[@intFromEnum(self)];
}
pub fn name(self: Index, builder: *const Builder) String {
return self.ptrConst(builder).global.name(builder);
}
pub fn typeOf(self: Index, builder: *const Builder) Type {
return self.ptrConst(builder).global.typeOf(builder);
}
@ -2299,6 +2385,88 @@ pub const Variable = struct {
return self.toConst(builder).toValue();
}
pub fn setLinkage(self: Index, linkage: Linkage, builder: *Builder) void {
return self.ptrConst(builder).global.setLinkage(linkage, builder);
}
pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void {
return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder);
}
pub fn setThreadLocal(self: Index, thread_local: ThreadLocal, builder: *Builder) void {
if (builder.useLibLlvm()) self.toLlvm(builder).setThreadLocalMode(thread_local.toLlvm());
self.ptr(builder).thread_local = thread_local;
}
pub fn setMutability(self: Index, mutability: Mutability, builder: *Builder) void {
if (builder.useLibLlvm()) self.toLlvm(builder).setGlobalConstant(
llvm.Bool.fromBool(mutability == .constant),
);
self.ptr(builder).mutability = mutability;
}
pub fn setInitializer(
self: Index,
initializer: Constant,
builder: *Builder,
) Allocator.Error!void {
if (initializer != .no_init) {
const variable = self.ptrConst(builder);
const global = variable.global.ptr(builder);
const initializer_type = initializer.typeOf(builder);
if (builder.useLibLlvm() and global.type != initializer_type) {
try builder.llvm.replacements.ensureUnusedCapacity(builder.gpa, 1);
// LLVM does not allow us to change the type of globals. So we must
// create a new global with the correct type, copy all its attributes,
// and then update all references to point to the new global,
// delete the original, and rename the new one to the old one's name.
// This is necessary because LLVM does not support const bitcasting
// a struct with padding bytes, which is needed to lower a const union value
// to LLVM, when a field other than the most-aligned is active. Instead,
// we must lower to an unnamed struct, and pointer cast at usage sites
// of the global. Such an unnamed struct is the cause of the global type
// mismatch, because we don't have the LLVM type until the *value* is created,
// whereas the global needs to be created based on the type alone, because
// lowering the value may reference the global as a pointer.
// Related: https://github.com/ziglang/zig/issues/13265
const old_global = &builder.llvm.globals.items[@intFromEnum(variable.global)];
const new_global = builder.llvm.module.?.addGlobalInAddressSpace(
initializer_type.toLlvm(builder),
"",
@intFromEnum(global.addr_space),
);
new_global.setLinkage(global.linkage.toLlvm());
new_global.setUnnamedAddr(llvm.Bool.fromBool(global.unnamed_addr != .default));
new_global.setAlignment(@intCast(variable.alignment.toByteUnits() orelse 0));
if (variable.section != .none)
new_global.setSection(variable.section.slice(builder).?);
old_global.*.replaceAllUsesWith(new_global);
builder.llvm.replacements.putAssumeCapacityNoClobber(old_global.*, variable.global);
new_global.takeName(old_global.*);
old_global.*.removeGlobalValue();
old_global.* = new_global;
self.ptr(builder).mutability = .global;
}
global.type = initializer_type;
}
if (builder.useLibLlvm()) self.toLlvm(builder).setInitializer(switch (initializer) {
.no_init => null,
else => initializer.toLlvm(builder),
});
self.ptr(builder).init = initializer;
}
pub fn setSection(self: Index, section: String, builder: *Builder) void {
if (builder.useLibLlvm()) self.toLlvm(builder).setSection(section.slice(builder).?);
self.ptr(builder).section = section;
}
pub fn setAlignment(self: Index, alignment: Alignment, builder: *Builder) void {
if (builder.useLibLlvm())
self.toLlvm(builder).setAlignment(@intCast(alignment.toByteUnits() orelse 0));
self.ptr(builder).alignment = alignment;
}
pub fn toLlvm(self: Index, builder: *const Builder) *llvm.Value {
return self.ptrConst(builder).global.toLlvm(builder);
}
@ -3640,6 +3808,10 @@ pub const Function = struct {
return &builder.functions.items[@intFromEnum(self)];
}
pub fn name(self: Index, builder: *const Builder) String {
return self.ptrConst(builder).global.name(builder);
}
pub fn typeOf(self: Index, builder: *const Builder) Type {
return self.ptrConst(builder).global.typeOf(builder);
}
@ -3652,6 +3824,19 @@ pub const Function = struct {
return self.toConst(builder).toValue();
}
pub fn setLinkage(self: Index, linkage: Linkage, builder: *Builder) void {
return self.ptrConst(builder).global.setLinkage(linkage, builder);
}
pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void {
return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder);
}
pub fn setCallConv(self: Index, call_conv: CallConv, builder: *Builder) void {
if (builder.useLibLlvm()) self.toLlvm(builder).setFunctionCallConv(call_conv.toLlvm());
self.ptr(builder).call_conv = call_conv;
}
pub fn setAttributes(
self: Index,
new_function_attributes: FunctionAttributes,
@ -3687,12 +3872,12 @@ pub const Function = struct {
)) {
.lt => {
// Removed
if (old_attribute_kind.toString()) |name| {
const slice = name.slice(builder).?;
if (old_attribute_kind.toString()) |attribute_name| {
const attribute_name_slice = attribute_name.slice(builder).?;
llvm_function.removeStringAttributeAtIndex(
llvm_attribute_index,
slice.ptr,
@intCast(slice.len),
attribute_name_slice.ptr,
@intCast(attribute_name_slice.len),
);
} else {
const llvm_kind_id = old_attribute_kind.toLlvm(builder).*;
@ -3732,6 +3917,17 @@ pub const Function = struct {
self.ptr(builder).attributes = new_function_attributes;
}
pub fn setSection(self: Index, section: String, builder: *Builder) void {
if (builder.useLibLlvm()) self.toLlvm(builder).setSection(section.slice(builder).?);
self.ptr(builder).section = section;
}
pub fn setAlignment(self: Index, alignment: Alignment, builder: *Builder) void {
if (builder.useLibLlvm())
self.toLlvm(builder).setAlignment(@intCast(alignment.toByteUnits() orelse 0));
self.ptr(builder).alignment = alignment;
}
pub fn toLlvm(self: Index, builder: *const Builder) *llvm.Value {
return self.ptrConst(builder).global.toLlvm(builder);
}
@ -4342,9 +4538,11 @@ pub const Function = struct {
return .{ .data = .{ .instruction = self, .function = function, .builder = builder } };
}
pub fn toLlvm(self: Instruction.Index, wip: *const WipFunction) *llvm.Value {
fn toLlvm(self: Instruction.Index, wip: *const WipFunction) *llvm.Value {
assert(wip.builder.useLibLlvm());
return wip.llvm.instructions.items[@intFromEnum(self)];
const llvm_value = wip.llvm.instructions.items[@intFromEnum(self)];
const global = wip.builder.llvm.replacements.get(llvm_value) orelse return llvm_value;
return global.toLlvm(wip.builder);
}
fn llvmName(self: Instruction.Index, wip: *const WipFunction) [:0]const u8 {
@ -4462,6 +4660,27 @@ pub const Function = struct {
fmax,
fmin,
none = std.math.maxInt(u5),
fn toLlvm(self: Operation) llvm.AtomicRMWBinOp {
return switch (self) {
.xchg => .Xchg,
.add => .Add,
.sub => .Sub,
.@"and" => .And,
.nand => .Nand,
.@"or" => .Or,
.xor => .Xor,
.max => .Max,
.min => .Min,
.umax => .UMax,
.umin => .UMin,
.fadd => .FAdd,
.fsub => .FSub,
.fmax => .FMax,
.fmin => .FMin,
.none => unreachable,
};
}
};
};
@ -5245,7 +5464,7 @@ pub const WipFunction = struct {
instruction.llvmName(self),
);
if (access_kind == .@"volatile") llvm_instruction.setVolatile(.True);
if (ordering != .none) llvm_instruction.setOrdering(@enumFromInt(@intFromEnum(ordering)));
if (ordering != .none) llvm_instruction.setOrdering(ordering.toLlvm());
if (alignment.toByteUnits()) |bytes| llvm_instruction.setAlignment(@intCast(bytes));
self.llvm.instructions.appendAssumeCapacity(llvm_instruction);
}
@ -5295,7 +5514,7 @@ pub const WipFunction = struct {
if (self.builder.useLibLlvm()) {
const llvm_instruction = self.llvm.builder.buildStore(val.toLlvm(self), ptr.toLlvm(self));
if (access_kind == .@"volatile") llvm_instruction.setVolatile(.True);
if (ordering != .none) llvm_instruction.setOrdering(@enumFromInt(@intFromEnum(ordering)));
if (ordering != .none) llvm_instruction.setOrdering(ordering.toLlvm());
if (alignment.toByteUnits()) |bytes| llvm_instruction.setAlignment(@intCast(bytes));
self.llvm.instructions.appendAssumeCapacity(llvm_instruction);
}
@ -5318,7 +5537,7 @@ pub const WipFunction = struct {
});
if (self.builder.useLibLlvm()) self.llvm.instructions.appendAssumeCapacity(
self.llvm.builder.buildFence(
@enumFromInt(@intFromEnum(ordering)),
ordering.toLlvm(),
llvm.Bool.fromBool(sync_scope == .singlethread),
"",
),
@ -5370,8 +5589,8 @@ pub const WipFunction = struct {
ptr.toLlvm(self),
cmp.toLlvm(self),
new.toLlvm(self),
@enumFromInt(@intFromEnum(success_ordering)),
@enumFromInt(@intFromEnum(failure_ordering)),
success_ordering.toLlvm(),
failure_ordering.toLlvm(),
llvm.Bool.fromBool(sync_scope == .singlethread),
);
if (kind == .weak) llvm_instruction.setWeak(.True);
@ -5418,10 +5637,10 @@ pub const WipFunction = struct {
});
if (self.builder.useLibLlvm()) {
const llvm_instruction = self.llvm.builder.buildAtomicRmw(
@enumFromInt(@intFromEnum(operation)),
operation.toLlvm(),
ptr.toLlvm(self),
val.toLlvm(self),
@enumFromInt(@intFromEnum(ordering)),
ordering.toLlvm(),
llvm.Bool.fromBool(sync_scope == .singlethread),
);
if (access_kind == .@"volatile") llvm_instruction.setVolatile(.True);
@ -5608,25 +5827,19 @@ pub const WipFunction = struct {
pub fn fcmp(
self: *WipFunction,
fast: FastMathKind,
cond: FloatCondition,
lhs: Value,
rhs: Value,
name: []const u8,
) Allocator.Error!Value {
return self.cmpTag(switch (cond) {
inline else => |tag| @field(Instruction.Tag, "fcmp " ++ @tagName(tag)),
}, @intFromEnum(cond), lhs, rhs, name);
}
pub fn fcmpFast(
self: *WipFunction,
cond: FloatCondition,
lhs: Value,
rhs: Value,
name: []const u8,
) Allocator.Error!Value {
return self.cmpTag(switch (cond) {
inline else => |tag| @field(Instruction.Tag, "fcmp fast " ++ @tagName(tag)),
return self.cmpTag(switch (fast) {
inline else => |fast_tag| switch (cond) {
inline else => |cond_tag| @field(Instruction.Tag, "fcmp " ++ switch (fast_tag) {
.normal => "",
.fast => "fast ",
} ++ @tagName(cond_tag)),
},
}, @intFromEnum(cond), lhs, rhs, name);
}
@ -5684,22 +5897,16 @@ pub const WipFunction = struct {
pub fn select(
self: *WipFunction,
fast: FastMathKind,
cond: Value,
lhs: Value,
rhs: Value,
name: []const u8,
) Allocator.Error!Value {
return self.selectTag(.select, cond, lhs, rhs, name);
}
pub fn selectFast(
self: *WipFunction,
cond: Value,
lhs: Value,
rhs: Value,
name: []const u8,
) Allocator.Error!Value {
return self.selectTag(.@"select fast", cond, lhs, rhs, name);
return self.selectTag(switch (fast) {
.normal => .select,
.fast => .@"select fast",
}, cond, lhs, rhs, name);
}
pub fn call(
@ -5774,7 +5981,7 @@ pub const WipFunction = struct {
else => instruction.llvmName(self),
},
);
llvm_instruction.setInstructionCallConv(@enumFromInt(@intFromEnum(call_conv)));
llvm_instruction.setInstructionCallConv(call_conv.toLlvm());
llvm_instruction.setTailCallKind(switch (kind) {
.normal, .fast => .None,
.musttail, .musttail_fast => .MustTail,
@ -5808,6 +6015,7 @@ pub const WipFunction = struct {
pub fn callIntrinsic(
self: *WipFunction,
fast: FastMathKind,
function_attributes: FunctionAttributes,
id: Intrinsic,
overload: []const Type,
@ -5816,7 +6024,7 @@ pub const WipFunction = struct {
) Allocator.Error!Value {
const intrinsic = try self.builder.getIntrinsic(id, overload);
return self.call(
.normal,
fast.toCallKind(),
CallConv.default,
function_attributes,
intrinsic.typeOf(self.builder),
@ -5838,6 +6046,7 @@ pub const WipFunction = struct {
var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = dst_align })};
var src_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = src_align })};
const value = try self.callIntrinsic(
.normal,
try self.builder.fnAttrs(&.{
.none,
.none,
@ -5865,6 +6074,7 @@ pub const WipFunction = struct {
) Allocator.Error!Instruction.Index {
var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = dst_align })};
const value = try self.callIntrinsic(
.normal,
try self.builder.fnAttrs(&.{ .none, .none, try self.builder.attrs(&dst_attrs) }),
.memset,
&.{ dst.typeOfWip(self), len.typeOfWip(self) },
@ -6740,6 +6950,24 @@ pub const FloatCondition = enum(u4) {
ult = 12,
ule = 13,
une = 14,
fn toLlvm(self: FloatCondition) llvm.RealPredicate {
return switch (self) {
.oeq => .OEQ,
.ogt => .OGT,
.oge => .OGE,
.olt => .OLT,
.ole => .OLE,
.one => .ONE,
.ord => .ORD,
.uno => .UNO,
.ueq => .UEQ,
.ugt => .UGT,
.uge => .UGE,
.ult => .ULT,
.uno => .UNE,
};
}
};
pub const IntegerCondition = enum(u6) {
@ -6753,6 +6981,20 @@ pub const IntegerCondition = enum(u6) {
sge = 39,
slt = 40,
sle = 41,
fn toLlvm(self: IntegerCondition) llvm.IntPredicate {
return switch (self) {
.eq => .EQ,
.ne => .NE,
.ugt => .UGT,
.uge => .UGE,
.ult => .ULT,
.sgt => .SGT,
.sge => .SGE,
.slt => .SLT,
.sle => .SLE,
};
}
};
pub const MemoryAccessKind = enum(u1) {
@ -6802,6 +7044,18 @@ pub const AtomicOrdering = enum(u3) {
) @TypeOf(writer).Error!void {
if (self != .none) try writer.print("{s}{s}", .{ prefix, @tagName(self) });
}
fn toLlvm(self: AtomicOrdering) llvm.AtomicOrdering {
return switch (self) {
.none => .NotAtomic,
.unordered => .Unordered,
.monotonic => .Monotonic,
.acquire => .Acquire,
.release => .Release,
.acq_rel => .AcquireRelease,
.seq_cst => .SequentiallyConsistent,
};
}
};
const MemoryAccessInfo = packed struct(u32) {
@ -6834,6 +7088,18 @@ pub const FastMath = packed struct(u32) {
};
};
pub const FastMathKind = enum {
normal,
fast,
pub fn toCallKind(self: FastMathKind) Function.Instruction.Call.Kind {
return switch (self) {
.normal => .normal,
.fast => .fast,
};
}
};
pub const Constant = enum(u32) {
false,
true,
@ -7247,7 +7513,7 @@ pub const Constant = enum(u32) {
}
},
.global => |global| switch (global.ptrConst(builder).kind) {
.alias => |alias| cur = alias.ptrConst(builder).init,
.alias => |alias| cur = alias.ptrConst(builder).aliasee,
.variable, .function => return global,
.replaced => unreachable,
},
@ -7586,10 +7852,12 @@ pub const Constant = enum(u32) {
pub fn toLlvm(self: Constant, builder: *const Builder) *llvm.Value {
assert(builder.useLibLlvm());
return switch (self.unwrap()) {
const llvm_value = switch (self.unwrap()) {
.constant => |constant| builder.llvm.constants.items[constant],
.global => |global| global.toLlvm(builder),
.global => |global| return global.toLlvm(builder),
};
const global = builder.llvm.replacements.get(llvm_value) orelse return llvm_value;
return global.toLlvm(builder);
}
};
@ -7726,6 +7994,7 @@ pub fn init(options: Options) InitError!Builder {
.types = .{},
.globals = .{},
.constants = .{},
.replacements = .{},
};
errdefer self.deinit();
@ -7805,6 +8074,20 @@ pub fn init(options: Options) InitError!Builder {
}
pub fn deinit(self: *Builder) void {
if (self.useLibLlvm()) {
var replacement_it = self.llvm.replacements.keyIterator();
while (replacement_it.next()) |replacement| replacement.*.deleteGlobalValue();
self.llvm.replacements.deinit(self.gpa);
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();
}
self.module_asm.deinit(self.gpa);
self.string_map.deinit(self.gpa);
@ -7834,16 +8117,6 @@ pub fn deinit(self: *Builder) void {
self.constant_extra.deinit(self.gpa);
self.constant_limbs.deinit(self.gpa);
if (self.useLibLlvm()) {
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();
}
self.* = undefined;
}
@ -8300,10 +8573,10 @@ pub fn addGlobalAssumeCapacity(self: *Builder, name: String, global: Global) Glo
const global_gop = self.globals.getOrPutAssumeCapacity(id);
if (!global_gop.found_existing) {
global_gop.value_ptr.* = global;
global_gop.value_ptr.updateAttributes();
const index: Global.Index = @enumFromInt(global_gop.index);
index.updateName(self);
return index;
const global_index: Global.Index = @enumFromInt(global_gop.index);
global_index.updateDsoLocal(self);
global_index.updateName(self);
return global_index;
}
const unique_gop = self.next_unique_global_id.getOrPutAssumeCapacity(name);
@ -8317,21 +8590,107 @@ pub fn getGlobal(self: *const Builder, name: String) ?Global.Index {
return @enumFromInt(self.globals.getIndex(name) orelse return null);
}
pub fn addFunction(self: *Builder, ty: Type, name: String) Allocator.Error!Function.Index {
pub fn addAlias(
self: *Builder,
name: String,
ty: Type,
addr_space: AddrSpace,
aliasee: Constant,
) Allocator.Error!Alias.Index {
assert(!name.isAnon());
try self.ensureUnusedTypeCapacity(1, NoExtra, 0);
try self.ensureUnusedGlobalCapacity(name);
try self.aliases.ensureUnusedCapacity(self.gpa, 1);
return self.addAliasAssumeCapacity(name, ty, addr_space, aliasee);
}
pub fn addAliasAssumeCapacity(
self: *Builder,
name: String,
ty: Type,
addr_space: AddrSpace,
aliasee: Constant,
) Alias.Index {
if (self.useLibLlvm()) self.llvm.globals.appendAssumeCapacity(self.llvm.module.?.addAlias(
ty.toLlvm(self),
@intFromEnum(addr_space),
aliasee.toLlvm(self),
name.slice(self).?,
));
const alias_index: Alias.Index = @enumFromInt(self.aliases.items.len);
self.aliases.appendAssumeCapacity(.{ .global = self.addGlobalAssumeCapacity(name, .{
.addr_space = addr_space,
.type = ty,
.kind = .{ .alias = alias_index },
}), .aliasee = aliasee });
return alias_index;
}
pub fn addVariable(
self: *Builder,
name: String,
ty: Type,
addr_space: AddrSpace,
) Allocator.Error!Variable.Index {
assert(!name.isAnon());
try self.ensureUnusedTypeCapacity(1, NoExtra, 0);
try self.ensureUnusedGlobalCapacity(name);
try self.variables.ensureUnusedCapacity(self.gpa, 1);
return self.addVariableAssumeCapacity(ty, name, addr_space);
}
pub fn addVariableAssumeCapacity(
self: *Builder,
ty: Type,
name: String,
addr_space: AddrSpace,
) Variable.Index {
if (self.useLibLlvm()) self.llvm.globals.appendAssumeCapacity(
self.llvm.module.?.addGlobalInAddressSpace(
ty.toLlvm(self),
name.slice(self).?,
@intFromEnum(addr_space),
),
);
const variable_index: Variable.Index = @enumFromInt(self.variables.items.len);
self.variables.appendAssumeCapacity(.{ .global = self.addGlobalAssumeCapacity(name, .{
.addr_space = addr_space,
.type = ty,
.kind = .{ .variable = variable_index },
}) });
return variable_index;
}
pub fn addFunction(
self: *Builder,
ty: Type,
name: String,
addr_space: AddrSpace,
) Allocator.Error!Function.Index {
assert(!name.isAnon());
try self.ensureUnusedTypeCapacity(1, NoExtra, 0);
try self.ensureUnusedGlobalCapacity(name);
try self.functions.ensureUnusedCapacity(self.gpa, 1);
return self.addFunctionAssumeCapacity(ty, name);
return self.addFunctionAssumeCapacity(ty, name, addr_space);
}
pub fn addFunctionAssumeCapacity(self: *Builder, ty: Type, name: String) Function.Index {
pub fn addFunctionAssumeCapacity(
self: *Builder,
ty: Type,
name: String,
addr_space: AddrSpace,
) Function.Index {
assert(ty.isFunction(self));
if (self.useLibLlvm()) self.llvm.globals.appendAssumeCapacity(
self.llvm.module.?.addFunction(name.slice(self).?, ty.toLlvm(self)),
self.llvm.module.?.addFunctionInAddressSpace(
name.slice(self).?,
ty.toLlvm(self),
@intFromEnum(addr_space),
),
);
const function_index: Function.Index = @enumFromInt(self.functions.items.len);
self.functions.appendAssumeCapacity(.{ .global = self.addGlobalAssumeCapacity(name, .{
.addr_space = addr_space,
.type = ty,
.kind = .{ .function = function_index },
}) });
@ -8423,12 +8782,11 @@ pub fn getIntrinsic(
};
}
const function_index =
try self.addFunction(try self.fnType(switch (signature.ret_len) {
const function_index = try self.addFunction(try self.fnType(switch (signature.ret_len) {
0 => .void,
1 => param_types[0],
else => try self.structType(.normal, param_types[0..signature.ret_len]),
}, param_types[signature.ret_len..], .normal), name);
}, param_types[signature.ret_len..], .normal), name, .default);
function_index.ptr(self).attributes = try self.fnAttrs(function_attributes);
return function_index;
}
@ -8889,6 +9247,40 @@ pub fn asmValue(
return (try self.asmConst(ty, info, assembly, constraints)).toValue();
}
pub fn verify(self: *Builder) error{}!bool {
if (self.useLibLlvm()) {
var error_message: [*:0]const u8 = undefined;
// verifyModule always allocs the error_message even if there is no error
defer llvm.disposeMessage(error_message);
if (self.llvm.module.?.verify(.ReturnStatus, &error_message).toBool()) {
log.err("failed verification of LLVM module:\n{s}\n", .{error_message});
return false;
}
}
return true;
}
pub fn writeBitcodeToFile(self: *Builder, path: []const u8) Allocator.Error!bool {
const path_z = try self.gpa.dupeZ(u8, path);
defer self.gpa.free(path_z);
return self.writeBitcodeToFileZ(path_z);
}
pub fn writeBitcodeToFileZ(self: *Builder, path: [*:0]const u8) bool {
if (self.useLibLlvm()) {
const error_code = self.llvm.module.?.writeBitcodeToFile(path);
if (error_code != 0) {
log.err("failed dumping LLVM module to \"{s}\": {d}", .{ path, error_code });
return false;
}
} else {
log.err("writing bitcode without libllvm not implemented", .{});
return false;
}
return true;
}
pub fn dump(self: *Builder) void {
if (self.useLibLlvm())
self.llvm.module.?.dump()
@ -8980,7 +9372,7 @@ pub fn printUnbuffered(
if (variable.global.getReplacement(self) != .none) continue;
const global = variable.global.ptrConst(self);
try writer.print(
\\{} ={}{}{}{}{}{}{ }{} {s} {%}{ }{, }
\\{} ={}{}{}{}{ }{}{ }{} {s} {%}{ }{, }
\\
, .{
variable.global.fmt(self),
@ -10906,7 +11298,7 @@ fn icmpConstAssumeCapacity(
.data = self.addConstantExtraAssumeCapacity(data),
});
if (self.useLibLlvm()) self.llvm.constants.appendAssumeCapacity(
llvm.constICmp(@enumFromInt(@intFromEnum(cond)), lhs.toLlvm(self), rhs.toLlvm(self)),
llvm.constICmp(cond.toLlvm(), lhs.toLlvm(self), rhs.toLlvm(self)),
);
}
return @enumFromInt(gop.index);
@ -10943,7 +11335,7 @@ fn fcmpConstAssumeCapacity(
.data = self.addConstantExtraAssumeCapacity(data),
});
if (self.useLibLlvm()) self.llvm.constants.appendAssumeCapacity(
llvm.constFCmp(@enumFromInt(@intFromEnum(cond)), lhs.toLlvm(self), rhs.toLlvm(self)),
llvm.constFCmp(cond.toLlvm(), lhs.toLlvm(self), rhs.toLlvm(self)),
);
}
return @enumFromInt(gop.index);

View File

@ -142,8 +142,14 @@ pub const Value = opaque {
pub const setSection = LLVMSetSection;
extern fn LLVMSetSection(Global: *Value, Section: [*:0]const u8) void;
pub const deleteGlobal = LLVMDeleteGlobal;
extern fn LLVMDeleteGlobal(GlobalVar: *Value) void;
pub const removeGlobalValue = ZigLLVMRemoveGlobalValue;
extern fn ZigLLVMRemoveGlobalValue(GlobalVal: *Value) void;
pub const eraseGlobalValue = ZigLLVMEraseGlobalValue;
extern fn ZigLLVMEraseGlobalValue(GlobalVal: *Value) void;
pub const deleteGlobalValue = ZigLLVMDeleteGlobalValue;
extern fn ZigLLVMDeleteGlobalValue(GlobalVal: *Value) void;
pub const setAliasee = LLVMAliasSetAliasee;
extern fn LLVMAliasSetAliasee(Alias: *Value, Aliasee: *Value) void;
@ -292,20 +298,14 @@ pub const Value = opaque {
pub const setValueName = LLVMSetValueName2;
extern fn LLVMSetValueName2(Val: *Value, Name: [*]const u8, NameLen: usize) void;
pub const getValueName = LLVMGetValueName;
extern fn LLVMGetValueName(Val: *Value) [*:0]const u8;
pub const takeName = ZigLLVMTakeName;
extern fn ZigLLVMTakeName(new_owner: *Value, victim: *Value) void;
pub const deleteFunction = LLVMDeleteFunction;
extern fn LLVMDeleteFunction(Fn: *Value) void;
pub const getParam = LLVMGetParam;
extern fn LLVMGetParam(Fn: *Value, Index: c_uint) *Value;
pub const setInitializer = LLVMSetInitializer;
extern fn LLVMSetInitializer(GlobalVar: *Value, ConstantVal: *Value) void;
pub const setInitializer = ZigLLVMSetInitializer;
extern fn ZigLLVMSetInitializer(GlobalVar: *Value, ConstantVal: ?*Value) void;
pub const setDLLStorageClass = LLVMSetDLLStorageClass;
extern fn LLVMSetDLLStorageClass(Global: *Value, Class: DLLStorageClass) void;
@ -316,15 +316,6 @@ pub const Value = opaque {
pub const replaceAllUsesWith = LLVMReplaceAllUsesWith;
extern fn LLVMReplaceAllUsesWith(OldVal: *Value, NewVal: *Value) void;
pub const getLinkage = LLVMGetLinkage;
extern fn LLVMGetLinkage(Global: *Value) Linkage;
pub const getUnnamedAddress = LLVMGetUnnamedAddress;
extern fn LLVMGetUnnamedAddress(Global: *Value) Bool;
pub const getAlignment = LLVMGetAlignment;
extern fn LLVMGetAlignment(V: *Value) c_uint;
pub const attachMetaData = ZigLLVMAttachMetaData;
extern fn ZigLLVMAttachMetaData(GlobalVar: *Value, DIG: *DIGlobalVariableExpression) void;
@ -423,18 +414,12 @@ pub const Module = opaque {
pub const setModuleCodeModel = ZigLLVMSetModuleCodeModel;
extern fn ZigLLVMSetModuleCodeModel(module: *Module, code_model: CodeModel) void;
pub const addFunction = LLVMAddFunction;
extern fn LLVMAddFunction(*Module, Name: [*:0]const u8, FunctionTy: *Type) *Value;
pub const addFunctionInAddressSpace = ZigLLVMAddFunctionInAddressSpace;
extern fn ZigLLVMAddFunctionInAddressSpace(*Module, Name: [*:0]const u8, FunctionTy: *Type, AddressSpace: c_uint) *Value;
pub const printToString = LLVMPrintModuleToString;
extern fn LLVMPrintModuleToString(*Module) [*:0]const u8;
pub const addGlobal = LLVMAddGlobal;
extern fn LLVMAddGlobal(M: *Module, Ty: *Type, Name: [*:0]const u8) *Value;
pub const addGlobalInAddressSpace = LLVMAddGlobalInAddressSpace;
extern fn LLVMAddGlobalInAddressSpace(M: *Module, Ty: *Type, Name: [*:0]const u8, AddressSpace: c_uint) *Value;
@ -450,16 +435,6 @@ pub const Module = opaque {
Name: [*:0]const u8,
) *Value;
pub const getNamedGlobalAlias = LLVMGetNamedGlobalAlias;
extern fn LLVMGetNamedGlobalAlias(
M: *Module,
/// Empirically, LLVM will call strlen() on `Name` and so it
/// must be both null terminated and also have `NameLen` set
/// to the size.
Name: [*:0]const u8,
NameLen: usize,
) ?*Value;
pub const setTarget = LLVMSetTarget;
extern fn LLVMSetTarget(M: *Module, Triple: [*:0]const u8) void;

View File

@ -1122,6 +1122,22 @@ void ZigLLVMTakeName(LLVMValueRef new_owner, LLVMValueRef victim) {
unwrap(new_owner)->takeName(unwrap(victim));
}
void ZigLLVMRemoveGlobalValue(LLVMValueRef GlobalVal) {
unwrap<GlobalValue>(GlobalVal)->removeFromParent();
}
void ZigLLVMEraseGlobalValue(LLVMValueRef GlobalVal) {
unwrap<GlobalValue>(GlobalVal)->eraseFromParent();
}
void ZigLLVMDeleteGlobalValue(LLVMValueRef GlobalVal) {
delete unwrap<GlobalVariable>(GlobalVal);
}
void ZigLLVMSetInitializer(LLVMValueRef GlobalVar, LLVMValueRef ConstantVal) {
unwrap<GlobalVariable>(GlobalVar)->setInitializer(ConstantVal ? unwrap<Constant>(ConstantVal) : nullptr);
}
ZigLLVMDIGlobalVariable* ZigLLVMGlobalGetVariable(ZigLLVMDIGlobalVariableExpression *global_variable_expression) {
return reinterpret_cast<ZigLLVMDIGlobalVariable*>(reinterpret_cast<DIGlobalVariableExpression*>(global_variable_expression)->getVariable());
}

View File

@ -492,6 +492,10 @@ enum ZigLLVM_ObjectFormatType {
};
ZIG_EXTERN_C void ZigLLVMTakeName(LLVMValueRef new_owner, LLVMValueRef victim);
ZIG_EXTERN_C void ZigLLVMRemoveGlobalValue(LLVMValueRef GlobalVal);
ZIG_EXTERN_C void ZigLLVMEraseGlobalValue(LLVMValueRef GlobalVal);
ZIG_EXTERN_C void ZigLLVMDeleteGlobalValue(LLVMValueRef GlobalVal);
ZIG_EXTERN_C void ZigLLVMSetInitializer(LLVMValueRef GlobalVar, LLVMValueRef ConstantVal);
#define ZigLLVM_DIFlags_Zero 0U
#define ZigLLVM_DIFlags_Private 1U