aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/llvm.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-01-12 23:53:26 -0700
committerAndrew Kelley <andrew@ziglang.org>2022-01-12 23:53:26 -0700
commit93b854eb745ab3294054ae71150fe60f134f4d10 (patch)
treed40e26fcf2524c70c30302f0b503d14a0cdf4e51 /src/codegen/llvm.zig
parentc4681b4889652d5228a84ac7af5ad5e17ac39055 (diff)
downloadzig-93b854eb745ab3294054ae71150fe60f134f4d10.tar.gz
zig-93b854eb745ab3294054ae71150fe60f134f4d10.zip
stage2: implement `@ctz` and `@clz` including SIMD
AIR: * `array_elem_val` is now allowed to be used with a vector as the array type. * New instructions: splat, vector_init AstGen: * The splat ZIR instruction uses coerced_ty for the ResultLoc, avoiding an unnecessary `as` instruction, since the coercion will be performed in Sema. * Builtins that accept vectors now ignore the type parameter. Comment from this commit reproduced here: The accepted proposal #6835 tells us to remove the type parameter from these builtins. To stay source-compatible with stage1, we still observe the parameter here, but we do not encode it into the ZIR. To implement this proposal in stage2, only AstGen code will need to be changed. Sema: * `clz` and `ctz` ZIR instructions are now handled by the same function which accept AIR tag and comptime eval function pointer to differentiate. * `@typeInfo` for vectors is implemented. * `@splat` is implemented. It takes advantage of `Value.Tag.repeated` 😎 * `elemValue` is implemented for vectors, when the index is a scalar. Handling a vector index is still TODO. * Element-wise coercion is implemented for vectors. It could probably be optimized a bit, but it is at least complete & correct. * `Type.intInfo` supports vectors, returning int info for the element. * `Value.ctz` initial implementation. Needs work. * `Value.eql` is implemented for arrays and vectors. LLVM backend: * Implement vector support when lowering `array_elem_val`. * Implement vector support when lowering `ctz` and `clz`. * Implement `splat` and `vector_init`.
Diffstat (limited to 'src/codegen/llvm.zig')
-rw-r--r--src/codegen/llvm.zig74
1 files changed, 63 insertions, 11 deletions
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 4a3ac80b70..7b94410912 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -2089,6 +2089,8 @@ pub const FuncGen = struct {
.popcount => try self.airPopCount(inst, "ctpop"),
.tag_name => try self.airTagName(inst),
.error_name => try self.airErrorName(inst),
+ .splat => try self.airSplat(inst),
+ .vector_init => try self.airVectorInit(inst),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
.atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@@ -2612,15 +2614,19 @@ pub const FuncGen = struct {
const array_ty = self.air.typeOf(bin_op.lhs);
const array_llvm_val = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
- assert(isByRef(array_ty));
- const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs };
- const elem_ptr = self.builder.buildInBoundsGEP(array_llvm_val, &indices, indices.len, "");
- const elem_ty = array_ty.childType();
- if (isByRef(elem_ty)) {
- return elem_ptr;
- } else {
- return self.builder.buildLoad(elem_ptr, "");
+ if (isByRef(array_ty)) {
+ const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs };
+ const elem_ptr = self.builder.buildInBoundsGEP(array_llvm_val, &indices, indices.len, "");
+ const elem_ty = array_ty.childType();
+ if (isByRef(elem_ty)) {
+ return elem_ptr;
+ } else {
+ return self.builder.buildLoad(elem_ptr, "");
+ }
}
+
+ // This branch can be reached for vectors, which are always by-value.
+ return self.builder.buildExtractElement(array_llvm_val, rhs, "");
}
fn airPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
@@ -4163,11 +4169,20 @@ pub const FuncGen = struct {
const operand = try self.resolveInst(ty_op.operand);
const target = self.dg.module.getTarget();
const bits = operand_ty.intInfo(target).bits;
+ const vec_len: ?u32 = switch (operand_ty.zigTypeTag()) {
+ .Vector => @intCast(u32, operand_ty.arrayLen()),
+ else => null,
+ };
var fn_name_buf: [100]u8 = undefined;
- const llvm_fn_name = std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.i{d}", .{
- prefix, bits,
- }) catch unreachable;
+ const llvm_fn_name = if (vec_len) |len|
+ std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.v{d}i{d}", .{
+ prefix, len, bits,
+ }) catch unreachable
+ else
+ std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.i{d}", .{
+ prefix, bits,
+ }) catch unreachable;
const llvm_i1 = self.context.intType(1);
const fn_val = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
const operand_llvm_ty = try self.dg.llvmType(operand_ty);
@@ -4350,6 +4365,43 @@ pub const FuncGen = struct {
return self.builder.buildLoad(error_name_ptr, "");
}
+ fn airSplat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+ const scalar = try self.resolveInst(ty_op.operand);
+ const scalar_ty = self.air.typeOf(ty_op.operand);
+ const vector_ty = self.air.typeOfIndex(inst);
+ const len = @intCast(u32, vector_ty.arrayLen());
+ const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);
+ const op_llvm_ty = scalar_llvm_ty.vectorType(1);
+ const u32_llvm_ty = self.context.intType(32);
+ const mask_llvm_ty = u32_llvm_ty.vectorType(len);
+ const undef_vector = op_llvm_ty.getUndef();
+ const u32_zero = u32_llvm_ty.constNull();
+ const op_vector = self.builder.buildInsertElement(undef_vector, scalar, u32_zero, "");
+ return self.builder.buildShuffleVector(op_vector, undef_vector, mask_llvm_ty.constNull(), "");
+ }
+
+ fn airVectorInit(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+ const vector_ty = self.air.typeOfIndex(inst);
+ const len = vector_ty.arrayLen();
+ const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
+ const llvm_vector_ty = try self.dg.llvmType(vector_ty);
+ const llvm_u32 = self.context.intType(32);
+
+ var vector = llvm_vector_ty.getUndef();
+ for (elements) |elem, i| {
+ const index_u32 = llvm_u32.constInt(i, .False);
+ const llvm_elem = try self.resolveInst(elem);
+ vector = self.builder.buildInsertElement(vector, llvm_elem, index_u32, "");
+ }
+ return vector;
+ }
+
fn getErrorNameTable(self: *FuncGen) !*const llvm.Value {
if (self.dg.object.error_name_table) |table| {
return table;