| 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,6 +306,9 @@ pub const Inst = struct { |
| 306 | /// Result type is the element type of the inner pointer operand. | 306 | /// Result type is the element type of the inner pointer operand. |
| 307 | /// Uses the `bin_op` field. | 307 | /// Uses the `bin_op` field. |
| 308 | ptr_ptr_elem_val, | 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 | pub fn fromCmpOp(op: std.math.CompareOperator) Tag { | 313 | pub fn fromCmpOp(op: std.math.CompareOperator) Tag { |
| 311 | return switch (op) { | 314 | return switch (op) { |
| ... | @@ -526,6 +529,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { | ... | @@ -526,6 +529,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 526 | .struct_field_ptr_index_1, | 529 | .struct_field_ptr_index_1, |
| 527 | .struct_field_ptr_index_2, | 530 | .struct_field_ptr_index_2, |
| 528 | .struct_field_ptr_index_3, | 531 | .struct_field_ptr_index_3, |
| 532 | .array_to_slice, | ||
| 529 | => return air.getRefType(datas[inst].ty_op.ty), | 533 | => return air.getRefType(datas[inst].ty_op.ty), |
| 530 | 534 | ||
| 531 | .loop, | 535 | .loop, |
src/Liveness.zig+1| ... | @@ -287,6 +287,7 @@ fn analyzeInst( | ... | @@ -287,6 +287,7 @@ fn analyzeInst( |
| 287 | .struct_field_ptr_index_1, | 287 | .struct_field_ptr_index_1, |
| 288 | .struct_field_ptr_index_2, | 288 | .struct_field_ptr_index_2, |
| 289 | .struct_field_ptr_index_3, | 289 | .struct_field_ptr_index_3, |
| 290 | .array_to_slice, | ||
| 290 | => { | 291 | => { |
| 291 | const o = inst_datas[inst].ty_op; | 292 | const o = inst_datas[inst].ty_op; |
| 292 | return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none }); | 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,7 +8952,8 @@ fn coerceArrayPtrToSlice( |
| 8952 | // The comptime Value representation is compatible with both types. | 8952 | // The comptime Value representation is compatible with both types. |
| 8953 | return sema.addConstant(dest_type, val); | 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 | fn coerceArrayPtrToMany( | 8959 | fn coerceArrayPtrToMany( |
src/codegen.zig+11| ... | @@ -856,6 +856,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -856,6 +856,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 856 | .store => try self.airStore(inst), | 856 | .store => try self.airStore(inst), |
| 857 | .struct_field_ptr=> try self.airStructFieldPtr(inst), | 857 | .struct_field_ptr=> try self.airStructFieldPtr(inst), |
| 858 | .struct_field_val=> try self.airStructFieldVal(inst), | 858 | .struct_field_val=> try self.airStructFieldVal(inst), |
| 859 | .array_to_slice => try self.airArrayToSlice(inst), | ||
| 859 | 860 | ||
| 860 | .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), | 861 | .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), |
| 861 | .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), | 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,6 +4762,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4761 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 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 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { | 4775 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 4765 | // First section of indexes correspond to a set number of constant values. | 4776 | // First section of indexes correspond to a set number of constant values. |
| 4766 | const ref_int = @enumToInt(inst); | 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,6 +910,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM |
| 910 | .switch_br => try airSwitchBr(o, inst), | 910 | .switch_br => try airSwitchBr(o, inst), |
| 911 | .wrap_optional => try airWrapOptional(o, inst), | 911 | .wrap_optional => try airWrapOptional(o, inst), |
| 912 | .struct_field_ptr => try airStructFieldPtr(o, inst), | 912 | .struct_field_ptr => try airStructFieldPtr(o, inst), |
| 913 | .array_to_slice => try airArrayToSlice(o, inst), | ||
| 913 | 914 | ||
| 914 | .struct_field_ptr_index_0 => try airStructFieldPtrIndex(o, inst, 0), | 915 | .struct_field_ptr_index_0 => try airStructFieldPtrIndex(o, inst, 0), |
| 915 | .struct_field_ptr_index_1 => try airStructFieldPtrIndex(o, inst, 1), | 916 | .struct_field_ptr_index_1 => try airStructFieldPtrIndex(o, inst, 1), |
| ... | @@ -1860,6 +1861,23 @@ fn airIsErr( | ... | @@ -1860,6 +1861,23 @@ fn airIsErr( |
| 1860 | return local; | 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 | fn IndentWriter(comptime UnderlyingWriter: type) type { | 1881 | fn IndentWriter(comptime UnderlyingWriter: type) type { |
| 1864 | return struct { | 1882 | return struct { |
| 1865 | const Self = @This(); | 1883 | const Self = @This(); |
src/codegen/llvm.zig+41-22| ... | @@ -1006,28 +1006,29 @@ pub const FuncGen = struct { | ... | @@ -1006,28 +1006,29 @@ pub const FuncGen = struct { |
| 1006 | .is_err => try self.airIsErr(inst, .NE, false), | 1006 | .is_err => try self.airIsErr(inst, .NE, false), |
| 1007 | .is_err_ptr => try self.airIsErr(inst, .NE, true), | 1007 | .is_err_ptr => try self.airIsErr(inst, .NE, true), |
| 1008 | 1008 | ||
| 1009 | .alloc => try self.airAlloc(inst), | 1009 | .alloc => try self.airAlloc(inst), |
| 1010 | .arg => try self.airArg(inst), | 1010 | .arg => try self.airArg(inst), |
| 1011 | .bitcast => try self.airBitCast(inst), | 1011 | .bitcast => try self.airBitCast(inst), |
| 1012 | .bool_to_int=> try self.airBoolToInt(inst), | 1012 | .bool_to_int => try self.airBoolToInt(inst), |
| 1013 | .block => try self.airBlock(inst), | 1013 | .block => try self.airBlock(inst), |
| 1014 | .br => try self.airBr(inst), | 1014 | .br => try self.airBr(inst), |
| 1015 | .switch_br => try self.airSwitchBr(inst), | 1015 | .switch_br => try self.airSwitchBr(inst), |
| 1016 | .breakpoint => try self.airBreakpoint(inst), | 1016 | .breakpoint => try self.airBreakpoint(inst), |
| 1017 | .call => try self.airCall(inst), | 1017 | .call => try self.airCall(inst), |
| 1018 | .cond_br => try self.airCondBr(inst), | 1018 | .cond_br => try self.airCondBr(inst), |
| 1019 | .intcast => try self.airIntCast(inst), | 1019 | .intcast => try self.airIntCast(inst), |
| 1020 | .trunc => try self.airTrunc(inst), | 1020 | .trunc => try self.airTrunc(inst), |
| 1021 | .floatcast => try self.airFloatCast(inst), | 1021 | .floatcast => try self.airFloatCast(inst), |
| 1022 | .ptrtoint => try self.airPtrToInt(inst), | 1022 | .ptrtoint => try self.airPtrToInt(inst), |
| 1023 | .load => try self.airLoad(inst), | 1023 | .load => try self.airLoad(inst), |
| 1024 | .loop => try self.airLoop(inst), | 1024 | .loop => try self.airLoop(inst), |
| 1025 | .not => try self.airNot(inst), | 1025 | .not => try self.airNot(inst), |
| 1026 | .ret => try self.airRet(inst), | 1026 | .ret => try self.airRet(inst), |
| 1027 | .store => try self.airStore(inst), | 1027 | .store => try self.airStore(inst), |
| 1028 | .assembly => try self.airAssembly(inst), | 1028 | .assembly => try self.airAssembly(inst), |
| 1029 | .slice_ptr => try self.airSliceField(inst, 0), | 1029 | .slice_ptr => try self.airSliceField(inst, 0), |
| 1030 | .slice_len => try self.airSliceField(inst, 1), | 1030 | .slice_len => try self.airSliceField(inst, 1), |
| 1031 | .array_to_slice => try self.airArrayToSlice(inst), | ||
| 1031 | 1032 | ||
| 1032 | .struct_field_ptr => try self.airStructFieldPtr(inst), | 1033 | .struct_field_ptr => try self.airStructFieldPtr(inst), |
| 1033 | .struct_field_val => try self.airStructFieldVal(inst), | 1034 | .struct_field_val => try self.airStructFieldVal(inst), |
| ... | @@ -1246,6 +1247,24 @@ pub const FuncGen = struct { | ... | @@ -1246,6 +1247,24 @@ pub const FuncGen = struct { |
| 1246 | return null; | 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 | fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value { | 1268 | fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value { |
| 1250 | if (self.liveness.isUnused(inst)) | 1269 | if (self.liveness.isUnused(inst)) |
| 1251 | return null; | 1270 | return null; |
src/codegen/llvm/bindings.zig+9| ... | @@ -484,6 +484,15 @@ pub const Builder = opaque { | ... | @@ -484,6 +484,15 @@ pub const Builder = opaque { |
| 484 | DestTy: *const Type, | 484 | DestTy: *const Type, |
| 485 | Name: [*:0]const u8, | 485 | Name: [*:0]const u8, |
| 486 | ) *const Value; | 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 | pub const IntPredicate = enum(c_uint) { | 498 | pub const IntPredicate = enum(c_uint) { |
src/print_air.zig+1| ... | @@ -174,6 +174,7 @@ const Writer = struct { | ... | @@ -174,6 +174,7 @@ const Writer = struct { |
| 174 | .struct_field_ptr_index_1, | 174 | .struct_field_ptr_index_1, |
| 175 | .struct_field_ptr_index_2, | 175 | .struct_field_ptr_index_2, |
| 176 | .struct_field_ptr_index_3, | 176 | .struct_field_ptr_index_3, |
| 177 | .array_to_slice, | ||
| 177 | => try w.writeTyOp(s, inst), | 178 | => try w.writeTyOp(s, inst), |
| 178 | 179 | ||
| 179 | .block, | 180 | .block, |
test/behavior/array.zig+24| ... | @@ -3,3 +3,27 @@ const testing = std.testing; | ... | @@ -3,3 +3,27 @@ const testing = std.testing; |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| 4 | const expect = testing.expect; | 4 | const expect = testing.expect; |
| 5 | const expectEqual = testing.expectEqual; | 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,30 +4,6 @@ const mem = std.mem; |
| 4 | const expect = testing.expect; | 4 | const expect = testing.expect; |
| 5 | const expectEqual = testing.expectEqual; | 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 | test "array with sentinels" { | 7 | test "array with sentinels" { |
| 32 | const S = struct { | 8 | const S = struct { |
| 33 | fn doTheTest(is_ct: bool) !void { | 9 | fn doTheTest(is_ct: bool) !void { |
| ... | @@ -64,12 +40,7 @@ test "void arrays" { | ... | @@ -64,12 +40,7 @@ test "void arrays" { |
| 64 | } | 40 | } |
| 65 | 41 | ||
| 66 | test "array literal" { | 42 | test "array literal" { |
| 67 | const hex_mult = [_]u16{ | 43 | const hex_mult = [_]u16{ 4096, 256, 16, 1 }; |
| 68 | 4096, | ||
| 69 | 256, | ||
| 70 | 16, | ||
| 71 | 1, | ||
| 72 | }; | ||
| 73 | 44 | ||
| 74 | try expect(hex_mult.len == 4); | 45 | try expect(hex_mult.len == 4); |
| 75 | try expect(hex_mult[1] == 256); | 46 | try expect(hex_mult[1] == 256); |
| ... | @@ -84,21 +55,10 @@ test "array dot len const expr" { | ... | @@ -84,21 +55,10 @@ test "array dot len const expr" { |
| 84 | const ArrayDotLenConstExpr = struct { | 55 | const ArrayDotLenConstExpr = struct { |
| 85 | y: [some_array.len]u8, | 56 | y: [some_array.len]u8, |
| 86 | }; | 57 | }; |
| 87 | const some_array = [_]u8{ | 58 | const some_array = [_]u8{ 0, 1, 2, 3 }; |
| 88 | 0, | ||
| 89 | 1, | ||
| 90 | 2, | ||
| 91 | 3, | ||
| 92 | }; | ||
| 93 | 59 | ||
| 94 | test "nested arrays" { | 60 | test "nested arrays" { |
| 95 | const array_of_strings = [_][]const u8{ | 61 | const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" }; |
| 96 | "hello", | ||
| 97 | "this", | ||
| 98 | "is", | ||
| 99 | "my", | ||
| 100 | "thing", | ||
| 101 | }; | ||
| 102 | for (array_of_strings) |s, i| { | 62 | for (array_of_strings) |s, i| { |
| 103 | if (i == 0) try expect(mem.eql(u8, s, "hello")); | 63 | if (i == 0) try expect(mem.eql(u8, s, "hello")); |
| 104 | if (i == 1) try expect(mem.eql(u8, s, "this")); | 64 | if (i == 1) try expect(mem.eql(u8, s, "this")); |
| ... | @@ -109,12 +69,8 @@ test "nested arrays" { | ... | @@ -109,12 +69,8 @@ test "nested arrays" { |
| 109 | } | 69 | } |
| 110 | 70 | ||
| 111 | var s_array: [8]Sub = undefined; | 71 | var s_array: [8]Sub = undefined; |
| 112 | const Sub = struct { | 72 | const Sub = struct { b: u8 }; |
| 113 | b: u8, | 73 | const Str = struct { a: []Sub }; |
| 114 | }; | ||
| 115 | const Str = struct { | ||
| 116 | a: []Sub, | ||
| 117 | }; | ||
| 118 | test "set global var array via slice embedded in struct" { | 74 | test "set global var array via slice embedded in struct" { |
| 119 | var s = Str{ .a = s_array[0..] }; | 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,26 +164,10 @@ test "runtime initialize array elem and then implicit cast to slice" { |
| 208 | test "array literal as argument to function" { | 164 | test "array literal as argument to function" { |
| 209 | const S = struct { | 165 | const S = struct { |
| 210 | fn entry(two: i32) !void { | 166 | fn entry(two: i32) !void { |
| 211 | try foo(&[_]i32{ | 167 | try foo(&[_]i32{ 1, 2, 3 }); |
| 212 | 1, | 168 | try foo(&[_]i32{ 1, two, 3 }); |
| 213 | 2, | 169 | try foo2(true, &[_]i32{ 1, 2, 3 }); |
| 214 | 3, | 170 | try foo2(true, &[_]i32{ 1, two, 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 | }); | ||
| 231 | } | 171 | } |
| 232 | fn foo(x: []const i32) !void { | 172 | fn foo(x: []const i32) !void { |
| 233 | try expect(x[0] == 1); | 173 | try expect(x[0] == 1); |