Commit d193ba9843

Robin Voetter <robin@voetter.nl>
2021-10-16 17:23:30
stage2: array->vector coercion
1 parent 9336a87
Changed files (3)
src/codegen/llvm/bindings.zig
@@ -567,6 +567,15 @@ pub const Builder = opaque {
         Name: [*:0]const u8,
     ) *const Value;
 
+    pub const buildInsertElement = LLVMBuildInsertElement;
+    extern fn LLVMBuildInsertElement(
+        *const Builder,
+        VecVal: *const Value,
+        EltVal: *const Value,
+        Index: *const Value,
+        Name: [*:0]const u8,
+    ) *const Value;
+
     pub const buildPtrToInt = LLVMBuildPtrToInt;
     extern fn LLVMBuildPtrToInt(
         *const Builder,
src/codegen/llvm.zig
@@ -2889,6 +2889,43 @@ pub const FuncGen = struct {
                 }
             }
             return array_ptr;
+        } else if (operand_ty.zigTypeTag() == .Array and inst_ty.zigTypeTag() == .Vector) {
+            const target = self.dg.module.getTarget();
+            const elem_ty = operand_ty.childType();
+            const llvm_vector_ty = try self.dg.llvmType(inst_ty);
+            if (!isByRef(operand_ty)) {
+                return self.dg.todo("implement bitcast non-ref array to vector", .{});
+            }
+
+            const bitcast_ok = elem_ty.bitSize(target) == elem_ty.abiSize(target) * 8;
+            if (bitcast_ok) {
+                const llvm_vector_ptr_ty = llvm_vector_ty.pointerType(0);
+                const casted_ptr = self.builder.buildBitCast(operand, llvm_vector_ptr_ty, "");
+                const vector = self.builder.buildLoad(casted_ptr, "");
+                // The array is aligned to the element's alignment, while the vector might have a completely
+                // different alignment. This means we need to enforce the alignment of this load.
+                vector.setAlignment(elem_ty.abiAlignment(target));
+                return vector;
+            } else {
+                // If the ABI size of the element type is not evenly divisible by size in bits;
+                // a simple bitcast will not work, and we fall back to extractelement.
+                const llvm_usize = try self.dg.llvmType(Type.usize);
+                const llvm_u32 = self.context.intType(32);
+                const zero = llvm_usize.constNull();
+                const vector_len = operand_ty.arrayLen();
+                var vector = llvm_vector_ty.getUndef();
+                var i: u64 = 0;
+                while (i < vector_len) : (i += 1) {
+                    const index_usize = llvm_usize.constInt(i, .False);
+                    const index_u32 = llvm_u32.constInt(i, .False);
+                    const indexes: [2]*const llvm.Value = .{ zero, index_usize };
+                    const elem_ptr = self.builder.buildInBoundsGEP(operand, &indexes, indexes.len, "");
+                    const elem = self.builder.buildLoad(elem_ptr, "");
+                    vector = self.builder.buildInsertElement(vector, elem, index_u32, "");
+                }
+
+                return vector;
+            }
         }
 
         return self.builder.buildBitCast(operand, llvm_dest_ty, "");
src/Sema.zig
@@ -11653,7 +11653,11 @@ fn coerce(
             else => {},
         },
         .Array => switch (inst_ty.zigTypeTag()) {
-            .Vector => return sema.coerceVectorToArray(block, dest_ty, dest_ty_src, inst, inst_src),
+            .Vector => return sema.coerceVectorInMemory(block, dest_ty, dest_ty_src, inst, inst_src),
+            else => {},
+        },
+        .Vector => switch (inst_ty.zigTypeTag()) {
+            .Array => return sema.coerceVectorInMemory(block, dest_ty, dest_ty_src, inst, inst_src),
             else => {},
         },
         else => {},
@@ -12237,46 +12241,49 @@ fn coerceEnumToUnion(
     return sema.failWithOwnedErrorMsg(msg);
 }
 
-fn coerceVectorToArray(
+// Coerces vectors/arrays which have the same in-memory layout. This can be used for
+// both coercing from and to vectors.
+fn coerceVectorInMemory(
     sema: *Sema,
     block: *Block,
-    array_ty: Type,
-    array_ty_src: LazySrcLoc,
-    vector: Air.Inst.Ref,
-    vector_src: LazySrcLoc,
+    dest_ty: Type,
+    dest_ty_src: LazySrcLoc,
+    inst: Air.Inst.Ref,
+    inst_src: LazySrcLoc,
 ) !Air.Inst.Ref {
-    const vector_ty = sema.typeOf(vector);
-    const array_len = array_ty.arrayLen();
-    const vector_len = vector_ty.arrayLen();
-    if (array_len != vector_len) {
+    const inst_ty = sema.typeOf(inst);
+    const inst_len = inst_ty.arrayLen();
+    const dest_len = dest_ty.arrayLen();
+
+    if (dest_len != inst_len) {
         const msg = msg: {
-            const msg = try sema.errMsg(block, vector_src, "expected {}, found {}", .{
-                array_ty, vector_ty,
+            const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{
+                dest_ty, inst_ty,
             });
             errdefer msg.destroy(sema.gpa);
-            try sema.errNote(block, array_ty_src, msg, "array has length {d}", .{array_len});
-            try sema.errNote(block, vector_src, msg, "vector has length {d}", .{vector_len});
+            try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len});
+            try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});
             break :msg msg;
         };
         return sema.failWithOwnedErrorMsg(msg);
     }
 
     const target = sema.mod.getTarget();
-    const array_elem_ty = array_ty.childType();
-    const vector_elem_ty = vector_ty.childType();
-    const in_memory_result = coerceInMemoryAllowed(array_elem_ty, vector_elem_ty, false, target);
+    const dest_elem_ty = dest_ty.childType();
+    const inst_elem_ty = inst_ty.childType();
+    const in_memory_result = coerceInMemoryAllowed(dest_elem_ty, inst_elem_ty, false, target);
     if (in_memory_result != .ok) {
         // TODO recursive error notes for coerceInMemoryAllowed failure
-        return sema.fail(block, vector_src, "expected {}, found {}", .{ array_ty, vector_ty });
+        return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty, inst_ty });
     }
 
-    if (try sema.resolveMaybeUndefVal(block, vector_src, vector)) |vector_val| {
+    if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {
         // These types share the same comptime value representation.
-        return sema.addConstant(array_ty, vector_val);
+        return sema.addConstant(dest_ty, inst_val);
     }
 
-    try sema.requireRuntimeBlock(block, vector_src);
-    return block.addTyOp(.bitcast, array_ty, vector);
+    try sema.requireRuntimeBlock(block, inst_src);
+    return block.addTyOp(.bitcast, dest_ty, inst);
 }
 
 fn coerceErrSetToAnyError(