From 6d6cf598475ab8d2c3259002655ba04f1d056b2e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 11 Oct 2021 11:00:32 -0700 Subject: stage2: support nested structs and arrays and sret * Add AIR instructions: ret_ptr, ret_load - This allows Sema to be blissfully unaware of the backend's decision to implement by-val/by-ref semantics for struct/union/array types. Backends can lower these simply as alloc, load, ret instructions, or they can take advantage of them to use a result pointer. * Add AIR instruction: array_elem_val - Allows for better codegen for `Sema.elemVal`. * Implement calculation of ABI alignment and ABI size for unions. * Before appending the following AIR instructions to a block, resolveTypeLayout is called on the type: - call - return type - ret - return type - store_ptr - elem type * Sema: fix memory leak in `zirArrayInit` and other cleanups to this function. * x86_64: implement the full x86_64 C ABI according to the spec * Type: implement `intInfo` for error sets. * Type: implement `intTagType` for tagged unions. The Zig type tag `Fn` is now used exclusively for function bodies. Function pointers are modeled as `*const T` where `T` is a `Fn` type. * The `call` AIR instruction now allows a function pointer operand as well as a function operand. * Sema now has a coercion from function body to function pointer. * Function type syntax, e.g. `fn()void`, now returns zig tag type of Pointer with child Fn, rather than Fn directly. - I think this should probably be reverted. Will discuss the lang specs before doing this. Idea being that function pointers would need to be specified as `*const fn()void` rather than `fn() void`. LLVM backend: * Enable calling the panic handler (previously this just emitted `@breakpoint()` since the backend could not handle the panic function). * Implement sret * Introduce `isByRef` and implement it for structs and arrays. Types that are `isByRef` are now passed as pointers to functions, and e.g. `elem_val` will return a pointer instead of doing a load. * Move the function type creating code from `resolveLlvmFunction` to `llvmType` where it belongs; now there is only 1 instance of this logic instead of two. * Add the `nonnull` attribute to non-optional pointer parameters. * Fix `resolveGlobalDecl` not using fully-qualified names and not using the `decl_map`. * Implement `genTypedValue` for pointer-like optionals. * Fix memory leak when lowering `block` instruction and OOM occurs. * Implement volatile checks where relevant. --- src/codegen.zig | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index b25e361f76..2a33cca702 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -855,6 +855,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .shr => try self.airShr(inst), .alloc => try self.airAlloc(inst), + .ret_ptr => try self.airRetPtr(inst), .arg => try self.airArg(inst), .assembly => try self.airAsm(inst), .bitcast => try self.airBitCast(inst), @@ -883,6 +884,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .not => try self.airNot(inst), .ptrtoint => try self.airPtrToInt(inst), .ret => try self.airRet(inst), + .ret_load => try self.airRetLoad(inst), .store => try self.airStore(inst), .struct_field_ptr=> try self.airStructFieldPtr(inst), .struct_field_val=> try self.airStructFieldVal(inst), @@ -914,6 +916,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .slice_ptr => try self.airSlicePtr(inst), .slice_len => try self.airSliceLen(inst), + .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), @@ -1185,6 +1188,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); } + fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { + const stack_offset = try self.allocMemPtr(inst); + return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); + } + fn airFptrunc(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) { @@ -1557,6 +1565,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.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) { + else => return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch}), + }; + 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; @@ -3213,6 +3229,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.finishAir(inst, .dead, .{ un_op, .none, .none }); } + fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const ptr = try self.resolveInst(un_op); + _ = ptr; + return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch}); + //return self.finishAir(inst, .dead, .{ un_op, .none, .none }); + } + fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; if (self.liveness.isUnused(inst)) -- cgit v1.2.3