From 97d69e3352ab50f88580c383b5f375b0edadacfd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Sep 2021 21:37:11 -0700 Subject: [PATCH] stage2: add array_to_slice AIR instruction --- src/Air.zig | 4 ++ src/Liveness.zig | 1 + src/Sema.zig | 3 +- src/codegen.zig | 11 +++++ src/codegen/c.zig | 18 ++++++++ src/codegen/llvm.zig | 63 +++++++++++++++++---------- src/codegen/llvm/bindings.zig | 9 ++++ src/print_air.zig | 1 + test/behavior/array.zig | 24 +++++++++++ test/behavior/array_stage1.zig | 78 ++++------------------------------ 10 files changed, 120 insertions(+), 92 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 60e5fdce1c..bc939f6b04 100644 --- a/src/Air.zig +++ b/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, diff --git a/src/Liveness.zig b/src/Liveness.zig index 2f1ecc9c43..f33c44d592 100644 --- a/src/Liveness.zig +++ b/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 }); diff --git a/src/Sema.zig b/src/Sema.zig index 136a687774..ec163dd0a6 100644 --- a/src/Sema.zig +++ b/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( diff --git a/src/codegen.zig b/src/codegen.zig index 0a8797d887..e28192ec1f 100644 --- a/src/codegen.zig +++ b/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); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index fd964f2829..84bc6ceeb0 100644 --- a/src/codegen/c.zig +++ b/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(); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 8b7fd5dc54..d39262d62e 100644 --- a/src/codegen/llvm.zig +++ b/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; diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index a10002b5d6..e0283be236 100644 --- a/src/codegen/llvm/bindings.zig +++ b/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) { diff --git a/src/print_air.zig b/src/print_air.zig index 0106d0a0f3..4d9ce1bb36 100644 --- a/src/print_air.zig +++ b/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, diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 232ba87f55..8cf0042d5f 100644 --- a/test/behavior/array.zig +++ b/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; +} diff --git a/test/behavior/array_stage1.zig b/test/behavior/array_stage1.zig index 84a2cdb36c..d3a97665ac 100644 --- a/test/behavior/array_stage1.zig +++ b/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);