From 1d3382202029cde2e00d7747ec0ea53a5df68047 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Thu, 21 Oct 2021 13:16:47 +0200 Subject: [PATCH 1/4] stage2: forbid double dereference in Sema.elemVal --- src/Sema.zig | 107 +++++++++++++-------------------------------------- 1 file changed, 26 insertions(+), 81 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 8ac31387f4..d0a94d3fd8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11494,117 +11494,62 @@ fn elemVal( sema: *Sema, block: *Block, src: LazySrcLoc, - array_maybe_ptr: Air.Inst.Ref, + array: Air.Inst.Ref, elem_index: Air.Inst.Ref, elem_index_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { - const array_ptr_src = src; // TODO better source location - const maybe_ptr_ty = sema.typeOf(array_maybe_ptr); - switch (maybe_ptr_ty.zigTypeTag()) { - .Pointer => switch (maybe_ptr_ty.ptrSize()) { + const array_src = src; // TODO better source location + const array_ty = sema.typeOf(array); + + if (!array_ty.isIndexable()) { + return sema.fail(block, src, "array access of non-indexable type '{}'", .{array_ty}); + } + + switch (array_ty.zigTypeTag()) { + .Pointer => switch (array_ty.ptrSize()) { .Slice => { - const maybe_slice_val = try sema.resolveDefinedValue(block, array_ptr_src, array_maybe_ptr); + const maybe_slice_val = try sema.resolveDefinedValue(block, array_src, array); const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); const runtime_src = if (maybe_slice_val) |slice_val| rs: { const index_val = maybe_index_val orelse break :rs elem_index_src; const index = @intCast(usize, index_val.toUnsignedInt()); const elem_val = try slice_val.elemValue(sema.arena, index); - return sema.addConstant(maybe_ptr_ty.elemType2(), elem_val); - } else array_ptr_src; + return sema.addConstant(array_ty.elemType2(), elem_val); + } else array_src; try sema.requireRuntimeBlock(block, runtime_src); - return block.addBinOp(.slice_elem_val, array_maybe_ptr, elem_index); + return block.addBinOp(.slice_elem_val, array, elem_index); }, .Many, .C => { - if (try sema.resolveDefinedValue(block, src, array_maybe_ptr)) |ptr_val| { + if (try sema.resolveDefinedValue(block, src, array)) |ptr_val| { _ = ptr_val; return sema.fail(block, src, "TODO implement Sema for elemVal for comptime known pointer", .{}); } try sema.requireRuntimeBlock(block, src); - return block.addBinOp(.ptr_elem_val, array_maybe_ptr, elem_index); + return block.addBinOp(.ptr_elem_val, array, elem_index); }, .One => { - const indexable_ty = maybe_ptr_ty.childType(); - switch (indexable_ty.zigTypeTag()) { - .Pointer => switch (indexable_ty.ptrSize()) { - .Slice => { - // We have a pointer to a slice and we want an element value. - if (try sema.isComptimeKnown(block, src, array_maybe_ptr)) { - const slice = try sema.analyzeLoad(block, src, array_maybe_ptr, array_ptr_src); - if (try sema.resolveDefinedValue(block, src, slice)) |slice_val| { - _ = slice_val; - return sema.fail(block, src, "TODO implement Sema for elemVal for comptime known slice", .{}); - } - try sema.requireRuntimeBlock(block, src); - return block.addBinOp(.slice_elem_val, slice, elem_index); - } - try sema.requireRuntimeBlock(block, src); - return block.addBinOp(.ptr_slice_elem_val, array_maybe_ptr, elem_index); - }, - .Many, .C => { - // We have a pointer to a pointer and we want an element value. - if (try sema.isComptimeKnown(block, src, array_maybe_ptr)) { - const ptr = try sema.analyzeLoad(block, src, array_maybe_ptr, array_ptr_src); - if (try sema.resolveDefinedValue(block, src, ptr)) |ptr_val| { - _ = ptr_val; - return sema.fail(block, src, "TODO implement Sema for elemVal for comptime known pointer", .{}); - } - try sema.requireRuntimeBlock(block, src); - return block.addBinOp(.ptr_elem_val, ptr, elem_index); - } - try sema.requireRuntimeBlock(block, src); - return block.addBinOp(.ptr_ptr_elem_val, array_maybe_ptr, elem_index); - }, - .One => { - const array_ty = indexable_ty.childType(); - if (array_ty.zigTypeTag() == .Array) { - // We have a double pointer to an array, and we want an element - // value. This can happen with this code for example: - // var a: *[1]u8 = undefined; _ = a[0]; - const array_ptr = try sema.analyzeLoad(block, src, array_maybe_ptr, array_ptr_src); - const ptr = try sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src); - return sema.analyzeLoad(block, src, ptr, elem_index_src); - } else return sema.fail( - block, - array_ptr_src, - "expected pointer, found '{}'", - .{array_ty}, - ); - }, - }, - .Array => { - const ptr = try sema.elemPtr(block, src, array_maybe_ptr, elem_index, elem_index_src); - return sema.analyzeLoad(block, src, ptr, elem_index_src); - }, - else => return sema.fail( - block, - array_ptr_src, - "expected pointer, found '{}'", - .{indexable_ty}, - ), - } + assert(array_ty.childType().zigTypeTag() == .Array); + const elem_ptr = try sema.elemPtr(block, src, array, elem_index, elem_index_src); + return sema.analyzeLoad(block, src, elem_ptr, elem_index_src); }, }, .Array => { - if (try sema.resolveMaybeUndefVal(block, src, array_maybe_ptr)) |array_val| { - const elem_ty = maybe_ptr_ty.childType(); - const opt_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); + if (try sema.resolveMaybeUndefVal(block, src, array)) |array_val| { + const elem_ty = array_ty.childType(); if (array_val.isUndef()) return sema.addConstUndef(elem_ty); - if (opt_index_val) |index_val| { + const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); + if (maybe_index_val) |index_val| { const index = @intCast(usize, index_val.toUnsignedInt()); const elem_val = try array_val.elemValue(sema.arena, index); return sema.addConstant(elem_ty, elem_val); } } try sema.requireRuntimeBlock(block, src); - return block.addBinOp(.array_elem_val, array_maybe_ptr, elem_index); + return block.addBinOp(.array_elem_val, array, elem_index); }, - else => return sema.fail( - block, - array_ptr_src, - "expected pointer or array; found '{}'", - .{maybe_ptr_ty}, - ), + .Vector => return sema.fail(block, src, "TODO implement Sema for elemVal for vector", .{}), + else => unreachable, } } From 84876fec582e13707a809c8136bb1eaf92b5da09 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Thu, 21 Oct 2021 14:12:54 +0200 Subject: [PATCH 2/4] stage2: remove ptr_ptr_elem_val and ptr_slice_elem_val --- src/Air.zig | 14 -------------- src/Liveness.zig | 2 -- src/arch/aarch64/CodeGen.zig | 16 ---------------- src/codegen.zig | 20 -------------------- src/codegen/c.zig | 2 -- src/codegen/llvm.zig | 33 --------------------------------- src/print_air.zig | 2 -- 7 files changed, 89 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 91b496a8e4..ee93f51096 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -384,10 +384,6 @@ pub const Inst = struct { /// Result type is the element type of the slice operand. /// Uses the `bin_op` field. slice_elem_val, - /// Given a pointer to a slice, and element index, return the element value at that index. - /// Result type is the element type of the slice operand (2 element type operations). - /// Uses the `bin_op` field. - ptr_slice_elem_val, /// Given a pointer value, and element index, return the element value at that index. /// Result type is the element type of the pointer operand. /// Uses the `bin_op` field. @@ -396,11 +392,6 @@ pub const Inst = struct { /// Result type is pointer to the element type of the pointer operand. /// Uses the `ty_pl` field with payload `Bin`. ptr_elem_ptr, - /// Given a pointer to a pointer, and element index, return the element value of the inner - /// pointer at that index. - /// 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, @@ -772,11 +763,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { const ptr_ty = air.typeOf(datas[inst].bin_op.lhs); return ptr_ty.elemType(); }, - .ptr_slice_elem_val, .ptr_ptr_elem_val => { - const outer_ptr_ty = air.typeOf(datas[inst].bin_op.lhs); - const inner_ptr_ty = outer_ptr_ty.elemType(); - return inner_ptr_ty.elemType(); - }, .atomic_load => { const ptr_ty = air.typeOf(datas[inst].atomic_load.ptr); return ptr_ty.elemType(); diff --git a/src/Liveness.zig b/src/Liveness.zig index 5d5bb64196..1b29b46ad8 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -252,9 +252,7 @@ fn analyzeInst( .store, .array_elem_val, .slice_elem_val, - .ptr_slice_elem_val, .ptr_elem_val, - .ptr_ptr_elem_val, .shl, .shl_exact, .shl_sat, diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index bb2fc471cf..5d1856b2fa 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -500,10 +500,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .array_elem_val => try self.airArrayElemVal(inst), .slice_elem_val => try self.airSliceElemVal(inst), - .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst), .ptr_elem_val => try self.airPtrElemVal(inst), .ptr_elem_ptr => try self.airPtrElemPtr(inst), - .ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst), .constant => unreachable, // excluded from function bodies .const_ty => unreachable, // excluded from function bodies @@ -1092,13 +1090,6 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airPtrSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { - const is_volatile = false; // TODO - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_slice_elem_val for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { const is_volatile = false; // TODO const bin_op = self.air.instructions.items(.data)[inst].bin_op; @@ -1113,13 +1104,6 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); } -fn airPtrPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { - const is_volatile = false; // TODO - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_ptr_elem_val for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; _ = bin_op; diff --git a/src/codegen.zig b/src/codegen.zig index 13169db1fd..873260329c 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -848,10 +848,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .array_elem_val => try self.airArrayElemVal(inst), .slice_elem_val => try self.airSliceElemVal(inst), - .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst), .ptr_elem_val => try self.airPtrElemVal(inst), .ptr_elem_ptr => try self.airPtrElemPtr(inst), - .ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst), .constant => unreachable, // excluded from function bodies .const_ty => unreachable, // excluded from function bodies @@ -1543,15 +1541,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } - fn airPtrSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { - const is_volatile = false; // TODO - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else switch (arch) { - else => return self.fail("TODO implement ptr_slice_elem_val for {}", .{self.target.cpu.arch}), - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); - } - fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { const is_volatile = false; // TODO const bin_op = self.air.instructions.items(.data)[inst].bin_op; @@ -1570,15 +1559,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); } - fn airPtrPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { - const is_volatile = false; // TODO - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else switch (arch) { - else => return self.fail("TODO implement ptr_ptr_elem_val for {}", .{self.target.cpu.arch}), - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); - } - fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const result: MCValue = switch (arch) { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 6e03de1cca..f09bb574af 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1081,10 +1081,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .ptr_slice_ptr_ptr => try airPtrSliceFieldPtr(f, inst, ".ptr;\n"), .ptr_elem_val => try airPtrElemVal(f, inst, "["), - .ptr_ptr_elem_val => try airPtrElemVal(f, inst, "[0]["), .ptr_elem_ptr => try airPtrElemPtr(f, inst), .slice_elem_val => try airSliceElemVal(f, inst, "["), - .ptr_slice_elem_val => try airSliceElemVal(f, inst, "[0]["), .array_elem_val => try airArrayElemVal(f, inst), .unwrap_errunion_payload => try airUnwrapErrUnionPay(f, inst), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index f924c0afc4..e5356582e2 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1760,10 +1760,8 @@ pub const FuncGen = struct { .array_elem_val => try self.airArrayElemVal(inst), .slice_elem_val => try self.airSliceElemVal(inst), - .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst), .ptr_elem_val => try self.airPtrElemVal(inst), .ptr_elem_ptr => try self.airPtrElemPtr(inst), - .ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst), .optional_payload => try self.airOptionalPayload(inst, false), .optional_payload_ptr => try self.airOptionalPayload(inst, true), @@ -2165,24 +2163,6 @@ pub const FuncGen = struct { return self.load(ptr, slice_ty); } - fn airPtrSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const slice_ty = self.air.typeOf(bin_op.lhs).childType(); - if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null; - - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - - const base_ptr = ptr: { - const ptr_field_ptr = self.builder.buildStructGEP(lhs, 0, ""); - break :ptr self.builder.buildLoad(ptr_field_ptr, ""); - }; - - const indices: [1]*const llvm.Value = .{rhs}; - const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); - return self.load(ptr, slice_ty); - } - fn airArrayElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; @@ -2240,19 +2220,6 @@ pub const FuncGen = struct { } } - fn airPtrPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const ptr_ty = self.air.typeOf(bin_op.lhs).childType(); - if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null; - - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - const base_ptr = self.builder.buildLoad(lhs, ""); - const indices: [1]*const llvm.Value = .{rhs}; - const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); - return self.load(ptr, ptr_ty); - } - fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; diff --git a/src/print_air.zig b/src/print_air.zig index a9485a57f9..5231bf0055 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -130,9 +130,7 @@ const Writer = struct { .store, .array_elem_val, .slice_elem_val, - .ptr_slice_elem_val, .ptr_elem_val, - .ptr_ptr_elem_val, .shl, .shl_exact, .shl_sat, From 09c7d5aebc4f7fe96a89f93c0cf99cc03977ea5f Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Thu, 21 Oct 2021 16:17:21 +0200 Subject: [PATCH 3/4] stage2: elemPtr for slices * Restructure elemPtr a bit * New AIR instruction: slice_elem_ptr, which returns a pointer to an element of a slice * Value: adapt elemPtr to work on slices --- src/Air.zig | 5 +++ src/Liveness.zig | 2 +- src/Sema.zig | 68 +++++++++++++++++++++++++++--------- src/arch/aarch64/CodeGen.zig | 8 +++++ src/codegen.zig | 10 ++++++ src/codegen/c.zig | 19 ++++++++++ src/codegen/llvm.zig | 25 +++++++++++-- src/print_air.zig | 10 ++++++ src/type.zig | 14 ++++++++ src/value.zig | 28 +++++++++------ 10 files changed, 157 insertions(+), 32 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index ee93f51096..380e7fbbc2 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -384,6 +384,10 @@ pub const Inst = struct { /// Result type is the element type of the slice operand. /// Uses the `bin_op` field. slice_elem_val, + /// Given a slice value and element index, return a pointer to the element value at that index. + /// Result type is a pointer to the element type of the slice operand. + /// Uses the `ty_pl` field with payload `Bin`. + slice_elem_ptr, /// Given a pointer value, and element index, return the element value at that index. /// Result type is the element type of the pointer operand. /// Uses the `bin_op` field. @@ -685,6 +689,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .constant, .struct_field_ptr, .struct_field_val, + .slice_elem_ptr, .ptr_elem_ptr, .cmpxchg_weak, .cmpxchg_strong, diff --git a/src/Liveness.zig b/src/Liveness.zig index 1b29b46ad8..054c8e38dc 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -360,7 +360,7 @@ fn analyzeInst( const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data; return trackOperands(a, new_set, inst, main_tomb, .{ extra.struct_operand, .none, .none }); }, - .ptr_elem_ptr => { + .ptr_elem_ptr, .slice_elem_ptr => { const extra = a.air.extraData(Air.Bin, inst_datas[inst].ty_pl.payload).data; return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none }); }, diff --git a/src/Sema.zig b/src/Sema.zig index d0a94d3fd8..5a77f81aa0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -333,6 +333,24 @@ pub const Block = struct { }); } + pub fn addSliceElemPtr( + block: *Block, + slice: Air.Inst.Ref, + elem_index: Air.Inst.Ref, + elem_ptr_ty: Type, + ) !Air.Inst.Ref { + return block.addInst(.{ + .tag = .slice_elem_ptr, + .data = .{ .ty_pl = .{ + .ty = try block.sema.addType(elem_ptr_ty), + .payload = try block.sema.addExtra(Air.Bin{ + .lhs = slice, + .rhs = elem_index, + }), + } }, + }); + } + pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref { return Air.indexToRef(try block.addInstAsIndex(inst)); } @@ -11476,18 +11494,39 @@ fn elemPtr( else => return sema.fail(block, array_ptr_src, "expected pointer, found '{}'", .{array_ptr_ty}), }; if (!array_ty.isIndexable()) { - return sema.fail(block, src, "array access of non-array type '{}'", .{array_ty}); - } - if (array_ty.isSinglePointer() and array_ty.elemType().zigTypeTag() == .Array) { - // we have to deref the ptr operand to get the actual array pointer - const array_ptr_deref = try sema.analyzeLoad(block, src, array_ptr, array_ptr_src); - return sema.elemPtrArray(block, src, array_ptr_deref, elem_index, elem_index_src); - } - if (array_ty.zigTypeTag() == .Array) { - return sema.elemPtrArray(block, src, array_ptr, elem_index, elem_index_src); + return sema.fail(block, src, "array access of non-indexable type '{}'", .{array_ty}); } - return sema.fail(block, src, "TODO implement more analyze elemptr", .{}); + switch (array_ty.zigTypeTag()) { + .Pointer => { + // In all below cases, we have to deref the ptr operand to get the actual array pointer. + const array = try sema.analyzeLoad(block, src, array_ptr, array_ptr_src); + switch (array_ty.ptrSize()) { + .Slice => { + const result_ty = try array_ty.elemPtrType(sema.arena); + const maybe_slice_val = try sema.resolveDefinedValue(block, array_ptr_src, array); + const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); + const runtime_src = if (maybe_slice_val) |slice_val| rs: { + const index_val = maybe_index_val orelse break :rs elem_index_src; + const index = @intCast(usize, index_val.toUnsignedInt()); + const elem_ptr = try slice_val.elemPtr(sema.arena, index); + return sema.addConstant(result_ty, elem_ptr); + } else array_ptr_src; + + try sema.requireRuntimeBlock(block, runtime_src); + return block.addSliceElemPtr(array, elem_index, result_ty); + }, + .Many, .C => return sema.fail(block, src, "TODO implement Sema for elemPtr for many/c pointer", .{}), + .One => { + assert(array_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable + return sema.elemPtrArray(block, src, array, elem_index, elem_index_src); + }, + } + }, + .Array => return sema.elemPtrArray(block, src, array_ptr, elem_index, elem_index_src), + .Vector => return sema.fail(block, src, "TODO implement Sema for elemPtr for vector", .{}), + else => unreachable, + } } fn elemVal( @@ -11529,7 +11568,7 @@ fn elemVal( return block.addBinOp(.ptr_elem_val, array, elem_index); }, .One => { - assert(array_ty.childType().zigTypeTag() == .Array); + assert(array_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable const elem_ptr = try sema.elemPtr(block, src, array, elem_index, elem_index_src); return sema.analyzeLoad(block, src, elem_ptr, elem_index_src); }, @@ -11562,12 +11601,7 @@ fn elemPtrArray( elem_index_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { const array_ptr_ty = sema.typeOf(array_ptr); - const pointee_type = array_ptr_ty.elemType().elemType(); - const result_ty = try Type.ptr(sema.arena, .{ - .pointee_type = pointee_type, - .mutable = array_ptr_ty.ptrIsMutable(), - .@"addrspace" = array_ptr_ty.ptrAddressSpace(), - }); + const result_ty = try array_ptr_ty.elemPtrType(sema.arena); if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| { if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| { diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 5d1856b2fa..8dd4526f4d 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -500,6 +500,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .array_elem_val => try self.airArrayElemVal(inst), .slice_elem_val => try self.airSliceElemVal(inst), + .slice_elem_ptr => try self.airSliceElemPtr(inst), .ptr_elem_val => try self.airPtrElemVal(inst), .ptr_elem_ptr => try self.airPtrElemPtr(inst), @@ -1084,6 +1085,13 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_elem_ptr for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); +} + fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch}); diff --git a/src/codegen.zig b/src/codegen.zig index 873260329c..5963cdd376 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -848,6 +848,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .array_elem_val => try self.airArrayElemVal(inst), .slice_elem_val => try self.airSliceElemVal(inst), + .slice_elem_ptr => try self.airSliceElemPtr(inst), .ptr_elem_val => try self.airPtrElemVal(inst), .ptr_elem_ptr => try self.airPtrElemPtr(inst), @@ -1533,6 +1534,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } + fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { + else => return self.fail("TODO implement slice_elem_ptr for {}", .{self.target.cpu.arch}), + }; + return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); + } + fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index f09bb574af..9050499fce 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1083,6 +1083,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .ptr_elem_val => try airPtrElemVal(f, inst, "["), .ptr_elem_ptr => try airPtrElemPtr(f, inst), .slice_elem_val => try airSliceElemVal(f, inst, "["), + .slice_elem_ptr => try airSliceElemPtr(f, inst), .array_elem_val => try airArrayElemVal(f, inst), .unwrap_errunion_payload => try airUnwrapErrUnionPay(f, inst), @@ -1165,6 +1166,24 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index, prefix: []const u8) !CVal return local; } +fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { + if (f.liveness.isUnused(inst)) + return CValue.none; + const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; + const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; + + const slice = try f.resolveInst(bin_op.lhs); + const index = try f.resolveInst(bin_op.rhs); + const writer = f.object.writer(); + const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const); + try writer.writeAll(" = &"); + try f.writeCValue(writer, slice); + try writer.writeByte('['); + try f.writeCValue(writer, index); + try writer.writeAll("];\n"); + return local; +} + fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index e5356582e2..a607b7a753 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1760,6 +1760,7 @@ pub const FuncGen = struct { .array_elem_val => try self.airArrayElemVal(inst), .slice_elem_val => try self.airSliceElemVal(inst), + .slice_elem_ptr => try self.airSliceElemPtr(inst), .ptr_elem_val => try self.airPtrElemVal(inst), .ptr_elem_ptr => try self.airPtrElemPtr(inst), @@ -2157,12 +2158,20 @@ pub const FuncGen = struct { const slice = try self.resolveInst(bin_op.lhs); const index = try self.resolveInst(bin_op.rhs); - const base_ptr = self.builder.buildExtractValue(slice, 0, ""); - const indices: [1]*const llvm.Value = .{index}; - const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); + const ptr = self.sliceElemPtr(slice, index); return self.load(ptr, slice_ty); } + fn airSliceElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) return null; + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + + const slice = try self.resolveInst(bin_op.lhs); + const index = try self.resolveInst(bin_op.rhs); + return self.sliceElemPtr(slice, index); + } + fn airArrayElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; @@ -3575,6 +3584,16 @@ pub const FuncGen = struct { return self.builder.buildBitCast(union_field_ptr, result_llvm_ty, ""); } + fn sliceElemPtr( + self: *FuncGen, + slice: *const llvm.Value, + index: *const llvm.Value, + ) *const llvm.Value { + const base_ptr = self.builder.buildExtractValue(slice, 0, ""); + const indices: [1]*const llvm.Value = .{index}; + return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); + } + fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value { const id = llvm.lookupIntrinsicID(name.ptr, name.len); assert(id != 0); diff --git a/src/print_air.zig b/src/print_air.zig index 5231bf0055..a6d99eee9f 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -200,6 +200,7 @@ const Writer = struct { .loop, => try w.writeBlock(s, inst), + .slice_elem_ptr => try w.writeSliceElemPtr(s, inst), .ptr_elem_ptr => try w.writePtrElemPtr(s, inst), .struct_field_ptr => try w.writeStructField(s, inst), .struct_field_val => try w.writeStructField(s, inst), @@ -281,6 +282,15 @@ const Writer = struct { try s.print(", {d}", .{extra.field_index}); } + fn writeSliceElemPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; + const extra = w.air.extraData(Air.Bin, ty_pl.payload).data; + + try w.writeOperand(s, inst, 0, extra.lhs); + try s.writeAll(", "); + try w.writeOperand(s, inst, 1, extra.rhs); + } + fn writePtrElemPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; const extra = w.air.extraData(Air.Bin, ty_pl.payload).data; diff --git a/src/type.zig b/src/type.zig index fceb3c4dfd..72347f5adc 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2520,6 +2520,20 @@ pub const Type = extern union { }; } + /// Returns the type of a pointer to an element. + /// Asserts that the type is a pointer, and that the element type is indexable. + /// For *[N]T, return *T + /// For [*]T, returns *T + /// For []T, returns *T + /// Handles const-ness and address spaces in particular. + pub fn elemPtrType(ptr_ty: Type, arena: *Allocator) !Type { + return try Type.ptr(arena, .{ + .pointee_type = ptr_ty.elemType2(), + .mutable = ptr_ty.ptrIsMutable(), + .@"addrspace" = ptr_ty.ptrAddressSpace(), + }); + } + fn shallowElemType(child_ty: Type) Type { return switch (child_ty.zigTypeTag()) { .Array, .Vector => child_ty.childType(), diff --git a/src/value.zig b/src/value.zig index f1ca384b75..bb904cea30 100644 --- a/src/value.zig +++ b/src/value.zig @@ -114,7 +114,7 @@ pub const Value = extern union { /// This Tag will never be seen by machine codegen backends. It is changed into a /// `decl_ref` when a comptime variable goes out of scope. decl_ref_mut, - /// Pointer to a specific element of an array. + /// Pointer to a specific element of an array, vector or slice. elem_ptr, /// Pointer to a specific field of a struct or union. field_ptr, @@ -1792,17 +1792,23 @@ pub const Value = extern union { /// Returns a pointer to the element value at the index. pub fn elemPtr(self: Value, allocator: *Allocator, index: usize) !Value { - if (self.castTag(.elem_ptr)) |elem_ptr| { - return Tag.elem_ptr.create(allocator, .{ - .array_ptr = elem_ptr.data.array_ptr, - .index = elem_ptr.data.index + index, - }); + switch (self.tag()) { + .elem_ptr => { + const elem_ptr = self.castTag(.elem_ptr).?.data; + return Tag.elem_ptr.create(allocator, .{ + .array_ptr = elem_ptr.array_ptr, + .index = elem_ptr.index + index, + }); + }, + .slice => return Tag.elem_ptr.create(allocator, .{ + .array_ptr = self.castTag(.slice).?.data.ptr, + .index = index, + }), + else => return Tag.elem_ptr.create(allocator, .{ + .array_ptr = self, + .index = index, + }), } - - return Tag.elem_ptr.create(allocator, .{ - .array_ptr = self, - .index = index, - }); } pub fn isUndef(self: Value) bool { From fc034ca94f0682a06795616992f176f19ec9a83d Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Thu, 21 Oct 2021 16:54:34 +0200 Subject: [PATCH 4/4] stage2: comptime elemVal and elemPtr for Many/C pointers --- src/Sema.zig | 82 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 5a77f81aa0..b01e4539b0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -351,6 +351,24 @@ pub const Block = struct { }); } + pub fn addPtrElemPtr( + block: *Block, + array_ptr: Air.Inst.Ref, + elem_index: Air.Inst.Ref, + elem_ptr_ty: Type, + ) !Air.Inst.Ref { + return block.addInst(.{ + .tag = .ptr_elem_ptr, + .data = .{ .ty_pl = .{ + .ty = try block.sema.addType(elem_ptr_ty), + .payload = try block.sema.addExtra(Air.Bin{ + .lhs = array_ptr, + .rhs = elem_index, + }), + } }, + }); + } + pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref { return Air.indexToRef(try block.addInstAsIndex(inst)); } @@ -11500,10 +11518,10 @@ fn elemPtr( switch (array_ty.zigTypeTag()) { .Pointer => { // In all below cases, we have to deref the ptr operand to get the actual array pointer. - const array = try sema.analyzeLoad(block, src, array_ptr, array_ptr_src); + const array = try sema.analyzeLoad(block, array_ptr_src, array_ptr, array_ptr_src); + const result_ty = try array_ty.elemPtrType(sema.arena); switch (array_ty.ptrSize()) { .Slice => { - const result_ty = try array_ty.elemPtrType(sema.arena); const maybe_slice_val = try sema.resolveDefinedValue(block, array_ptr_src, array); const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); const runtime_src = if (maybe_slice_val) |slice_val| rs: { @@ -11516,14 +11534,28 @@ fn elemPtr( try sema.requireRuntimeBlock(block, runtime_src); return block.addSliceElemPtr(array, elem_index, result_ty); }, - .Many, .C => return sema.fail(block, src, "TODO implement Sema for elemPtr for many/c pointer", .{}), + .Many, .C => { + const maybe_ptr_val = try sema.resolveDefinedValue(block, array_ptr_src, array); + const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); + + const runtime_src = rs: { + const ptr_val = maybe_ptr_val orelse break :rs array_ptr_src; + const index_val = maybe_index_val orelse break :rs elem_index_src; + const index = @intCast(usize, index_val.toUnsignedInt()); + const elem_ptr = try ptr_val.elemPtr(sema.arena, index); + return sema.addConstant(result_ty, elem_ptr); + }; + + try sema.requireRuntimeBlock(block, runtime_src); + return block.addPtrElemPtr(array, elem_index, result_ty); + }, .One => { assert(array_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable - return sema.elemPtrArray(block, src, array, elem_index, elem_index_src); + return sema.elemPtrArray(block, array_ptr_src, array, elem_index, elem_index_src); }, } }, - .Array => return sema.elemPtrArray(block, src, array_ptr, elem_index, elem_index_src), + .Array => return sema.elemPtrArray(block, array_ptr_src, array_ptr, elem_index, elem_index_src), .Vector => return sema.fail(block, src, "TODO implement Sema for elemPtr for vector", .{}), else => unreachable, } @@ -11560,21 +11592,30 @@ fn elemVal( return block.addBinOp(.slice_elem_val, array, elem_index); }, .Many, .C => { - if (try sema.resolveDefinedValue(block, src, array)) |ptr_val| { - _ = ptr_val; - return sema.fail(block, src, "TODO implement Sema for elemVal for comptime known pointer", .{}); - } - try sema.requireRuntimeBlock(block, src); + const maybe_ptr_val = try sema.resolveDefinedValue(block, array_src, array); + const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); + + const runtime_src = rs: { + const ptr_val = maybe_ptr_val orelse break :rs array_src; + const index_val = maybe_index_val orelse break :rs elem_index_src; + const index = @intCast(usize, index_val.toUnsignedInt()); + const maybe_array_val = try ptr_val.pointerDeref(sema.arena); + const array_val = maybe_array_val orelse break :rs array_src; + const elem_val = try array_val.elemValue(sema.arena, index); + return sema.addConstant(array_ty.elemType2(), elem_val); + }; + + try sema.requireRuntimeBlock(block, runtime_src); return block.addBinOp(.ptr_elem_val, array, elem_index); }, .One => { assert(array_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable - const elem_ptr = try sema.elemPtr(block, src, array, elem_index, elem_index_src); - return sema.analyzeLoad(block, src, elem_ptr, elem_index_src); + const elem_ptr = try sema.elemPtr(block, array_src, array, elem_index, elem_index_src); + return sema.analyzeLoad(block, array_src, elem_ptr, elem_index_src); }, }, .Array => { - if (try sema.resolveMaybeUndefVal(block, src, array)) |array_val| { + if (try sema.resolveMaybeUndefVal(block, array_src, array)) |array_val| { const elem_ty = array_ty.childType(); if (array_val.isUndef()) return sema.addConstUndef(elem_ty); const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); @@ -11584,10 +11625,10 @@ fn elemVal( return sema.addConstant(elem_ty, elem_val); } } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, array_src); return block.addBinOp(.array_elem_val, array, elem_index); }, - .Vector => return sema.fail(block, src, "TODO implement Sema for elemVal for vector", .{}), + .Vector => return sema.fail(block, array_src, "TODO implement Sema for elemVal for vector", .{}), else => unreachable, } } @@ -11615,16 +11656,7 @@ fn elemPtrArray( } // TODO safety check for array bounds try sema.requireRuntimeBlock(block, src); - return block.addInst(.{ - .tag = .ptr_elem_ptr, - .data = .{ .ty_pl = .{ - .ty = try sema.addType(result_ty), - .payload = try sema.addExtra(Air.Bin{ - .lhs = array_ptr, - .rhs = elem_index, - }), - } }, - }); + return block.addPtrElemPtr(array_ptr, elem_index, result_ty); } fn coerce(