Commit 97d69e3352

Andrew Kelley <andrew@ziglang.org>
2021-09-14 06:37:11
stage2: add array_to_slice AIR instruction
1 parent a9a21c5
src/codegen/llvm/bindings.zig
@@ -484,6 +484,15 @@ pub const Builder = opaque {
         DestTy: *const Type,
         Name: [*:0]const u8,
     ) *const Value;
+
+    pub const buildInsertValue = LLVMBuildInsertValue;
+    extern fn LLVMBuildInsertValue(
+        *const Builder,
+        AggVal: *const Value,
+        EltVal: *const Value,
+        Index: c_uint,
+        Name: [*:0]const u8,
+    ) *const Value;
 };
 
 pub const IntPredicate = enum(c_uint) {
src/codegen/c.zig
@@ -910,6 +910,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
             .switch_br        => try airSwitchBr(o, inst),
             .wrap_optional    => try airWrapOptional(o, inst),
             .struct_field_ptr => try airStructFieldPtr(o, inst),
+            .array_to_slice   => try airArrayToSlice(o, inst),
 
             .struct_field_ptr_index_0 => try airStructFieldPtrIndex(o, inst, 0),
             .struct_field_ptr_index_1 => try airStructFieldPtrIndex(o, inst, 1),
@@ -1860,6 +1861,23 @@ fn airIsErr(
     return local;
 }
 
+fn airArrayToSlice(o: *Object, inst: Air.Inst.Index) !CValue {
+    if (o.liveness.isUnused(inst))
+        return CValue.none;
+
+    const inst_ty = o.air.typeOfIndex(inst);
+    const local = try o.allocLocal(inst_ty, .Const);
+    const ty_op = o.air.instructions.items(.data)[inst].ty_op;
+    const writer = o.writer();
+    const operand = try o.resolveInst(ty_op.operand);
+    const array_len = o.air.typeOf(ty_op.operand).elemType().arrayLen();
+
+    try writer.writeAll(" = { .ptr = ");
+    try o.writeCValue(writer, operand);
+    try writer.print(", .len = {d} }};\n", .{array_len});
+    return local;
+}
+
 fn IndentWriter(comptime UnderlyingWriter: type) type {
     return struct {
         const Self = @This();
src/codegen/llvm.zig
@@ -1006,28 +1006,29 @@ pub const FuncGen = struct {
                 .is_err          => try self.airIsErr(inst, .NE, false),
                 .is_err_ptr      => try self.airIsErr(inst, .NE, true),
 
-                .alloc      => try self.airAlloc(inst),
-                .arg        => try self.airArg(inst),
-                .bitcast    => try self.airBitCast(inst),
-                .bool_to_int=> try self.airBoolToInt(inst),
-                .block      => try self.airBlock(inst),
-                .br         => try self.airBr(inst),
-                .switch_br  => try self.airSwitchBr(inst),
-                .breakpoint => try self.airBreakpoint(inst),
-                .call       => try self.airCall(inst),
-                .cond_br    => try self.airCondBr(inst),
-                .intcast    => try self.airIntCast(inst),
-                .trunc      => try self.airTrunc(inst),
-                .floatcast  => try self.airFloatCast(inst),
-                .ptrtoint   => try self.airPtrToInt(inst),
-                .load       => try self.airLoad(inst),
-                .loop       => try self.airLoop(inst),
-                .not        => try self.airNot(inst),
-                .ret        => try self.airRet(inst),
-                .store      => try self.airStore(inst),
-                .assembly   => try self.airAssembly(inst),
-                .slice_ptr  => try self.airSliceField(inst, 0),
-                .slice_len  => try self.airSliceField(inst, 1),
+                .alloc          => try self.airAlloc(inst),
+                .arg            => try self.airArg(inst),
+                .bitcast        => try self.airBitCast(inst),
+                .bool_to_int    => try self.airBoolToInt(inst),
+                .block          => try self.airBlock(inst),
+                .br             => try self.airBr(inst),
+                .switch_br      => try self.airSwitchBr(inst),
+                .breakpoint     => try self.airBreakpoint(inst),
+                .call           => try self.airCall(inst),
+                .cond_br        => try self.airCondBr(inst),
+                .intcast        => try self.airIntCast(inst),
+                .trunc          => try self.airTrunc(inst),
+                .floatcast      => try self.airFloatCast(inst),
+                .ptrtoint       => try self.airPtrToInt(inst),
+                .load           => try self.airLoad(inst),
+                .loop           => try self.airLoop(inst),
+                .not            => try self.airNot(inst),
+                .ret            => try self.airRet(inst),
+                .store          => try self.airStore(inst),
+                .assembly       => try self.airAssembly(inst),
+                .slice_ptr      => try self.airSliceField(inst, 0),
+                .slice_len      => try self.airSliceField(inst, 1),
+                .array_to_slice => try self.airArrayToSlice(inst),
 
                 .struct_field_ptr => try self.airStructFieldPtr(inst),
                 .struct_field_val => try self.airStructFieldVal(inst),
@@ -1246,6 +1247,24 @@ pub const FuncGen = struct {
         return null;
     }
 
+    fn airArrayToSlice(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 operand = try self.resolveInst(ty_op.operand);
+        const array_len = self.air.typeOf(ty_op.operand).elemType().arrayLen();
+        const usize_llvm_ty = try self.dg.llvmType(Type.initTag(.usize));
+        const len = usize_llvm_ty.constInt(array_len, .False);
+        const slice_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst));
+        const indices: [2]*const llvm.Value = .{
+            usize_llvm_ty.constNull(), usize_llvm_ty.constNull(),
+        };
+        const ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
+        const partial = self.builder.buildInsertValue(slice_llvm_ty.getUndef(), ptr, 0, "");
+        return self.builder.buildInsertValue(partial, len, 1, "");
+    }
+
     fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value {
         if (self.liveness.isUnused(inst))
             return null;
src/Air.zig
@@ -306,6 +306,9 @@ pub const Inst = struct {
         /// Result type is the element type of the inner pointer operand.
         /// Uses the `bin_op` field.
         ptr_ptr_elem_val,
+        /// Given a pointer to an array, return a slice.
+        /// Uses the `ty_op` field.
+        array_to_slice,
 
         pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
             return switch (op) {
@@ -526,6 +529,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
         .struct_field_ptr_index_1,
         .struct_field_ptr_index_2,
         .struct_field_ptr_index_3,
+        .array_to_slice,
         => return air.getRefType(datas[inst].ty_op.ty),
 
         .loop,
src/codegen.zig
@@ -856,6 +856,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                     .store           => try self.airStore(inst),
                     .struct_field_ptr=> try self.airStructFieldPtr(inst),
                     .struct_field_val=> try self.airStructFieldVal(inst),
+                    .array_to_slice  => try self.airArrayToSlice(inst),
 
                     .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
                     .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),
@@ -4761,6 +4762,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
             return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
         }
 
+        fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
+            const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+            const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
+                else => return self.fail("TODO implement airArrayToSlice for {}", .{
+                    self.target.cpu.arch,
+                }),
+            };
+            return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+        }
+
         fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
             // First section of indexes correspond to a set number of constant values.
             const ref_int = @enumToInt(inst);
src/Liveness.zig
@@ -287,6 +287,7 @@ fn analyzeInst(
         .struct_field_ptr_index_1,
         .struct_field_ptr_index_2,
         .struct_field_ptr_index_3,
+        .array_to_slice,
         => {
             const o = inst_datas[inst].ty_op;
             return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });
src/print_air.zig
@@ -174,6 +174,7 @@ const Writer = struct {
             .struct_field_ptr_index_1,
             .struct_field_ptr_index_2,
             .struct_field_ptr_index_3,
+            .array_to_slice,
             => try w.writeTyOp(s, inst),
 
             .block,
src/Sema.zig
@@ -8952,7 +8952,8 @@ fn coerceArrayPtrToSlice(
         // The comptime Value representation is compatible with both types.
         return sema.addConstant(dest_type, val);
     }
-    return sema.mod.fail(&block.base, inst_src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
+    try sema.requireRuntimeBlock(block, inst_src);
+    return block.addTyOp(.array_to_slice, dest_type, inst);
 }
 
 fn coerceArrayPtrToMany(
test/behavior/array.zig
@@ -3,3 +3,27 @@ const testing = std.testing;
 const mem = std.mem;
 const expect = testing.expect;
 const expectEqual = testing.expectEqual;
+
+test "arrays" {
+    var array: [5]u32 = undefined;
+
+    var i: u32 = 0;
+    while (i < 5) {
+        array[i] = i + 1;
+        i = array[i];
+    }
+
+    i = 0;
+    var accumulator = @as(u32, 0);
+    while (i < 5) {
+        accumulator += array[i];
+
+        i += 1;
+    }
+
+    try expect(accumulator == 15);
+    try expect(getArrayLen(&array) == 5);
+}
+fn getArrayLen(a: []const u32) usize {
+    return a.len;
+}
test/behavior/array_stage1.zig
@@ -4,30 +4,6 @@ const mem = std.mem;
 const expect = testing.expect;
 const expectEqual = testing.expectEqual;
 
-test "arrays" {
-    var array: [5]u32 = undefined;
-
-    var i: u32 = 0;
-    while (i < 5) {
-        array[i] = i + 1;
-        i = array[i];
-    }
-
-    i = 0;
-    var accumulator = @as(u32, 0);
-    while (i < 5) {
-        accumulator += array[i];
-
-        i += 1;
-    }
-
-    try expect(accumulator == 15);
-    try expect(getArrayLen(&array) == 5);
-}
-fn getArrayLen(a: []const u32) usize {
-    return a.len;
-}
-
 test "array with sentinels" {
     const S = struct {
         fn doTheTest(is_ct: bool) !void {
@@ -64,12 +40,7 @@ test "void arrays" {
 }
 
 test "array literal" {
-    const hex_mult = [_]u16{
-        4096,
-        256,
-        16,
-        1,
-    };
+    const hex_mult = [_]u16{ 4096, 256, 16, 1 };
 
     try expect(hex_mult.len == 4);
     try expect(hex_mult[1] == 256);
@@ -84,21 +55,10 @@ test "array dot len const expr" {
 const ArrayDotLenConstExpr = struct {
     y: [some_array.len]u8,
 };
-const some_array = [_]u8{
-    0,
-    1,
-    2,
-    3,
-};
+const some_array = [_]u8{ 0, 1, 2, 3 };
 
 test "nested arrays" {
-    const array_of_strings = [_][]const u8{
-        "hello",
-        "this",
-        "is",
-        "my",
-        "thing",
-    };
+    const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };
     for (array_of_strings) |s, i| {
         if (i == 0) try expect(mem.eql(u8, s, "hello"));
         if (i == 1) try expect(mem.eql(u8, s, "this"));
@@ -109,12 +69,8 @@ test "nested arrays" {
 }
 
 var s_array: [8]Sub = undefined;
-const Sub = struct {
-    b: u8,
-};
-const Str = struct {
-    a: []Sub,
-};
+const Sub = struct { b: u8 };
+const Str = struct { a: []Sub };
 test "set global var array via slice embedded in struct" {
     var s = Str{ .a = s_array[0..] };
 
@@ -208,26 +164,10 @@ test "runtime initialize array elem and then implicit cast to slice" {
 test "array literal as argument to function" {
     const S = struct {
         fn entry(two: i32) !void {
-            try foo(&[_]i32{
-                1,
-                2,
-                3,
-            });
-            try foo(&[_]i32{
-                1,
-                two,
-                3,
-            });
-            try foo2(true, &[_]i32{
-                1,
-                2,
-                3,
-            });
-            try foo2(true, &[_]i32{
-                1,
-                two,
-                3,
-            });
+            try foo(&[_]i32{ 1, 2, 3 });
+            try foo(&[_]i32{ 1, two, 3 });
+            try foo2(true, &[_]i32{ 1, 2, 3 });
+            try foo2(true, &[_]i32{ 1, two, 3 });
         }
         fn foo(x: []const i32) !void {
             try expect(x[0] == 1);