| author | |
| committer | |
| log | 97d69e3352ab50f88580c383b5f375b0edadacfd |
| tree | 8245d3d55b3304c19ca18238a01d6532031e1ff8 |
| parent | a9a21c59888905e060915dee818633110cc54cfa |
10 files changed, 120 insertions(+), 92 deletions(-)
src/Air.zig+4| ... | ... | @@ -306,6 +306,9 @@ pub const Inst = struct { |
| 306 | 306 | /// Result type is the element type of the inner pointer operand. |
| 307 | 307 | /// Uses the `bin_op` field. |
| 308 | 308 | ptr_ptr_elem_val, |
| 309 | /// Given a pointer to an array, return a slice. | |
| 310 | /// Uses the `ty_op` field. | |
| 311 | array_to_slice, | |
| 309 | 312 | |
| 310 | 313 | pub fn fromCmpOp(op: std.math.CompareOperator) Tag { |
| 311 | 314 | return switch (op) { |
| ... | ... | @@ -526,6 +529,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 526 | 529 | .struct_field_ptr_index_1, |
| 527 | 530 | .struct_field_ptr_index_2, |
| 528 | 531 | .struct_field_ptr_index_3, |
| 532 | .array_to_slice, | |
| 529 | 533 | => return air.getRefType(datas[inst].ty_op.ty), |
| 530 | 534 | |
| 531 | 535 | .loop, |
src/Liveness.zig+1| ... | ... | @@ -287,6 +287,7 @@ fn analyzeInst( |
| 287 | 287 | .struct_field_ptr_index_1, |
| 288 | 288 | .struct_field_ptr_index_2, |
| 289 | 289 | .struct_field_ptr_index_3, |
| 290 | .array_to_slice, | |
| 290 | 291 | => { |
| 291 | 292 | const o = inst_datas[inst].ty_op; |
| 292 | 293 | return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none }); |
src/Sema.zig+2-1| ... | ... | @@ -8952,7 +8952,8 @@ fn coerceArrayPtrToSlice( |
| 8952 | 8952 | // The comptime Value representation is compatible with both types. |
| 8953 | 8953 | return sema.addConstant(dest_type, val); |
| 8954 | 8954 | } |
| 8955 | return sema.mod.fail(&block.base, inst_src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{}); | |
| 8955 | try sema.requireRuntimeBlock(block, inst_src); | |
| 8956 | return block.addTyOp(.array_to_slice, dest_type, inst); | |
| 8956 | 8957 | } |
| 8957 | 8958 | |
| 8958 | 8959 | fn coerceArrayPtrToMany( |
src/codegen.zig+11| ... | ... | @@ -856,6 +856,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 856 | 856 | .store => try self.airStore(inst), |
| 857 | 857 | .struct_field_ptr=> try self.airStructFieldPtr(inst), |
| 858 | 858 | .struct_field_val=> try self.airStructFieldVal(inst), |
| 859 | .array_to_slice => try self.airArrayToSlice(inst), | |
| 859 | 860 | |
| 860 | 861 | .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), |
| 861 | 862 | .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), |
| ... | ... | @@ -4761,6 +4762,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4761 | 4762 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 4762 | 4763 | } |
| 4763 | 4764 | |
| 4765 | fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { | |
| 4766 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 4767 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | |
| 4768 | else => return self.fail("TODO implement airArrayToSlice for {}", .{ | |
| 4769 | self.target.cpu.arch, | |
| 4770 | }), | |
| 4771 | }; | |
| 4772 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | |
| 4773 | } | |
| 4774 | ||
| 4764 | 4775 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 4765 | 4776 | // First section of indexes correspond to a set number of constant values. |
| 4766 | 4777 | const ref_int = @enumToInt(inst); |
src/codegen/c.zig+18| ... | ... | @@ -910,6 +910,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM |
| 910 | 910 | .switch_br => try airSwitchBr(o, inst), |
| 911 | 911 | .wrap_optional => try airWrapOptional(o, inst), |
| 912 | 912 | .struct_field_ptr => try airStructFieldPtr(o, inst), |
| 913 | .array_to_slice => try airArrayToSlice(o, inst), | |
| 913 | 914 | |
| 914 | 915 | .struct_field_ptr_index_0 => try airStructFieldPtrIndex(o, inst, 0), |
| 915 | 916 | .struct_field_ptr_index_1 => try airStructFieldPtrIndex(o, inst, 1), |
| ... | ... | @@ -1860,6 +1861,23 @@ fn airIsErr( |
| 1860 | 1861 | return local; |
| 1861 | 1862 | } |
| 1862 | 1863 | |
| 1864 | fn airArrayToSlice(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1865 | if (o.liveness.isUnused(inst)) | |
| 1866 | return CValue.none; | |
| 1867 | ||
| 1868 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1869 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1870 | const ty_op = o.air.instructions.items(.data)[inst].ty_op; | |
| 1871 | const writer = o.writer(); | |
| 1872 | const operand = try o.resolveInst(ty_op.operand); | |
| 1873 | const array_len = o.air.typeOf(ty_op.operand).elemType().arrayLen(); | |
| 1874 | ||
| 1875 | try writer.writeAll(" = { .ptr = "); | |
| 1876 | try o.writeCValue(writer, operand); | |
| 1877 | try writer.print(", .len = {d} }};\n", .{array_len}); | |
| 1878 | return local; | |
| 1879 | } | |
| 1880 | ||
| 1863 | 1881 | fn IndentWriter(comptime UnderlyingWriter: type) type { |
| 1864 | 1882 | return struct { |
| 1865 | 1883 | const Self = @This(); |
src/codegen/llvm.zig+41-22| ... | ... | @@ -1006,28 +1006,29 @@ pub const FuncGen = struct { |
| 1006 | 1006 | .is_err => try self.airIsErr(inst, .NE, false), |
| 1007 | 1007 | .is_err_ptr => try self.airIsErr(inst, .NE, true), |
| 1008 | 1008 | |
| 1009 | .alloc => try self.airAlloc(inst), | |
| 1010 | .arg => try self.airArg(inst), | |
| 1011 | .bitcast => try self.airBitCast(inst), | |
| 1012 | .bool_to_int=> try self.airBoolToInt(inst), | |
| 1013 | .block => try self.airBlock(inst), | |
| 1014 | .br => try self.airBr(inst), | |
| 1015 | .switch_br => try self.airSwitchBr(inst), | |
| 1016 | .breakpoint => try self.airBreakpoint(inst), | |
| 1017 | .call => try self.airCall(inst), | |
| 1018 | .cond_br => try self.airCondBr(inst), | |
| 1019 | .intcast => try self.airIntCast(inst), | |
| 1020 | .trunc => try self.airTrunc(inst), | |
| 1021 | .floatcast => try self.airFloatCast(inst), | |
| 1022 | .ptrtoint => try self.airPtrToInt(inst), | |
| 1023 | .load => try self.airLoad(inst), | |
| 1024 | .loop => try self.airLoop(inst), | |
| 1025 | .not => try self.airNot(inst), | |
| 1026 | .ret => try self.airRet(inst), | |
| 1027 | .store => try self.airStore(inst), | |
| 1028 | .assembly => try self.airAssembly(inst), | |
| 1029 | .slice_ptr => try self.airSliceField(inst, 0), | |
| 1030 | .slice_len => try self.airSliceField(inst, 1), | |
| 1009 | .alloc => try self.airAlloc(inst), | |
| 1010 | .arg => try self.airArg(inst), | |
| 1011 | .bitcast => try self.airBitCast(inst), | |
| 1012 | .bool_to_int => try self.airBoolToInt(inst), | |
| 1013 | .block => try self.airBlock(inst), | |
| 1014 | .br => try self.airBr(inst), | |
| 1015 | .switch_br => try self.airSwitchBr(inst), | |
| 1016 | .breakpoint => try self.airBreakpoint(inst), | |
| 1017 | .call => try self.airCall(inst), | |
| 1018 | .cond_br => try self.airCondBr(inst), | |
| 1019 | .intcast => try self.airIntCast(inst), | |
| 1020 | .trunc => try self.airTrunc(inst), | |
| 1021 | .floatcast => try self.airFloatCast(inst), | |
| 1022 | .ptrtoint => try self.airPtrToInt(inst), | |
| 1023 | .load => try self.airLoad(inst), | |
| 1024 | .loop => try self.airLoop(inst), | |
| 1025 | .not => try self.airNot(inst), | |
| 1026 | .ret => try self.airRet(inst), | |
| 1027 | .store => try self.airStore(inst), | |
| 1028 | .assembly => try self.airAssembly(inst), | |
| 1029 | .slice_ptr => try self.airSliceField(inst, 0), | |
| 1030 | .slice_len => try self.airSliceField(inst, 1), | |
| 1031 | .array_to_slice => try self.airArrayToSlice(inst), | |
| 1031 | 1032 | |
| 1032 | 1033 | .struct_field_ptr => try self.airStructFieldPtr(inst), |
| 1033 | 1034 | .struct_field_val => try self.airStructFieldVal(inst), |
| ... | ... | @@ -1246,6 +1247,24 @@ pub const FuncGen = struct { |
| 1246 | 1247 | return null; |
| 1247 | 1248 | } |
| 1248 | 1249 | |
| 1250 | fn airArrayToSlice(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 1251 | if (self.liveness.isUnused(inst)) | |
| 1252 | return null; | |
| 1253 | ||
| 1254 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 1255 | const operand = try self.resolveInst(ty_op.operand); | |
| 1256 | const array_len = self.air.typeOf(ty_op.operand).elemType().arrayLen(); | |
| 1257 | const usize_llvm_ty = try self.dg.llvmType(Type.initTag(.usize)); | |
| 1258 | const len = usize_llvm_ty.constInt(array_len, .False); | |
| 1259 | const slice_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst)); | |
| 1260 | const indices: [2]*const llvm.Value = .{ | |
| 1261 | usize_llvm_ty.constNull(), usize_llvm_ty.constNull(), | |
| 1262 | }; | |
| 1263 | const ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""); | |
| 1264 | const partial = self.builder.buildInsertValue(slice_llvm_ty.getUndef(), ptr, 0, ""); | |
| 1265 | return self.builder.buildInsertValue(partial, len, 1, ""); | |
| 1266 | } | |
| 1267 | ||
| 1249 | 1268 | fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value { |
| 1250 | 1269 | if (self.liveness.isUnused(inst)) |
| 1251 | 1270 | return null; |
src/codegen/llvm/bindings.zig+9| ... | ... | @@ -484,6 +484,15 @@ pub const Builder = opaque { |
| 484 | 484 | DestTy: *const Type, |
| 485 | 485 | Name: [*:0]const u8, |
| 486 | 486 | ) *const Value; |
| 487 | ||
| 488 | pub const buildInsertValue = LLVMBuildInsertValue; | |
| 489 | extern fn LLVMBuildInsertValue( | |
| 490 | *const Builder, | |
| 491 | AggVal: *const Value, | |
| 492 | EltVal: *const Value, | |
| 493 | Index: c_uint, | |
| 494 | Name: [*:0]const u8, | |
| 495 | ) *const Value; | |
| 487 | 496 | }; |
| 488 | 497 | |
| 489 | 498 | pub const IntPredicate = enum(c_uint) { |
src/print_air.zig+1| ... | ... | @@ -174,6 +174,7 @@ const Writer = struct { |
| 174 | 174 | .struct_field_ptr_index_1, |
| 175 | 175 | .struct_field_ptr_index_2, |
| 176 | 176 | .struct_field_ptr_index_3, |
| 177 | .array_to_slice, | |
| 177 | 178 | => try w.writeTyOp(s, inst), |
| 178 | 179 | |
| 179 | 180 | .block, |
test/behavior/array.zig+24| ... | ... | @@ -3,3 +3,27 @@ const testing = std.testing; |
| 3 | 3 | const mem = std.mem; |
| 4 | 4 | const expect = testing.expect; |
| 5 | 5 | const expectEqual = testing.expectEqual; |
| 6 | ||
| 7 | test "arrays" { | |
| 8 | var array: [5]u32 = undefined; | |
| 9 | ||
| 10 | var i: u32 = 0; | |
| 11 | while (i < 5) { | |
| 12 | array[i] = i + 1; | |
| 13 | i = array[i]; | |
| 14 | } | |
| 15 | ||
| 16 | i = 0; | |
| 17 | var accumulator = @as(u32, 0); | |
| 18 | while (i < 5) { | |
| 19 | accumulator += array[i]; | |
| 20 | ||
| 21 | i += 1; | |
| 22 | } | |
| 23 | ||
| 24 | try expect(accumulator == 15); | |
| 25 | try expect(getArrayLen(&array) == 5); | |
| 26 | } | |
| 27 | fn getArrayLen(a: []const u32) usize { | |
| 28 | return a.len; | |
| 29 | } |
test/behavior/array_stage1.zig+9-69| ... | ... | @@ -4,30 +4,6 @@ const mem = std.mem; |
| 4 | 4 | const expect = testing.expect; |
| 5 | 5 | const expectEqual = testing.expectEqual; |
| 6 | 6 | |
| 7 | test "arrays" { | |
| 8 | var array: [5]u32 = undefined; | |
| 9 | ||
| 10 | var i: u32 = 0; | |
| 11 | while (i < 5) { | |
| 12 | array[i] = i + 1; | |
| 13 | i = array[i]; | |
| 14 | } | |
| 15 | ||
| 16 | i = 0; | |
| 17 | var accumulator = @as(u32, 0); | |
| 18 | while (i < 5) { | |
| 19 | accumulator += array[i]; | |
| 20 | ||
| 21 | i += 1; | |
| 22 | } | |
| 23 | ||
| 24 | try expect(accumulator == 15); | |
| 25 | try expect(getArrayLen(&array) == 5); | |
| 26 | } | |
| 27 | fn getArrayLen(a: []const u32) usize { | |
| 28 | return a.len; | |
| 29 | } | |
| 30 | ||
| 31 | 7 | test "array with sentinels" { |
| 32 | 8 | const S = struct { |
| 33 | 9 | fn doTheTest(is_ct: bool) !void { |
| ... | ... | @@ -64,12 +40,7 @@ test "void arrays" { |
| 64 | 40 | } |
| 65 | 41 | |
| 66 | 42 | test "array literal" { |
| 67 | const hex_mult = [_]u16{ | |
| 68 | 4096, | |
| 69 | 256, | |
| 70 | 16, | |
| 71 | 1, | |
| 72 | }; | |
| 43 | const hex_mult = [_]u16{ 4096, 256, 16, 1 }; | |
| 73 | 44 | |
| 74 | 45 | try expect(hex_mult.len == 4); |
| 75 | 46 | try expect(hex_mult[1] == 256); |
| ... | ... | @@ -84,21 +55,10 @@ test "array dot len const expr" { |
| 84 | 55 | const ArrayDotLenConstExpr = struct { |
| 85 | 56 | y: [some_array.len]u8, |
| 86 | 57 | }; |
| 87 | const some_array = [_]u8{ | |
| 88 | 0, | |
| 89 | 1, | |
| 90 | 2, | |
| 91 | 3, | |
| 92 | }; | |
| 58 | const some_array = [_]u8{ 0, 1, 2, 3 }; | |
| 93 | 59 | |
| 94 | 60 | test "nested arrays" { |
| 95 | const array_of_strings = [_][]const u8{ | |
| 96 | "hello", | |
| 97 | "this", | |
| 98 | "is", | |
| 99 | "my", | |
| 100 | "thing", | |
| 101 | }; | |
| 61 | const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" }; | |
| 102 | 62 | for (array_of_strings) |s, i| { |
| 103 | 63 | if (i == 0) try expect(mem.eql(u8, s, "hello")); |
| 104 | 64 | if (i == 1) try expect(mem.eql(u8, s, "this")); |
| ... | ... | @@ -109,12 +69,8 @@ test "nested arrays" { |
| 109 | 69 | } |
| 110 | 70 | |
| 111 | 71 | var s_array: [8]Sub = undefined; |
| 112 | const Sub = struct { | |
| 113 | b: u8, | |
| 114 | }; | |
| 115 | const Str = struct { | |
| 116 | a: []Sub, | |
| 117 | }; | |
| 72 | const Sub = struct { b: u8 }; | |
| 73 | const Str = struct { a: []Sub }; | |
| 118 | 74 | test "set global var array via slice embedded in struct" { |
| 119 | 75 | var s = Str{ .a = s_array[0..] }; |
| 120 | 76 | |
| ... | ... | @@ -208,26 +164,10 @@ test "runtime initialize array elem and then implicit cast to slice" { |
| 208 | 164 | test "array literal as argument to function" { |
| 209 | 165 | const S = struct { |
| 210 | 166 | fn entry(two: i32) !void { |
| 211 | try foo(&[_]i32{ | |
| 212 | 1, | |
| 213 | 2, | |
| 214 | 3, | |
| 215 | }); | |
| 216 | try foo(&[_]i32{ | |
| 217 | 1, | |
| 218 | two, | |
| 219 | 3, | |
| 220 | }); | |
| 221 | try foo2(true, &[_]i32{ | |
| 222 | 1, | |
| 223 | 2, | |
| 224 | 3, | |
| 225 | }); | |
| 226 | try foo2(true, &[_]i32{ | |
| 227 | 1, | |
| 228 | two, | |
| 229 | 3, | |
| 230 | }); | |
| 167 | try foo(&[_]i32{ 1, 2, 3 }); | |
| 168 | try foo(&[_]i32{ 1, two, 3 }); | |
| 169 | try foo2(true, &[_]i32{ 1, 2, 3 }); | |
| 170 | try foo2(true, &[_]i32{ 1, two, 3 }); | |
| 231 | 171 | } |
| 232 | 172 | fn foo(x: []const i32) !void { |
| 233 | 173 | try expect(x[0] == 1); |