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 DW = std.dwarf;
const Builder = @import("llvm/Builder.zig"); 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 link = @import("../link.zig");
const Compilation = @import("../Compilation.zig"); const Compilation = @import("../Compilation.zig");
const build_options = @import("build_options"); const build_options = @import("build_options");
@ -577,7 +580,7 @@ pub const Object = struct {
extern_collisions: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void), extern_collisions: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void),
/// Memoizes a null `?usize` value. /// Memoizes a null `?usize` value.
null_opt_addr: ?*llvm.Value, null_opt_usize: Builder.Constant,
pub const TypeMap = std.AutoHashMapUnmanaged(InternPool.Index, Builder.Type); pub const TypeMap = std.AutoHashMapUnmanaged(InternPool.Index, Builder.Type);
@ -733,7 +736,7 @@ pub const Object = struct {
.di_type_map = .{}, .di_type_map = .{},
.error_name_table = .none, .error_name_table = .none,
.extern_collisions = .{}, .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 name = try o.builder.string(mod.intern_pool.stringToSlice(name_nts));
const str_init = try o.builder.stringNullConst(name); const str_init = try o.builder.stringNullConst(name);
const str_ty = str_init.typeOf(&o.builder); const str_ty = str_init.typeOf(&o.builder);
const str_global = o.llvm_module.addGlobal(str_ty.toLlvm(&o.builder), ""); const str_llvm_global = o.llvm_module.addGlobal(str_ty.toLlvm(&o.builder), "");
str_global.setInitializer(str_init.toLlvm(&o.builder)); str_llvm_global.setInitializer(str_init.toLlvm(&o.builder));
str_global.setLinkage(.Private); str_llvm_global.setLinkage(.Private);
str_global.setGlobalConstant(.True); str_llvm_global.setGlobalConstant(.True);
str_global.setUnnamedAddr(.True); str_llvm_global.setUnnamedAddr(.True);
str_global.setAlignment(1); str_llvm_global.setAlignment(1);
var global = Builder.Global{ var str_global = Builder.Global{
.linkage = .private, .linkage = .private,
.unnamed_addr = .unnamed_addr, .unnamed_addr = .unnamed_addr,
.type = str_ty, .type = str_ty,
.alignment = comptime Builder.Alignment.fromByteUnits(1), .alignment = comptime Builder.Alignment.fromByteUnits(1),
.kind = .{ .variable = @enumFromInt(o.builder.variables.items.len) }, .kind = .{ .variable = @enumFromInt(o.builder.variables.items.len) },
}; };
var variable = Builder.Variable{ var str_variable = Builder.Variable{
.global = @enumFromInt(o.builder.globals.count()), .global = @enumFromInt(o.builder.globals.count()),
.mutability = .constant, .mutability = .constant,
.init = str_init, .init = str_init,
}; };
try o.builder.llvm.globals.append(o.gpa, str_global); try o.builder.llvm.globals.append(o.gpa, str_llvm_global);
const str_global_index = try o.builder.addGlobal(.empty, global); const global_index = try o.builder.addGlobal(.empty, str_global);
try o.builder.variables.append(o.gpa, variable); try o.builder.variables.append(o.gpa, str_variable);
llvm_error.* = try o.builder.structConst(llvm_slice_ty, &.{ 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), try o.builder.intConst(llvm_usize_ty, name.toSlice(&o.builder).?.len),
}); });
} }
@ -2684,55 +2687,6 @@ pub const Object = struct {
return buffer.toOwnedSliceSentinel(0); 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. /// If the llvm function does not exist, create it.
/// Note that this can be called before the function's semantic analysis has /// 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. /// completed, so if any attributes rely on that, they must be done in updateFunc, not here.
@ -3755,25 +3709,34 @@ pub const Object = struct {
}, },
}, },
.vector_type => |vector_type| { .vector_type => |vector_type| {
const ExpectedContents = [Builder.expected_fields_len]Builder.Constant; const vector_ty = try o.lowerType(ty);
var stack align(@max(
@alignOf(std.heap.StackFallbackAllocator(0)),
@alignOf(ExpectedContents),
)) = std.heap.stackFallback(@sizeOf(ExpectedContents), o.gpa);
const allocator = stack.get();
const vals = try allocator.alloc(Builder.Constant, vector_type.len);
defer allocator.free(vals);
switch (aggregate.storage) { switch (aggregate.storage) {
.bytes => |bytes| for (vals, bytes) |*result_val, byte| { .bytes, .elems => {
result_val.* = try o.builder.intConst(.i8, byte); const ExpectedContents = [Builder.expected_fields_len]Builder.Constant;
var stack align(@max(
@alignOf(std.heap.StackFallbackAllocator(0)),
@alignOf(ExpectedContents),
)) = std.heap.stackFallback(@sizeOf(ExpectedContents), o.gpa);
const allocator = stack.get();
const vals = try allocator.alloc(Builder.Constant, vector_type.len);
defer allocator.free(vals);
switch (aggregate.storage) {
.bytes => |bytes| for (vals, bytes) |*result_val, byte| {
result_val.* = try o.builder.intConst(.i8, byte);
},
.elems => |elems| for (vals, elems) |*result_val, elem| {
result_val.* = try o.lowerValue(elem);
},
.repeated_elem => unreachable,
}
return o.builder.vectorConst(vector_ty, vals);
}, },
.elems => |elems| for (vals, elems) |*result_val, elem| { .repeated_elem => |elem| return o.builder.splatConst(
result_val.* = try o.lowerValue(elem); vector_ty,
}, try o.lowerValue(elem),
.repeated_elem => |elem| @memset(vals, try o.lowerValue(elem)), ),
} }
return o.builder.vectorConst(try o.lowerType(ty), vals);
}, },
.anon_struct_type => |tuple| { .anon_struct_type => |tuple| {
const struct_ty = try o.lowerType(ty); const struct_ty = try o.lowerType(ty);
@ -4209,14 +4172,11 @@ pub const Object = struct {
else else
(try o.resolveGlobalDecl(decl_index)).ptrConst(&o.builder).global; (try o.resolveGlobalDecl(decl_index)).ptrConst(&o.builder).global;
const target = mod.getTarget(); const llvm_val = try o.builder.convConst(
const llvm_wanted_addrspace = toLlvmAddressSpace(decl.@"addrspace", target); .unneeded,
const llvm_actual_addrspace = toLlvmGlobalAddressSpace(decl.@"addrspace", target);
const llvm_val = if (llvm_wanted_addrspace != llvm_actual_addrspace) try o.builder.castConst(
.addrspacecast,
llvm_global.toConst(), llvm_global.toConst(),
try o.builder.ptrType(llvm_wanted_addrspace), try o.builder.ptrType(toLlvmAddressSpace(decl.@"addrspace", mod.getTarget())),
) else llvm_global.toConst(); );
return o.builder.convConst(if (ty.isAbiInt(mod)) switch (ty.intInfo(mod).signedness) { return o.builder.convConst(if (ty.isAbiInt(mod)) switch (ty.intInfo(mod).signedness) {
.signed => .signed, .signed => .signed,
@ -4618,15 +4578,15 @@ pub const FuncGen = struct {
.ty = self.typeOf(inst), .ty = self.typeOf(inst),
.val = (try self.air.value(inst, mod)).?, .val = (try self.air.value(inst, mod)).?,
}); });
gop.value_ptr.* = llvm_val; gop.value_ptr.* = llvm_val.toLlvm(&o.builder);
return llvm_val; 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 o = self.dg.object;
const mod = o.module; const mod = o.module;
const llvm_val = try o.lowerValue(tv.val.toIntern()); 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 // 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. // 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{ var global = Builder.Global{
.linkage = .private, .linkage = .private,
.unnamed_addr = .unnamed_addr, .unnamed_addr = .unnamed_addr,
.addr_space = llvm_actual_addrspace,
.type = llvm_ty, .type = llvm_ty,
.alignment = Builder.Alignment.fromByteUnits(llvm_alignment), .alignment = Builder.Alignment.fromByteUnits(llvm_alignment),
.kind = .{ .variable = @enumFromInt(o.builder.variables.items.len) }, .kind = .{ .variable = @enumFromInt(o.builder.variables.items.len) },
@ -4655,16 +4616,27 @@ pub const FuncGen = struct {
.init = llvm_val, .init = llvm_val,
}; };
try o.builder.llvm.globals.append(o.gpa, llvm_global); 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); try o.builder.variables.append(o.gpa, variable);
const addrspace_casted_ptr = if (llvm_actual_addrspace != llvm_wanted_addrspace) return try o.builder.convConst(
llvm_global.constAddrSpaceCast( .unneeded,
(try o.builder.ptrType(llvm_wanted_addrspace)).toLlvm(&o.builder), global_index.toConst(),
) try o.builder.ptrType(llvm_wanted_addrspace),
else );
llvm_global; }
return addrspace_casted_ptr;
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 { 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_decl = mod.declPtr(msg_decl_index);
const msg_len = msg_decl.ty.childType(mod).arrayLen(mod); const msg_len = msg_decl.ty.childType(mod).arrayLen(mod);
const msg_ptr = try o.lowerValue(try msg_decl.internValue(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 target = mod.getTarget();
const llvm_usize = try o.lowerType(Type.usize); const llvm_usize = try o.lowerType(Type.usize);
// example: // example:
@ -5257,7 +5229,7 @@ pub const FuncGen = struct {
msg_ptr.toLlvm(&o.builder), msg_ptr.toLlvm(&o.builder),
(try o.builder.intConst(llvm_usize, msg_len)).toLlvm(&o.builder), (try o.builder.intConst(llvm_usize, msg_len)).toLlvm(&o.builder),
(try o.builder.nullConst(.ptr)).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_func = mod.funcInfo(mod.panic_func_index);
const panic_decl = mod.declPtr(panic_func.owner_decl); 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 = try self.resolveInst(un_op);
const operand_ty = self.typeOf(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_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); const payload_ty = optional_ty.optionalChild(mod);
if (optional_ty.optionalReprIsPayload(mod)) { if (optional_ty.optionalReprIsPayload(mod)) {
const loaded = if (operand_is_ptr) const loaded = if (operand_is_ptr)
self.builder.buildLoad(optional_llvm_ty, operand, "") self.builder.buildLoad(optional_llvm_ty.toLlvm(&o.builder), operand, "")
else else
operand; operand;
if (payload_ty.isSlice(mod)) { 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, 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); comptime assert(optional_layout_version == 3);
if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
const loaded = if (operand_is_ptr) const loaded = if (operand_is_ptr)
self.builder.buildLoad(optional_llvm_ty, operand, "") self.builder.buildLoad(optional_llvm_ty.toLlvm(&o.builder), operand, "")
else else
operand; operand;
return self.builder.buildICmp(pred, loaded, (try o.builder.intConst(.i8, 0)).toLlvm(&o.builder), ""); 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 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) { if (pred == .EQ) {
return self.builder.buildNot(non_null_bit, ""); return self.builder.buildNot(non_null_bit, "");
} else { } else {
@ -7549,24 +7521,18 @@ pub const FuncGen = struct {
} }
if (scalar_ty.isSignedInt(mod)) { if (scalar_ty.isSignedInt(mod)) {
const inst_llvm_ty = try o.lowerType(inst_ty); 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 = try o.builder.splatConst(inst_llvm_ty, try o.builder.intConst(
const bit_size_minus_one = if (inst_ty.zigTypeTag(mod) == .Vector) const_vector: { inst_llvm_ty.scalarType(&o.builder),
const vec_len = inst_ty.vectorLen(mod); inst_llvm_ty.scalarBits(&o.builder) - 1,
));
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 div = self.builder.buildSDiv(lhs, rhs, ""); const div = self.builder.buildSDiv(lhs, rhs, "");
const rem = self.builder.buildSRem(lhs, rhs, ""); const rem = self.builder.buildSRem(lhs, rhs, "");
const div_sign = self.builder.buildXor(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 zero = inst_llvm_ty.toLlvm(&o.builder).constNull(); const zero = try o.builder.zeroInitConst(inst_llvm_ty);
const rem_nonzero = self.builder.buildICmp(.NE, rem, zero, ""); const rem_nonzero = self.builder.buildICmp(.NE, rem, zero.toLlvm(&o.builder), "");
const correction = self.builder.buildSelect(rem_nonzero, div_sign_mask, zero, ""); const correction = self.builder.buildSelect(rem_nonzero, div_sign_mask, zero.toLlvm(&o.builder), "");
return self.builder.buildNSWAdd(div, correction, ""); return self.builder.buildNSWAdd(div, correction, "");
} }
return self.builder.buildUDiv(lhs, rhs, ""); 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 a = try self.buildFloatOp(.fmod, inst_ty, 2, .{ lhs, rhs });
const b = try self.buildFloatOp(.add, inst_ty, 2, .{ a, rhs }); const b = try self.buildFloatOp(.add, inst_ty, 2, .{ a, rhs });
const c = try self.buildFloatOp(.fmod, inst_ty, 2, .{ b, rhs }); const c = try self.buildFloatOp(.fmod, inst_ty, 2, .{ b, rhs });
const zero = inst_llvm_ty.toLlvm(&o.builder).constNull(); const zero = try o.builder.zeroInitConst(inst_llvm_ty);
const ltz = try self.buildFloatCmp(.lt, inst_ty, .{ lhs, zero }); const ltz = try self.buildFloatCmp(.lt, inst_ty, .{ lhs, zero.toLlvm(&o.builder) });
return self.builder.buildSelect(ltz, c, a, ""); return self.builder.buildSelect(ltz, c, a, "");
} }
if (scalar_ty.isSignedInt(mod)) { if (scalar_ty.isSignedInt(mod)) {
const scalar_bit_size_minus_one = scalar_ty.bitSize(mod) - 1; const bit_size_minus_one = try o.builder.splatConst(inst_llvm_ty, try o.builder.intConst(
const bit_size_minus_one = if (inst_ty.zigTypeTag(mod) == .Vector) const_vector: { inst_llvm_ty.scalarType(&o.builder),
const vec_len = inst_ty.vectorLen(mod); inst_llvm_ty.scalarBits(&o.builder) - 1,
));
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 rem = self.builder.buildSRem(lhs, rhs, ""); const rem = self.builder.buildSRem(lhs, rhs, "");
const div_sign = self.builder.buildXor(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 rhs_masked = self.builder.buildAnd(rhs, div_sign_mask, "");
const zero = inst_llvm_ty.toLlvm(&o.builder).constNull(); const zero = try o.builder.zeroInitConst(inst_llvm_ty);
const rem_nonzero = self.builder.buildICmp(.NE, rem, zero, ""); const rem_nonzero = self.builder.buildICmp(.NE, rem, zero.toLlvm(&o.builder), "");
const correction = self.builder.buildSelect(rem_nonzero, rhs_masked, zero, ""); const correction = self.builder.buildSelect(rem_nonzero, rhs_masked, zero.toLlvm(&o.builder), "");
return self.builder.buildNSWAdd(rem, correction, ""); return self.builder.buildNSWAdd(rem, correction, "");
} }
return self.builder.buildURem(lhs, rhs, ""); 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 // In this case we can generate a softfloat negation by XORing the
// bits with a constant. // bits with a constant.
const int_ty = try o.builder.intType(@intCast(float_bits)); 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 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 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 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), ""); const bitcasted_operand = self.builder.buildBitCast(params[0], cast_ty.toLlvm(&o.builder), "");
break :blk self.builder.buildXor(bitcasted_operand, splat_sign_mask, ""); break :blk self.builder.buildXor(bitcasted_operand, splat_sign_mask, "");
} else blk: { } else blk: {
const bitcasted_operand = self.builder.buildBitCast(params[0], int_ty.toLlvm(&o.builder), ""); 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), ""); 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); const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty);
if (intrinsic_len0_traps) { 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 { } 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; return null;
} }
@ -8987,8 +8947,9 @@ pub const FuncGen = struct {
dest_ptr_align: u32, dest_ptr_align: u32,
is_volatile: bool, is_volatile: bool,
) !void { ) !void {
const llvm_usize_ty = self.context.intType(self.dg.object.target.ptrBitWidth()); const o = self.dg.object;
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 memset_block = self.context.appendBasicBlock(self.llvm_func, "MemsetTrapSkip"); const memset_block = self.context.appendBasicBlock(self.llvm_func, "MemsetTrapSkip");
const end_block = self.context.appendBasicBlock(self.llvm_func, "MemsetTrapEnd"); const end_block = self.context.appendBasicBlock(self.llvm_func, "MemsetTrapEnd");
_ = self.builder.buildCondBr(cond, memset_block, end_block); _ = 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 std.Target.wasm.featureSetHas(o.target.cpu.features, .bulk_memory) and
dest_ptr_ty.isSlice(mod)) dest_ptr_ty.isSlice(mod))
{ {
const llvm_usize_ty = self.context.intType(self.dg.object.target.ptrBitWidth()); const llvm_usize_ty = try o.lowerType(Type.usize);
const cond = try self.cmp(len, llvm_usize_ty.constInt(0, .False), Type.usize, .neq); 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 memcpy_block = self.context.appendBasicBlock(self.llvm_func, "MemcpyTrapSkip");
const end_block = self.context.appendBasicBlock(self.llvm_func, "MemcpyTrapEnd"); const end_block = self.context.appendBasicBlock(self.llvm_func, "MemcpyTrapEnd");
_ = self.builder.buildCondBr(cond, memcpy_block, end_block); _ = self.builder.buildCondBr(cond, memcpy_block, end_block);
@ -9183,19 +9144,13 @@ pub const FuncGen = struct {
if (operand_ty.zigTypeTag(mod) == .Vector) { if (operand_ty.zigTypeTag(mod) == .Vector) {
const vec_len = operand_ty.vectorLen(mod); const vec_len = operand_ty.vectorLen(mod);
operand_llvm_ty = try o.builder.vectorType(.normal, vec_len, scalar_ty); 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); const shift_amt =
defer self.gpa.free(shifts); try o.builder.splatConst(operand_llvm_ty, try o.builder.intConst(scalar_ty, 8));
@memset(shifts, (try o.builder.intConst(scalar_ty, 8)).toLlvm(&o.builder)); const extended = self.builder.buildZExt(operand, operand_llvm_ty.toLlvm(&o.builder), "");
const shift_vec = llvm.constVector(shifts.ptr, vec_len); operand = self.builder.buildShl(extended, shift_amt.toLlvm(&o.builder), "");
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;
}
bits = bits + 8; bits = bits + 8;
} }
@ -9358,11 +9313,8 @@ pub const FuncGen = struct {
const fqn = try mod.declPtr(enum_type.decl).getFullyQualifiedName(mod); 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 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(Type.slice_const_u8_sentinel_0);
const ret_ty = try o.lowerType(slice_ty);
const llvm_ret_ty = ret_ty.toLlvm(&o.builder);
const usize_ty = try o.lowerType(Type.usize); const usize_ty = try o.lowerType(Type.usize);
const slice_alignment = slice_ty.abiAlignment(mod);
const fn_type = try o.builder.fnType(ret_ty, &.{ const fn_type = try o.builder.fnType(ret_ty, &.{
try o.lowerType(enum_type.tag_ty.toType()), 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 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 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| { for (enum_type.names, 0..) |name_ip, field_index_usize| {
const field_index = @as(u32, @intCast(field_index_usize)); const field_index = @as(u32, @intCast(field_index_usize));
const name = mod.intern_pool.stringToSlice(name_ip); const name = try o.builder.string(mod.intern_pool.stringToSlice(name_ip));
const str_init = self.context.constString(name.ptr, @as(c_uint, @intCast(name.len)), .False); const str_init = try o.builder.stringNullConst(name);
const str_init_llvm_ty = str_init.typeOf(); const str_ty = str_init.typeOf(&o.builder);
const str_global = o.llvm_module.addGlobal(str_init_llvm_ty, ""); const str_llvm_global = o.llvm_module.addGlobal(str_ty.toLlvm(&o.builder), "");
str_global.setInitializer(str_init); str_llvm_global.setInitializer(str_init.toLlvm(&o.builder));
str_global.setLinkage(.Private); str_llvm_global.setLinkage(.Private);
str_global.setGlobalConstant(.True); str_llvm_global.setGlobalConstant(.True);
str_global.setUnnamedAddr(.True); str_llvm_global.setUnnamedAddr(.True);
str_global.setAlignment(1); str_llvm_global.setAlignment(1);
const slice_fields = [_]*llvm.Value{ var str_global = Builder.Global{
str_init_llvm_ty.constInBoundsGEP(str_global, &array_ptr_indices, array_ptr_indices.len), .linkage = .private,
(try o.builder.intConst(usize_ty, name.len)).toLlvm(&o.builder), .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); var str_variable = Builder.Variable{
const slice_global = o.llvm_module.addGlobal(slice_init.typeOf(), ""); .global = @enumFromInt(o.builder.globals.count()),
slice_global.setInitializer(slice_init); .mutability = .constant,
slice_global.setLinkage(.Private); .init = str_init,
slice_global.setGlobalConstant(.True); };
slice_global.setUnnamedAddr(.True); try o.builder.llvm.globals.append(o.gpa, str_llvm_global);
slice_global.setAlignment(slice_alignment); 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 return_block = self.context.appendBasicBlock(fn_val, "Name");
const this_tag_int_value = 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); switch_instr.addCase(this_tag_int_value.toLlvm(&o.builder), return_block);
self.builder.positionBuilderAtEnd(return_block); self.builder.positionBuilderAtEnd(return_block);
const loaded = self.builder.buildLoad(llvm_ret_ty, slice_global, ""); _ = self.builder.buildRet(slice_val.toLlvm(&o.builder));
loaded.setAlignment(slice_alignment);
_ = self.builder.buildRet(loaded);
} }
self.builder.positionBuilderAtEnd(bad_value_block); 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 // 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 // second vector. These start at -1 and go down, and are easiest to use
// with the ~ operator. Here we convert between the two formats. // 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); defer self.gpa.free(values);
for (values, 0..) |*val, i| { for (values, 0..) |*val, i| {
const elem = try mask.elemValue(mod, i); const elem = try mask.elemValue(mod, i);
if (elem.isUndef(mod)) { if (elem.isUndef(mod)) {
val.* = Builder.Type.i32.toLlvm(&o.builder).getUndef(); val.* = try o.builder.undefConst(.i32);
} else { } else {
const int = elem.toSignedInt(mod); const int = elem.toSignedInt(mod);
const unsigned = if (int >= 0) @as(u32, @intCast(int)) else @as(u32, @intCast(~int + a_len)); 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); const llvm_mask_value = try o.builder.vectorConst(
return self.builder.buildShuffleVector(a, b, llvm_mask_value, ""); 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. /// Reduce a vector by repeatedly applying `llvm_fn` to produce an accumulated result.
@ -9816,7 +9774,7 @@ pub const FuncGen = struct {
.val = sent_val, .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; return alloca_inst;
@ -10431,12 +10389,13 @@ pub const FuncGen = struct {
else else
self.builder.buildBitCast(elem, value_bits_type.toLlvm(&o.builder), ""); self.builder.buildBitCast(elem, value_bits_type.toLlvm(&o.builder), "");
var mask_val = (try o.builder.intConst(value_bits_type, -1)).toLlvm(&o.builder); var mask_val = try o.builder.intConst(value_bits_type, -1);
mask_val = mask_val.constZExt(containing_int_ty.toLlvm(&o.builder)); mask_val = try o.builder.castConst(.zext, mask_val, containing_int_ty);
mask_val = mask_val.constShl(shift_amt.toLlvm(&o.builder)); mask_val = try o.builder.binConst(.shl, mask_val, shift_amt);
mask_val = mask_val.constNot(); 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 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 shifted_value = self.builder.buildShl(extended_value, shift_amt.toLlvm(&o.builder), "");
const ored_value = self.builder.buildOr(shifted_value, anded_containing_int, ""); 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)) { return switch (self.tag(builder)) {
.function, .vararg_function => true, .function, .vararg_function => true,
else => false, 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)) { return switch (self.tag(builder)) {
.function => .normal, .function => .normal,
.vararg_function => .vararg, .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 { pub fn vectorLen(self: Type, builder: *const Builder) u32 {
const item = builder.type_items.items[@intFromEnum(self)]; const item = builder.type_items.items[@intFromEnum(self)];
return switch (item.tag) { 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 { 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); self.renameAssumeCapacity(new_name, builder);
} }
pub fn takeName(self: Index, other: Index, builder: *Builder) Allocator.Error!void { pub fn takeName(self: Index, other: Index, builder: *Builder) Allocator.Error!void {
try builder.ensureUnusedCapacityGlobal(.empty); try builder.ensureUnusedGlobalCapacity(.empty);
self.takeNameAssumeCapacity(other, builder); self.takeNameAssumeCapacity(other, builder);
} }
pub fn replace(self: Index, other: Index, builder: *Builder) Allocator.Error!void { pub fn replace(self: Index, other: Index, builder: *Builder) Allocator.Error!void {
try builder.ensureUnusedCapacityGlobal(.empty); try builder.ensureUnusedGlobalCapacity(.empty);
self.replaceAssumeCapacity(other, builder); self.replaceAssumeCapacity(other, builder);
} }
@ -1047,6 +1088,7 @@ pub const Constant = enum(u32) {
string, string,
string_null, string_null,
vector, vector,
splat,
zeroinitializer, zeroinitializer,
undef, undef,
poison, poison,
@ -1126,6 +1168,11 @@ pub const Constant = enum(u32) {
type: Type, type: Type,
}; };
pub const Splat = extern struct {
type: Type,
value: Constant,
};
pub const BlockAddress = extern struct { pub const BlockAddress = extern struct {
function: Function.Index, function: Function.Index,
block: Function.Block.Index, block: Function.Block.Index,
@ -1217,6 +1264,7 @@ pub const Constant = enum(u32) {
.array, .array,
.vector, .vector,
=> builder.constantExtraData(Aggregate, item.data).type, => builder.constantExtraData(Aggregate, item.data).type,
.splat => builder.constantExtraData(Splat, item.data).type,
.string, .string,
.string_null, .string_null,
=> builder.arrayTypeAssumeCapacity( => builder.arrayTypeAssumeCapacity(
@ -1270,28 +1318,10 @@ pub const Constant = enum(u32) {
}, },
.icmp, .fcmp => { .icmp, .fcmp => {
const ty = builder.constantExtraData(Compare, item.data).lhs.typeOf(builder); const ty = builder.constantExtraData(Compare, item.data).lhs.typeOf(builder);
return switch (ty) { return if (ty.isVector(builder)) switch (ty.vectorKind(builder)) {
.half, inline else => |kind| builder
.bfloat, .vectorTypeAssumeCapacity(kind, ty.vectorLen(builder), .i1),
.float, } else ty;
.double,
.fp128,
.x86_fp80,
.ppc_fp128,
.i1,
.i8,
.i16,
.i29,
.i32,
.i64,
.i80,
.i128,
=> ty,
else => if (ty.isVector(builder)) switch (ty.vectorKind(builder)) {
inline else => |kind| builder
.vectorTypeAssumeCapacity(kind, ty.vectorLen(builder), .i1),
} else ty,
};
}, },
.extractelement => builder.constantExtraData(ExtractElement, item.data) .extractelement => builder.constantExtraData(ExtractElement, item.data)
.arg.typeOf(builder).childType(builder), .arg.typeOf(builder).childType(builder),
@ -1479,7 +1509,6 @@ pub const Constant = enum(u32) {
const len = extra.data.type.aggregateLen(data.builder); const len = extra.data.type.aggregateLen(data.builder);
const vals: []const Constant = const vals: []const Constant =
@ptrCast(data.builder.constant_extra.items[extra.end..][0..len]); @ptrCast(data.builder.constant_extra.items[extra.end..][0..len]);
try writer.writeAll(switch (tag) { try writer.writeAll(switch (tag) {
.structure => "{ ", .structure => "{ ",
.packed_structure => "<{ ", .packed_structure => "<{ ",
@ -1499,6 +1528,16 @@ pub const Constant = enum(u32) {
else => unreachable, 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, inline .string,
.string_null, .string_null,
=> |tag| try writer.print("c{\"" ++ switch (tag) { => |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 { pub fn addGlobal(self: *Builder, name: String, global: Global) Allocator.Error!Global.Index {
assert(!name.isAnon()); assert(!name.isAnon());
try self.ensureUnusedTypeCapacity(1, null, 0); try self.ensureUnusedTypeCapacity(1, null, 0);
try self.ensureUnusedCapacityGlobal(name); try self.ensureUnusedGlobalCapacity(name);
return self.addGlobalAssumeCapacity(name, global); 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); 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 { 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); return self.zeroInitConstAssumeCapacity(ty);
} }
@ -2477,7 +2525,7 @@ fn isValidIdentifier(id: []const u8) bool {
return true; 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); if (self.useLibLlvm()) try self.llvm.globals.ensureUnusedCapacity(self.gpa, 1);
try self.string_map.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 + if (name.toSlice(self)) |id| try self.string_bytes.ensureUnusedCapacity(self.gpa, id.len +
@ -2571,6 +2619,7 @@ fn vectorTypeAssumeCapacity(
len: u32, len: u32,
child: Type, child: Type,
) Type { ) Type {
assert(child.isFloatingPoint() or child.isInteger(self) or child.isPointer(self));
const tag: Type.Tag = switch (kind) { const tag: Type.Tag = switch (kind) {
.normal => .vector, .normal => .vector,
.scalable => .scalable_vector, .scalable => .scalable_vector,
@ -3321,14 +3370,13 @@ fn vectorConstAssumeCapacity(
ty: Type, ty: Type,
vals: []const Constant, vals: []const Constant,
) if (build_options.have_llvm) Allocator.Error!Constant else Constant { ) if (build_options.have_llvm) Allocator.Error!Constant else Constant {
if (std.debug.runtime_safety) { assert(ty.isVector(self));
const type_item = self.type_items.items[@intFromEnum(ty)]; assert(ty.vectorLen(self) == vals.len);
assert(type_item.tag == .vector); for (vals) |val| assert(ty.childType(self) == val.typeOf(self));
const extra = self.typeExtraData(Type.Vector, type_item.data);
assert(extra.len == vals.len);
for (vals) |val| assert(extra.child == val.typeOf(self));
}
for (vals[1..]) |val| {
if (vals[0] != val) break;
} else return self.splatConstAssumeCapacity(ty, vals[0]);
for (vals) |val| { for (vals) |val| {
if (!val.isZeroInit(self)) break; if (!val.isZeroInit(self)) break;
} else return self.zeroInitConstAssumeCapacity(ty); } else return self.zeroInitConstAssumeCapacity(ty);
@ -3351,23 +3399,91 @@ fn vectorConstAssumeCapacity(
return result.constant; 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 { fn zeroInitConstAssumeCapacity(self: *Builder, ty: Type) Constant {
switch (self.type_items.items[@intFromEnum(ty)].tag) { switch (ty) {
.simple, inline .half,
.function, .bfloat,
.vararg_function, .float,
.integer, .double,
.pointer, .fp128,
=> unreachable, .x86_fp80,
.target, => |tag| return @field(Builder, @tagName(tag) ++ "ConstAssumeCapacity")(self, 0.0),
.vector, .ppc_fp128 => return self.ppc_fp128ConstAssumeCapacity(.{ 0.0, 0.0 }),
.scalable_vector, .token => return .none,
.small_array, .i1 => return .false,
.array, else => switch (self.type_items.items[@intFromEnum(ty)].tag) {
.structure, .simple,
.packed_structure, .function,
.named_structure, .vararg_function,
=> {}, => 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,
.small_array,
.array,
.structure,
.packed_structure,
.named_structure,
=> {},
},
} }
const result = self.getOrPutConstantNoExtraAssumeCapacity( const result = self.getOrPutConstantNoExtraAssumeCapacity(
.{ .tag = .zeroinitializer, .data = @intFromEnum(ty) }, .{ .tag = .zeroinitializer, .data = @intFromEnum(ty) },
@ -4034,7 +4150,10 @@ pub inline fn useLibLlvm(self: *const Builder) bool {
const assert = std.debug.assert; const assert = std.debug.assert;
const build_options = @import("build_options"); const build_options = @import("build_options");
const builtin = @import("builtin"); 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 log = std.log.scoped(.llvm);
const std = @import("std"); const std = @import("std");