llvm: finish converting constants

This commit is contained in:
Jacob Young 2023-07-11 23:02:18 -04:00
parent 1f8407c356
commit 8fde3a8f04
2 changed files with 331 additions and 253 deletions

View File

@ -8,7 +8,10 @@ const native_endian = builtin.cpu.arch.endian();
const DW = std.dwarf;
const Builder = @import("llvm/Builder.zig");
const llvm = @import("llvm/bindings.zig");
const llvm = if (build_options.have_llvm)
@import("llvm/bindings.zig")
else
@compileError("LLVM unavailable");
const link = @import("../link.zig");
const Compilation = @import("../Compilation.zig");
const build_options = @import("build_options");
@ -577,7 +580,7 @@ pub const Object = struct {
extern_collisions: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void),
/// Memoizes a null `?usize` value.
null_opt_addr: ?*llvm.Value,
null_opt_usize: Builder.Constant,
pub const TypeMap = std.AutoHashMapUnmanaged(InternPool.Index, Builder.Type);
@ -733,7 +736,7 @@ pub const Object = struct {
.di_type_map = .{},
.error_name_table = .none,
.extern_collisions = .{},
.null_opt_addr = null,
.null_opt_usize = .no_init,
};
}
@ -789,31 +792,31 @@ pub const Object = struct {
const name = try o.builder.string(mod.intern_pool.stringToSlice(name_nts));
const str_init = try o.builder.stringNullConst(name);
const str_ty = str_init.typeOf(&o.builder);
const str_global = o.llvm_module.addGlobal(str_ty.toLlvm(&o.builder), "");
str_global.setInitializer(str_init.toLlvm(&o.builder));
str_global.setLinkage(.Private);
str_global.setGlobalConstant(.True);
str_global.setUnnamedAddr(.True);
str_global.setAlignment(1);
const str_llvm_global = o.llvm_module.addGlobal(str_ty.toLlvm(&o.builder), "");
str_llvm_global.setInitializer(str_init.toLlvm(&o.builder));
str_llvm_global.setLinkage(.Private);
str_llvm_global.setGlobalConstant(.True);
str_llvm_global.setUnnamedAddr(.True);
str_llvm_global.setAlignment(1);
var global = Builder.Global{
var str_global = Builder.Global{
.linkage = .private,
.unnamed_addr = .unnamed_addr,
.type = str_ty,
.alignment = comptime Builder.Alignment.fromByteUnits(1),
.kind = .{ .variable = @enumFromInt(o.builder.variables.items.len) },
};
var variable = Builder.Variable{
var str_variable = Builder.Variable{
.global = @enumFromInt(o.builder.globals.count()),
.mutability = .constant,
.init = str_init,
};
try o.builder.llvm.globals.append(o.gpa, str_global);
const str_global_index = try o.builder.addGlobal(.empty, global);
try o.builder.variables.append(o.gpa, variable);
try o.builder.llvm.globals.append(o.gpa, str_llvm_global);
const global_index = try o.builder.addGlobal(.empty, str_global);
try o.builder.variables.append(o.gpa, str_variable);
llvm_error.* = try o.builder.structConst(llvm_slice_ty, &.{
str_global_index.toConst(),
global_index.toConst(),
try o.builder.intConst(llvm_usize_ty, name.toSlice(&o.builder).?.len),
});
}
@ -2684,55 +2687,6 @@ pub const Object = struct {
return buffer.toOwnedSliceSentinel(0);
}
fn getNullOptAddr(o: *Object) !*llvm.Value {
if (o.null_opt_addr) |global| return global;
const mod = o.module;
const target = mod.getTarget();
const ty = try mod.intern(.{ .opt_type = .usize_type });
const llvm_init = try o.lowerValue(try mod.intern(.{ .opt = .{
.ty = ty,
.val = .none,
} }));
const llvm_ty = llvm_init.typeOf(&o.builder);
const llvm_wanted_addrspace = toLlvmAddressSpace(.generic, target);
const llvm_actual_addrspace = toLlvmGlobalAddressSpace(.generic, target);
const llvm_alignment = ty.toType().abiAlignment(mod);
const llvm_global = o.llvm_module.addGlobalInAddressSpace(
llvm_ty.toLlvm(&o.builder),
"",
@intFromEnum(llvm_actual_addrspace),
);
llvm_global.setLinkage(.Internal);
llvm_global.setUnnamedAddr(.True);
llvm_global.setAlignment(llvm_alignment);
llvm_global.setInitializer(llvm_init.toLlvm(&o.builder));
var global = Builder.Global{
.linkage = .internal,
.unnamed_addr = .unnamed_addr,
.type = llvm_ty,
.alignment = Builder.Alignment.fromByteUnits(llvm_alignment),
.kind = .{ .variable = @enumFromInt(o.builder.variables.items.len) },
};
var variable = Builder.Variable{
.global = @enumFromInt(o.builder.globals.count()),
.init = llvm_init,
};
try o.builder.llvm.globals.append(o.gpa, llvm_global);
_ = try o.builder.addGlobal(.empty, global);
try o.builder.variables.append(o.gpa, variable);
const addrspace_casted_global = if (llvm_wanted_addrspace != llvm_actual_addrspace)
llvm_global.constAddrSpaceCast((try o.builder.ptrType(llvm_wanted_addrspace)).toLlvm(&o.builder))
else
llvm_global;
o.null_opt_addr = addrspace_casted_global;
return addrspace_casted_global;
}
/// If the llvm function does not exist, create it.
/// Note that this can be called before the function's semantic analysis has
/// completed, so if any attributes rely on that, they must be done in updateFunc, not here.
@ -3755,6 +3709,9 @@ pub const Object = struct {
},
},
.vector_type => |vector_type| {
const vector_ty = try o.lowerType(ty);
switch (aggregate.storage) {
.bytes, .elems => {
const ExpectedContents = [Builder.expected_fields_len]Builder.Constant;
var stack align(@max(
@alignOf(std.heap.StackFallbackAllocator(0)),
@ -3771,9 +3728,15 @@ pub const Object = struct {
.elems => |elems| for (vals, elems) |*result_val, elem| {
result_val.* = try o.lowerValue(elem);
},
.repeated_elem => |elem| @memset(vals, try o.lowerValue(elem)),
.repeated_elem => unreachable,
}
return o.builder.vectorConst(vector_ty, vals);
},
.repeated_elem => |elem| return o.builder.splatConst(
vector_ty,
try o.lowerValue(elem),
),
}
return o.builder.vectorConst(try o.lowerType(ty), vals);
},
.anon_struct_type => |tuple| {
const struct_ty = try o.lowerType(ty);
@ -4209,14 +4172,11 @@ pub const Object = struct {
else
(try o.resolveGlobalDecl(decl_index)).ptrConst(&o.builder).global;
const target = mod.getTarget();
const llvm_wanted_addrspace = toLlvmAddressSpace(decl.@"addrspace", target);
const llvm_actual_addrspace = toLlvmGlobalAddressSpace(decl.@"addrspace", target);
const llvm_val = if (llvm_wanted_addrspace != llvm_actual_addrspace) try o.builder.castConst(
.addrspacecast,
const llvm_val = try o.builder.convConst(
.unneeded,
llvm_global.toConst(),
try o.builder.ptrType(llvm_wanted_addrspace),
) else llvm_global.toConst();
try o.builder.ptrType(toLlvmAddressSpace(decl.@"addrspace", mod.getTarget())),
);
return o.builder.convConst(if (ty.isAbiInt(mod)) switch (ty.intInfo(mod).signedness) {
.signed => .signed,
@ -4618,15 +4578,15 @@ pub const FuncGen = struct {
.ty = self.typeOf(inst),
.val = (try self.air.value(inst, mod)).?,
});
gop.value_ptr.* = llvm_val;
return llvm_val;
gop.value_ptr.* = llvm_val.toLlvm(&o.builder);
return gop.value_ptr.*;
}
fn resolveValue(self: *FuncGen, tv: TypedValue) !*llvm.Value {
fn resolveValue(self: *FuncGen, tv: TypedValue) Error!Builder.Constant {
const o = self.dg.object;
const mod = o.module;
const llvm_val = try o.lowerValue(tv.val.toIntern());
if (!isByRef(tv.ty, mod)) return llvm_val.toLlvm(&o.builder);
if (!isByRef(tv.ty, mod)) return llvm_val;
// We have an LLVM value but we need to create a global constant and
// set the value as its initializer, and then return a pointer to the global.
@ -4645,6 +4605,7 @@ pub const FuncGen = struct {
var global = Builder.Global{
.linkage = .private,
.unnamed_addr = .unnamed_addr,
.addr_space = llvm_actual_addrspace,
.type = llvm_ty,
.alignment = Builder.Alignment.fromByteUnits(llvm_alignment),
.kind = .{ .variable = @enumFromInt(o.builder.variables.items.len) },
@ -4655,16 +4616,27 @@ pub const FuncGen = struct {
.init = llvm_val,
};
try o.builder.llvm.globals.append(o.gpa, llvm_global);
_ = try o.builder.addGlobal(.empty, global);
const global_index = try o.builder.addGlobal(.empty, global);
try o.builder.variables.append(o.gpa, variable);
const addrspace_casted_ptr = if (llvm_actual_addrspace != llvm_wanted_addrspace)
llvm_global.constAddrSpaceCast(
(try o.builder.ptrType(llvm_wanted_addrspace)).toLlvm(&o.builder),
)
else
llvm_global;
return addrspace_casted_ptr;
return try o.builder.convConst(
.unneeded,
global_index.toConst(),
try o.builder.ptrType(llvm_wanted_addrspace),
);
}
fn resolveNullOptUsize(self: *FuncGen) Error!Builder.Constant {
const o = self.dg.object;
const mod = o.module;
if (o.null_opt_usize == .no_init) {
const ty = try mod.intern(.{ .opt_type = .usize_type });
o.null_opt_usize = try self.resolveValue(.{
.ty = ty.toType(),
.val = (try mod.intern(.{ .opt = .{ .ty = ty, .val = .none } })).toValue(),
});
}
return o.null_opt_usize;
}
fn genBody(self: *FuncGen, body: []const Air.Inst.Index) Error!void {
@ -5243,7 +5215,7 @@ pub const FuncGen = struct {
const msg_decl = mod.declPtr(msg_decl_index);
const msg_len = msg_decl.ty.childType(mod).arrayLen(mod);
const msg_ptr = try o.lowerValue(try msg_decl.internValue(mod));
const null_opt_addr_global = try o.getNullOptAddr();
const null_opt_addr_global = try fg.resolveNullOptUsize();
const target = mod.getTarget();
const llvm_usize = try o.lowerType(Type.usize);
// example:
@ -5257,7 +5229,7 @@ pub const FuncGen = struct {
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,
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);
@ -6872,11 +6844,11 @@ pub const FuncGen = struct {
const operand = try self.resolveInst(un_op);
const operand_ty = self.typeOf(un_op);
const optional_ty = if (operand_is_ptr) operand_ty.childType(mod) else operand_ty;
const optional_llvm_ty = (try o.lowerType(optional_ty)).toLlvm(&o.builder);
const optional_llvm_ty = try o.lowerType(optional_ty);
const payload_ty = optional_ty.optionalChild(mod);
if (optional_ty.optionalReprIsPayload(mod)) {
const loaded = if (operand_is_ptr)
self.builder.buildLoad(optional_llvm_ty, operand, "")
self.builder.buildLoad(optional_llvm_ty.toLlvm(&o.builder), operand, "")
else
operand;
if (payload_ty.isSlice(mod)) {
@ -6887,21 +6859,21 @@ pub const FuncGen = struct {
));
return self.builder.buildICmp(pred, slice_ptr, (try o.builder.nullConst(ptr_ty)).toLlvm(&o.builder), "");
}
return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), "");
return self.builder.buildICmp(pred, loaded, (try o.builder.zeroInitConst(optional_llvm_ty)).toLlvm(&o.builder), "");
}
comptime assert(optional_layout_version == 3);
if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
const loaded = if (operand_is_ptr)
self.builder.buildLoad(optional_llvm_ty, operand, "")
self.builder.buildLoad(optional_llvm_ty.toLlvm(&o.builder), operand, "")
else
operand;
return self.builder.buildICmp(pred, loaded, (try o.builder.intConst(.i8, 0)).toLlvm(&o.builder), "");
}
const is_by_ref = operand_is_ptr or isByRef(optional_ty, mod);
const non_null_bit = try self.optIsNonNull(optional_llvm_ty, operand, is_by_ref);
const non_null_bit = try self.optIsNonNull(optional_llvm_ty.toLlvm(&o.builder), operand, is_by_ref);
if (pred == .EQ) {
return self.builder.buildNot(non_null_bit, "");
} else {
@ -7549,24 +7521,18 @@ pub const FuncGen = struct {
}
if (scalar_ty.isSignedInt(mod)) {
const inst_llvm_ty = try o.lowerType(inst_ty);
const scalar_bit_size_minus_one = scalar_ty.bitSize(mod) - 1;
const bit_size_minus_one = if (inst_ty.zigTypeTag(mod) == .Vector) const_vector: {
const vec_len = inst_ty.vectorLen(mod);
const shifts = try self.gpa.alloc(*llvm.Value, vec_len);
defer self.gpa.free(shifts);
@memset(shifts, (try o.builder.intConst(try o.lowerType(scalar_ty), scalar_bit_size_minus_one)).toLlvm(&o.builder));
break :const_vector llvm.constVector(shifts.ptr, vec_len);
} else (try o.builder.intConst(inst_llvm_ty, scalar_bit_size_minus_one)).toLlvm(&o.builder);
const bit_size_minus_one = try o.builder.splatConst(inst_llvm_ty, try o.builder.intConst(
inst_llvm_ty.scalarType(&o.builder),
inst_llvm_ty.scalarBits(&o.builder) - 1,
));
const div = self.builder.buildSDiv(lhs, rhs, "");
const rem = self.builder.buildSRem(lhs, rhs, "");
const div_sign = self.builder.buildXor(lhs, rhs, "");
const div_sign_mask = self.builder.buildAShr(div_sign, bit_size_minus_one, "");
const zero = inst_llvm_ty.toLlvm(&o.builder).constNull();
const rem_nonzero = self.builder.buildICmp(.NE, rem, zero, "");
const correction = self.builder.buildSelect(rem_nonzero, div_sign_mask, zero, "");
const div_sign_mask = self.builder.buildAShr(div_sign, bit_size_minus_one.toLlvm(&o.builder), "");
const zero = try o.builder.zeroInitConst(inst_llvm_ty);
const rem_nonzero = self.builder.buildICmp(.NE, rem, zero.toLlvm(&o.builder), "");
const correction = self.builder.buildSelect(rem_nonzero, div_sign_mask, zero.toLlvm(&o.builder), "");
return self.builder.buildNSWAdd(div, correction, "");
}
return self.builder.buildUDiv(lhs, rhs, "");
@ -7620,29 +7586,23 @@ pub const FuncGen = struct {
const a = try self.buildFloatOp(.fmod, inst_ty, 2, .{ lhs, rhs });
const b = try self.buildFloatOp(.add, inst_ty, 2, .{ a, rhs });
const c = try self.buildFloatOp(.fmod, inst_ty, 2, .{ b, rhs });
const zero = inst_llvm_ty.toLlvm(&o.builder).constNull();
const ltz = try self.buildFloatCmp(.lt, inst_ty, .{ lhs, zero });
const zero = try o.builder.zeroInitConst(inst_llvm_ty);
const ltz = try self.buildFloatCmp(.lt, inst_ty, .{ lhs, zero.toLlvm(&o.builder) });
return self.builder.buildSelect(ltz, c, a, "");
}
if (scalar_ty.isSignedInt(mod)) {
const scalar_bit_size_minus_one = scalar_ty.bitSize(mod) - 1;
const bit_size_minus_one = if (inst_ty.zigTypeTag(mod) == .Vector) const_vector: {
const vec_len = inst_ty.vectorLen(mod);
const shifts = try self.gpa.alloc(*llvm.Value, vec_len);
defer self.gpa.free(shifts);
@memset(shifts, (try o.builder.intConst(try o.lowerType(scalar_ty), scalar_bit_size_minus_one)).toLlvm(&o.builder));
break :const_vector llvm.constVector(shifts.ptr, vec_len);
} else (try o.builder.intConst(inst_llvm_ty, scalar_bit_size_minus_one)).toLlvm(&o.builder);
const bit_size_minus_one = try o.builder.splatConst(inst_llvm_ty, try o.builder.intConst(
inst_llvm_ty.scalarType(&o.builder),
inst_llvm_ty.scalarBits(&o.builder) - 1,
));
const rem = self.builder.buildSRem(lhs, rhs, "");
const div_sign = self.builder.buildXor(lhs, rhs, "");
const div_sign_mask = self.builder.buildAShr(div_sign, bit_size_minus_one, "");
const div_sign_mask = self.builder.buildAShr(div_sign, bit_size_minus_one.toLlvm(&o.builder), "");
const rhs_masked = self.builder.buildAnd(rhs, div_sign_mask, "");
const zero = inst_llvm_ty.toLlvm(&o.builder).constNull();
const rem_nonzero = self.builder.buildICmp(.NE, rem, zero, "");
const correction = self.builder.buildSelect(rem_nonzero, rhs_masked, zero, "");
const zero = try o.builder.zeroInitConst(inst_llvm_ty);
const rem_nonzero = self.builder.buildICmp(.NE, rem, zero.toLlvm(&o.builder), "");
const correction = self.builder.buildSelect(rem_nonzero, rhs_masked, zero.toLlvm(&o.builder), "");
return self.builder.buildNSWAdd(rem, correction, "");
}
return self.builder.buildURem(lhs, rhs, "");
@ -7953,17 +7913,17 @@ pub const FuncGen = struct {
// In this case we can generate a softfloat negation by XORing the
// bits with a constant.
const int_ty = try o.builder.intType(@intCast(float_bits));
const one = (try o.builder.intConst(int_ty, 1)).toLlvm(&o.builder);
const one = try o.builder.intConst(int_ty, 1);
const shift_amt = try o.builder.intConst(int_ty, float_bits - 1);
const sign_mask = one.constShl(shift_amt.toLlvm(&o.builder));
const sign_mask = try o.builder.binConst(.shl, one, shift_amt);
const result = if (ty.zigTypeTag(mod) == .Vector) blk: {
const splat_sign_mask = self.builder.buildVectorSplat(ty.vectorLen(mod), sign_mask, "");
const splat_sign_mask = self.builder.buildVectorSplat(ty.vectorLen(mod), sign_mask.toLlvm(&o.builder), "");
const cast_ty = try o.builder.vectorType(.normal, ty.vectorLen(mod), int_ty);
const bitcasted_operand = self.builder.buildBitCast(params[0], cast_ty.toLlvm(&o.builder), "");
break :blk self.builder.buildXor(bitcasted_operand, splat_sign_mask, "");
} else blk: {
const bitcasted_operand = self.builder.buildBitCast(params[0], int_ty.toLlvm(&o.builder), "");
break :blk self.builder.buildXor(bitcasted_operand, sign_mask, "");
break :blk self.builder.buildXor(bitcasted_operand, sign_mask.toLlvm(&o.builder), "");
};
return self.builder.buildBitCast(result, llvm_ty.toLlvm(&o.builder), "");
},
@ -8886,9 +8846,9 @@ pub const FuncGen = struct {
const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty);
if (intrinsic_len0_traps) {
try self.safeWasmMemset(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile);
try self.safeWasmMemset(dest_ptr, fill_byte.toLlvm(&o.builder), len, dest_ptr_align, is_volatile);
} else {
_ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile);
_ = self.builder.buildMemSet(dest_ptr, fill_byte.toLlvm(&o.builder), len, dest_ptr_align, is_volatile);
}
return null;
}
@ -8987,8 +8947,9 @@ pub const FuncGen = struct {
dest_ptr_align: u32,
is_volatile: bool,
) !void {
const llvm_usize_ty = self.context.intType(self.dg.object.target.ptrBitWidth());
const cond = try self.cmp(len, llvm_usize_ty.constInt(0, .False), Type.usize, .neq);
const o = self.dg.object;
const llvm_usize_ty = try o.lowerType(Type.usize);
const cond = try self.cmp(len, (try o.builder.intConst(llvm_usize_ty, 0)).toLlvm(&o.builder), Type.usize, .neq);
const memset_block = self.context.appendBasicBlock(self.llvm_func, "MemsetTrapSkip");
const end_block = self.context.appendBasicBlock(self.llvm_func, "MemsetTrapEnd");
_ = self.builder.buildCondBr(cond, memset_block, end_block);
@ -9020,8 +8981,8 @@ pub const FuncGen = struct {
std.Target.wasm.featureSetHas(o.target.cpu.features, .bulk_memory) and
dest_ptr_ty.isSlice(mod))
{
const llvm_usize_ty = self.context.intType(self.dg.object.target.ptrBitWidth());
const cond = try self.cmp(len, llvm_usize_ty.constInt(0, .False), Type.usize, .neq);
const llvm_usize_ty = try o.lowerType(Type.usize);
const cond = try self.cmp(len, (try o.builder.intConst(llvm_usize_ty, 0)).toLlvm(&o.builder), Type.usize, .neq);
const memcpy_block = self.context.appendBasicBlock(self.llvm_func, "MemcpyTrapSkip");
const end_block = self.context.appendBasicBlock(self.llvm_func, "MemcpyTrapEnd");
_ = self.builder.buildCondBr(cond, memcpy_block, end_block);
@ -9183,19 +9144,13 @@ pub const FuncGen = struct {
if (operand_ty.zigTypeTag(mod) == .Vector) {
const vec_len = operand_ty.vectorLen(mod);
operand_llvm_ty = try o.builder.vectorType(.normal, vec_len, scalar_ty);
} else operand_llvm_ty = scalar_ty;
const shifts = try self.gpa.alloc(*llvm.Value, vec_len);
defer self.gpa.free(shifts);
@memset(shifts, (try o.builder.intConst(scalar_ty, 8)).toLlvm(&o.builder));
const shift_vec = llvm.constVector(shifts.ptr, vec_len);
const shift_amt =
try o.builder.splatConst(operand_llvm_ty, try o.builder.intConst(scalar_ty, 8));
const extended = self.builder.buildZExt(operand, operand_llvm_ty.toLlvm(&o.builder), "");
operand = self.builder.buildShl(extended, shift_vec, "");
} else {
const extended = self.builder.buildZExt(operand, scalar_ty.toLlvm(&o.builder), "");
operand = self.builder.buildShl(extended, (try o.builder.intConst(scalar_ty, 8)).toLlvm(&o.builder), "");
operand_llvm_ty = scalar_ty;
}
operand = self.builder.buildShl(extended, shift_amt.toLlvm(&o.builder), "");
bits = bits + 8;
}
@ -9358,11 +9313,8 @@ 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)});
const slice_ty = Type.slice_const_u8_sentinel_0;
const ret_ty = try o.lowerType(slice_ty);
const llvm_ret_ty = ret_ty.toLlvm(&o.builder);
const ret_ty = try o.lowerType(Type.slice_const_u8_sentinel_0);
const usize_ty = try o.lowerType(Type.usize);
const slice_alignment = slice_ty.abiAlignment(mod);
const fn_type = try o.builder.fnType(ret_ty, &.{
try o.lowerType(enum_type.tag_ty.toType()),
@ -9399,33 +9351,38 @@ pub const FuncGen = struct {
const tag_int_value = fn_val.getParam(0);
const switch_instr = self.builder.buildSwitch(tag_int_value, bad_value_block, @as(c_uint, @intCast(enum_type.names.len)));
const array_ptr_indices: [2]*llvm.Value = .{
(try o.builder.intConst(usize_ty, 0)).toLlvm(&o.builder),
} ** 2;
for (enum_type.names, 0..) |name_ip, field_index_usize| {
const field_index = @as(u32, @intCast(field_index_usize));
const name = mod.intern_pool.stringToSlice(name_ip);
const str_init = self.context.constString(name.ptr, @as(c_uint, @intCast(name.len)), .False);
const str_init_llvm_ty = str_init.typeOf();
const str_global = o.llvm_module.addGlobal(str_init_llvm_ty, "");
str_global.setInitializer(str_init);
str_global.setLinkage(.Private);
str_global.setGlobalConstant(.True);
str_global.setUnnamedAddr(.True);
str_global.setAlignment(1);
const name = try o.builder.string(mod.intern_pool.stringToSlice(name_ip));
const str_init = try o.builder.stringNullConst(name);
const str_ty = str_init.typeOf(&o.builder);
const str_llvm_global = o.llvm_module.addGlobal(str_ty.toLlvm(&o.builder), "");
str_llvm_global.setInitializer(str_init.toLlvm(&o.builder));
str_llvm_global.setLinkage(.Private);
str_llvm_global.setGlobalConstant(.True);
str_llvm_global.setUnnamedAddr(.True);
str_llvm_global.setAlignment(1);
const slice_fields = [_]*llvm.Value{
str_init_llvm_ty.constInBoundsGEP(str_global, &array_ptr_indices, array_ptr_indices.len),
(try o.builder.intConst(usize_ty, name.len)).toLlvm(&o.builder),
var str_global = Builder.Global{
.linkage = .private,
.unnamed_addr = .unnamed_addr,
.type = str_ty,
.alignment = comptime Builder.Alignment.fromByteUnits(1),
.kind = .{ .variable = @enumFromInt(o.builder.variables.items.len) },
};
const slice_init = llvm_ret_ty.constNamedStruct(&slice_fields, slice_fields.len);
const slice_global = o.llvm_module.addGlobal(slice_init.typeOf(), "");
slice_global.setInitializer(slice_init);
slice_global.setLinkage(.Private);
slice_global.setGlobalConstant(.True);
slice_global.setUnnamedAddr(.True);
slice_global.setAlignment(slice_alignment);
var str_variable = Builder.Variable{
.global = @enumFromInt(o.builder.globals.count()),
.mutability = .constant,
.init = str_init,
};
try o.builder.llvm.globals.append(o.gpa, str_llvm_global);
const global_index = try o.builder.addGlobal(.empty, str_global);
try o.builder.variables.append(o.gpa, str_variable);
const slice_val = try o.builder.structConst(ret_ty, &.{
global_index.toConst(),
try o.builder.intConst(usize_ty, name.toSlice(&o.builder).?.len),
});
const return_block = self.context.appendBasicBlock(fn_val, "Name");
const this_tag_int_value =
@ -9433,9 +9390,7 @@ pub const FuncGen = struct {
switch_instr.addCase(this_tag_int_value.toLlvm(&o.builder), return_block);
self.builder.positionBuilderAtEnd(return_block);
const loaded = self.builder.buildLoad(llvm_ret_ty, slice_global, "");
loaded.setAlignment(slice_alignment);
_ = self.builder.buildRet(loaded);
_ = self.builder.buildRet(slice_val.toLlvm(&o.builder));
}
self.builder.positionBuilderAtEnd(bad_value_block);
@ -9530,22 +9485,25 @@ pub const FuncGen = struct {
// when changing code, so Zig uses negative numbers to index the
// second vector. These start at -1 and go down, and are easiest to use
// with the ~ operator. Here we convert between the two formats.
const values = try self.gpa.alloc(*llvm.Value, mask_len);
const values = try self.gpa.alloc(Builder.Constant, mask_len);
defer self.gpa.free(values);
for (values, 0..) |*val, i| {
const elem = try mask.elemValue(mod, i);
if (elem.isUndef(mod)) {
val.* = Builder.Type.i32.toLlvm(&o.builder).getUndef();
val.* = try o.builder.undefConst(.i32);
} else {
const int = elem.toSignedInt(mod);
const unsigned = if (int >= 0) @as(u32, @intCast(int)) else @as(u32, @intCast(~int + a_len));
val.* = (try o.builder.intConst(.i32, unsigned)).toLlvm(&o.builder);
val.* = try o.builder.intConst(.i32, unsigned);
}
}
const llvm_mask_value = llvm.constVector(values.ptr, mask_len);
return self.builder.buildShuffleVector(a, b, llvm_mask_value, "");
const llvm_mask_value = try o.builder.vectorConst(
try o.builder.vectorType(.normal, mask_len, .i32),
values,
);
return self.builder.buildShuffleVector(a, b, llvm_mask_value.toLlvm(&o.builder), "");
}
/// Reduce a vector by repeatedly applying `llvm_fn` to produce an accumulated result.
@ -9816,7 +9774,7 @@ pub const FuncGen = struct {
.val = sent_val,
});
try self.store(elem_ptr, elem_ptr_ty, llvm_elem, .NotAtomic);
try self.store(elem_ptr, elem_ptr_ty, llvm_elem.toLlvm(&o.builder), .NotAtomic);
}
return alloca_inst;
@ -10431,12 +10389,13 @@ pub const FuncGen = struct {
else
self.builder.buildBitCast(elem, value_bits_type.toLlvm(&o.builder), "");
var mask_val = (try o.builder.intConst(value_bits_type, -1)).toLlvm(&o.builder);
mask_val = mask_val.constZExt(containing_int_ty.toLlvm(&o.builder));
mask_val = mask_val.constShl(shift_amt.toLlvm(&o.builder));
mask_val = mask_val.constNot();
var mask_val = try o.builder.intConst(value_bits_type, -1);
mask_val = try o.builder.castConst(.zext, mask_val, containing_int_ty);
mask_val = try o.builder.binConst(.shl, mask_val, shift_amt);
mask_val =
try o.builder.binConst(.xor, mask_val, try o.builder.intConst(containing_int_ty, -1));
const anded_containing_int = self.builder.buildAnd(containing_int, mask_val, "");
const anded_containing_int = self.builder.buildAnd(containing_int, mask_val.toLlvm(&o.builder), "");
const extended_value = self.builder.buildZExt(value_bits, containing_int_ty.toLlvm(&o.builder), "");
const shifted_value = self.builder.buildShl(extended_value, shift_amt.toLlvm(&o.builder), "");
const ored_value = self.builder.buildOr(shifted_value, anded_containing_int, "");

View File

@ -251,14 +251,41 @@ pub const Type = enum(u32) {
};
}
pub fn isFn(self: Type, builder: *const Builder) bool {
pub fn isFloatingPoint(self: Type) bool {
return switch (self) {
.half, .bfloat, .float, .double, .fp128, .x86_fp80, .ppc_fp128 => true,
else => false,
};
}
pub fn isInteger(self: Type, builder: *const Builder) bool {
return switch (self) {
.i1, .i8, .i16, .i29, .i32, .i64, .i80, .i128 => true,
else => switch (self.tag(builder)) {
.integer => true,
else => false,
},
};
}
pub fn isPointer(self: Type, builder: *const Builder) bool {
return switch (self) {
.ptr => true,
else => switch (self.tag(builder)) {
.pointer => true,
else => false,
},
};
}
pub fn isFunction(self: Type, builder: *const Builder) bool {
return switch (self.tag(builder)) {
.function, .vararg_function => true,
else => false,
};
}
pub fn fnKind(self: Type, builder: *const Builder) Type.Function.Kind {
pub fn functionKind(self: Type, builder: *const Builder) Type.Function.Kind {
return switch (self.tag(builder)) {
.function => .normal,
.vararg_function => .vararg,
@ -345,6 +372,20 @@ pub const Type = enum(u32) {
};
}
pub fn scalarType(self: Type, builder: *const Builder) Type {
if (self.isFloatingPoint()) return self;
const item = builder.type_items.items[@intFromEnum(self)];
return switch (item.tag) {
.integer,
.pointer,
=> self,
.vector,
.scalable_vector,
=> builder.typeExtraData(Type.Vector, item.data).child,
else => unreachable,
};
}
pub fn vectorLen(self: Type, builder: *const Builder) u32 {
const item = builder.type_items.items[@intFromEnum(self)];
return switch (item.tag) {
@ -809,17 +850,17 @@ pub const Global = struct {
}
pub fn rename(self: Index, new_name: String, builder: *Builder) Allocator.Error!void {
try builder.ensureUnusedCapacityGlobal(new_name);
try builder.ensureUnusedGlobalCapacity(new_name);
self.renameAssumeCapacity(new_name, builder);
}
pub fn takeName(self: Index, other: Index, builder: *Builder) Allocator.Error!void {
try builder.ensureUnusedCapacityGlobal(.empty);
try builder.ensureUnusedGlobalCapacity(.empty);
self.takeNameAssumeCapacity(other, builder);
}
pub fn replace(self: Index, other: Index, builder: *Builder) Allocator.Error!void {
try builder.ensureUnusedCapacityGlobal(.empty);
try builder.ensureUnusedGlobalCapacity(.empty);
self.replaceAssumeCapacity(other, builder);
}
@ -1047,6 +1088,7 @@ pub const Constant = enum(u32) {
string,
string_null,
vector,
splat,
zeroinitializer,
undef,
poison,
@ -1126,6 +1168,11 @@ pub const Constant = enum(u32) {
type: Type,
};
pub const Splat = extern struct {
type: Type,
value: Constant,
};
pub const BlockAddress = extern struct {
function: Function.Index,
block: Function.Block.Index,
@ -1217,6 +1264,7 @@ pub const Constant = enum(u32) {
.array,
.vector,
=> builder.constantExtraData(Aggregate, item.data).type,
.splat => builder.constantExtraData(Splat, item.data).type,
.string,
.string_null,
=> builder.arrayTypeAssumeCapacity(
@ -1270,28 +1318,10 @@ pub const Constant = enum(u32) {
},
.icmp, .fcmp => {
const ty = builder.constantExtraData(Compare, item.data).lhs.typeOf(builder);
return switch (ty) {
.half,
.bfloat,
.float,
.double,
.fp128,
.x86_fp80,
.ppc_fp128,
.i1,
.i8,
.i16,
.i29,
.i32,
.i64,
.i80,
.i128,
=> ty,
else => if (ty.isVector(builder)) switch (ty.vectorKind(builder)) {
return if (ty.isVector(builder)) switch (ty.vectorKind(builder)) {
inline else => |kind| builder
.vectorTypeAssumeCapacity(kind, ty.vectorLen(builder), .i1),
} else ty,
};
} else ty;
},
.extractelement => builder.constantExtraData(ExtractElement, item.data)
.arg.typeOf(builder).childType(builder),
@ -1479,7 +1509,6 @@ pub const Constant = enum(u32) {
const len = extra.data.type.aggregateLen(data.builder);
const vals: []const Constant =
@ptrCast(data.builder.constant_extra.items[extra.end..][0..len]);
try writer.writeAll(switch (tag) {
.structure => "{ ",
.packed_structure => "<{ ",
@ -1499,6 +1528,16 @@ pub const Constant = enum(u32) {
else => unreachable,
});
},
.splat => {
const extra = data.builder.constantExtraData(Splat, item.data);
const len = extra.type.vectorLen(data.builder);
try writer.writeByte('<');
for (0..len) |index| {
if (index > 0) try writer.writeAll(", ");
try writer.print("{%}", .{extra.value.fmt(data.builder)});
}
try writer.writeByte('>');
},
inline .string,
.string_null,
=> |tag| try writer.print("c{\"" ++ switch (tag) {
@ -2093,7 +2132,7 @@ pub fn namedTypeSetBody(
pub fn addGlobal(self: *Builder, name: String, global: Global) Allocator.Error!Global.Index {
assert(!name.isAnon());
try self.ensureUnusedTypeCapacity(1, null, 0);
try self.ensureUnusedCapacityGlobal(name);
try self.ensureUnusedGlobalCapacity(name);
return self.addGlobalAssumeCapacity(name, global);
}
@ -2231,8 +2270,17 @@ pub fn vectorConst(self: *Builder, ty: Type, vals: []const Constant) Allocator.E
return self.vectorConstAssumeCapacity(ty, vals);
}
pub fn splatConst(self: *Builder, ty: Type, val: Constant) Allocator.Error!Constant {
try self.ensureUnusedConstantCapacity(1, Constant.Splat, 0);
return self.splatConstAssumeCapacity(ty, val);
}
pub fn zeroInitConst(self: *Builder, ty: Type) Allocator.Error!Constant {
try self.ensureUnusedConstantCapacity(1, null, 0);
try self.ensureUnusedConstantCapacity(1, Constant.Fp128, 0);
try self.constant_limbs.ensureUnusedCapacity(
self.gpa,
Constant.Integer.limbs + comptime std.math.big.int.calcLimbLen(0),
);
return self.zeroInitConstAssumeCapacity(ty);
}
@ -2477,7 +2525,7 @@ fn isValidIdentifier(id: []const u8) bool {
return true;
}
fn ensureUnusedCapacityGlobal(self: *Builder, name: String) Allocator.Error!void {
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| try self.string_bytes.ensureUnusedCapacity(self.gpa, id.len +
@ -2571,6 +2619,7 @@ fn vectorTypeAssumeCapacity(
len: u32,
child: Type,
) Type {
assert(child.isFloatingPoint() or child.isInteger(self) or child.isPointer(self));
const tag: Type.Tag = switch (kind) {
.normal => .vector,
.scalable => .scalable_vector,
@ -3321,14 +3370,13 @@ fn vectorConstAssumeCapacity(
ty: Type,
vals: []const Constant,
) if (build_options.have_llvm) Allocator.Error!Constant else Constant {
if (std.debug.runtime_safety) {
const type_item = self.type_items.items[@intFromEnum(ty)];
assert(type_item.tag == .vector);
const extra = self.typeExtraData(Type.Vector, type_item.data);
assert(extra.len == vals.len);
for (vals) |val| assert(extra.child == val.typeOf(self));
}
assert(ty.isVector(self));
assert(ty.vectorLen(self) == vals.len);
for (vals) |val| assert(ty.childType(self) == val.typeOf(self));
for (vals[1..]) |val| {
if (vals[0] != val) break;
} else return self.splatConstAssumeCapacity(ty, vals[0]);
for (vals) |val| {
if (!val.isZeroInit(self)) break;
} else return self.zeroInitConstAssumeCapacity(ty);
@ -3351,14 +3399,81 @@ fn vectorConstAssumeCapacity(
return result.constant;
}
fn splatConstAssumeCapacity(
self: *Builder,
ty: Type,
val: Constant,
) if (build_options.have_llvm) Allocator.Error!Constant else Constant {
assert(ty.scalarType(self) == val.typeOf(self));
if (!ty.isVector(self)) return val;
if (val.isZeroInit(self)) return self.zeroInitConstAssumeCapacity(ty);
const Adapter = struct {
builder: *const Builder,
pub fn hash(_: @This(), key: Constant.Splat) u32 {
return @truncate(std.hash.Wyhash.hash(
comptime std.hash.uint32(@intFromEnum(Constant.Tag.splat)),
std.mem.asBytes(&key),
));
}
pub fn eql(ctx: @This(), lhs_key: Constant.Splat, _: void, rhs_index: usize) bool {
if (ctx.builder.constant_items.items(.tag)[rhs_index] != .splat) return false;
const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index];
const rhs_extra = ctx.builder.constantExtraData(Constant.Splat, rhs_data);
return std.meta.eql(lhs_key, rhs_extra);
}
};
const data = Constant.Splat{ .type = ty, .value = val };
const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self });
if (!gop.found_existing) {
gop.key_ptr.* = {};
gop.value_ptr.* = {};
self.constant_items.appendAssumeCapacity(.{
.tag = .splat,
.data = self.addConstantExtraAssumeCapacity(data),
});
if (self.useLibLlvm()) {
const ExpectedContents = [expected_fields_len]*llvm.Value;
var stack align(@alignOf(ExpectedContents)) =
std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa);
const allocator = stack.get();
const llvm_vals = try allocator.alloc(*llvm.Value, ty.vectorLen(self));
defer allocator.free(llvm_vals);
@memset(llvm_vals, val.toLlvm(self));
self.llvm.constants.appendAssumeCapacity(
llvm.constVector(llvm_vals.ptr, @intCast(llvm_vals.len)),
);
}
}
return @enumFromInt(gop.index);
}
fn zeroInitConstAssumeCapacity(self: *Builder, ty: Type) Constant {
switch (self.type_items.items[@intFromEnum(ty)].tag) {
switch (ty) {
inline .half,
.bfloat,
.float,
.double,
.fp128,
.x86_fp80,
=> |tag| return @field(Builder, @tagName(tag) ++ "ConstAssumeCapacity")(self, 0.0),
.ppc_fp128 => return self.ppc_fp128ConstAssumeCapacity(.{ 0.0, 0.0 }),
.token => return .none,
.i1 => return .false,
else => switch (self.type_items.items[@intFromEnum(ty)].tag) {
.simple,
.function,
.vararg_function,
.integer,
.pointer,
=> unreachable,
.integer => {
var limbs: [std.math.big.int.calcLimbLen(0)]std.math.big.Limb = undefined;
const bigint = std.math.big.int.Mutable.init(&limbs, 0);
return self.bigIntConstAssumeCapacity(ty, bigint.toConst()) catch unreachable;
},
.pointer => return self.nullConstAssumeCapacity(ty),
.target,
.vector,
.scalable_vector,
@ -3368,6 +3483,7 @@ fn zeroInitConstAssumeCapacity(self: *Builder, ty: Type) Constant {
.packed_structure,
.named_structure,
=> {},
},
}
const result = self.getOrPutConstantNoExtraAssumeCapacity(
.{ .tag = .zeroinitializer, .data = @intFromEnum(ty) },
@ -4034,7 +4150,10 @@ pub inline fn useLibLlvm(self: *const Builder) bool {
const assert = std.debug.assert;
const build_options = @import("build_options");
const builtin = @import("builtin");
const llvm = @import("bindings.zig");
const llvm = if (build_options.have_llvm)
@import("bindings.zig")
else
@compileError("LLVM unavailable");
const log = std.log.scoped(.llvm);
const std = @import("std");